~allenap/maas/xxx-a-thon

« back to all changes in this revision

Viewing changes to src/provisioningserver/utils/ipaddr.py

  • Committer: LaMont Jones
  • Date: 2016-03-07 23:20:52 UTC
  • mfrom: (4657.1.84 maas)
  • mto: (4657.1.93 maas)
  • mto: This revision was merged to the branch mainline in revision 4660.
  • Revision ID: lamont@canonical.com-20160307232052-rgfxbq7dujj6s093
MergeĀ fromĀ trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2015 Canonical Ltd.  This software is licensed under the
 
1
# Copyright 2015-2016 Canonical Ltd.  This software is licensed under the
2
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
3
 
4
4
"""Utility to parse 'ip addr [show]'.
64
64
from provisioningserver.utils.shell import call_and_check
65
65
 
66
66
 
67
 
def _get_settings_dict(settings_line):
 
67
def get_settings_dict(settings_line):
68
68
    """
69
69
    Given a string of the format:
70
70
        "[[<key1> <value1>] <key2> <value2>][...]"
114
114
        else:
115
115
            flags = []
116
116
        interface['flags'] = flags
117
 
        interface['settings'] = _get_settings_dict(matches.group(2))
 
117
        interface['settings'] = get_settings_dict(matches.group(2))
118
118
    else:
119
119
        raise ValueError("Malformed 'ip addr' line (%s)" % line)
120
120
    return interface
128
128
    :param interface: dict
129
129
    :param line: unicode
130
130
    """
131
 
    settings = _get_settings_dict(line)
 
131
    settings = get_settings_dict(line)
132
132
    mac = settings.get('link/ether')
133
133
    if mac is not None:
134
134
        interface['mac'] = mac
221
221
 
222
222
def get_interface_type(
223
223
        ifname, sys_class_net="/sys/class/net",
224
 
        proc_net_vlan='/proc/net/vlan'):
 
224
        proc_net="/proc/net"):
225
225
    """Heuristic to return the type of the given interface.
226
226
 
227
227
    The given interface must be able to be found in /sys/class/net/ifname.
270
270
        bond_dir = os.path.join(sys_path, 'bonding')
271
271
        if os.path.isdir(bond_dir):
272
272
            return 'ethernet.bond'
273
 
        if os.path.isfile('%s/%s' % (proc_net_vlan, ifname)):
 
273
        if os.path.isfile(os.path.join(proc_net, "vlan", ifname)):
274
274
            return 'ethernet.vlan'
275
275
        device_path = os.path.join(sys_path, 'device')
276
276
        if os.path.islink(device_path):
291
291
        return 'unknown-%d' % iftype
292
292
 
293
293
 
294
 
def annotate_with_driver_information(interfaces):
 
294
def _parse_proc_net_bonding(file):
 
295
    """Parse the given file, which must be a path to a file in the format
 
296
    that is used for file in `/proc/net/bonding/<interface>`.
 
297
 
 
298
    Returns a dictionary mapping each interface name found in the file to
 
299
    its original MAC address.
 
300
    """
 
301
    interfaces = {}
 
302
    current_iface = None
 
303
    with open(file) as f:
 
304
        for line in f.readlines():
 
305
            line = line.strip()
 
306
            slave_iface = line.split("Slave Interface: ")
 
307
            if len(slave_iface) == 2:
 
308
                current_iface = slave_iface[1]
 
309
            hw_addr = line.split("Permanent HW addr: ")
 
310
            if len(hw_addr) == 2:
 
311
                interfaces[current_iface] = hw_addr[1]
 
312
    return interfaces
 
313
 
 
314
 
 
315
def annotate_with_proc_net_bonding_original_macs(
 
316
        interfaces, proc_net="/proc/net"):
 
317
    """Repairs the MAC addresses of bond members in the specified structure.
 
318
 
 
319
    Given the specified interfaces structure, uses the data in
 
320
    `/proc/net/bonding/*` to determine if any of the interfaces
 
321
    in the structure are bond members. If so, modifies their MAC address,
 
322
    setting it back to the original hardware MAC. (When an interface is added
 
323
    to a bond, its MAC address is set to the bond MAC, and subsequently
 
324
    reported in commands like "ip addr".)
 
325
    """
 
326
    proc_net_bonding = os.path.join(proc_net, "bonding")
 
327
    if os.path.isdir(proc_net_bonding):
 
328
        bonds = os.listdir(proc_net_bonding)
 
329
        for bond in bonds:
 
330
            parent_macs = _parse_proc_net_bonding(
 
331
                os.path.join(proc_net_bonding, bond))
 
332
            for interface in parent_macs:
 
333
                if interface in interfaces:
 
334
                    interfaces[interface]['mac'] = parent_macs[interface]
 
335
    return interfaces
 
336
 
 
337
 
 
338
def annotate_with_driver_information(
 
339
        interfaces, sys_class_net="/sys/class/net", proc_net="/proc/net"):
295
340
    """Determines driver information for each of the given interfaces.
296
341
 
297
342
    Annotates the given dictionary to update it with driver information
298
343
    (if found) for each interface.
 
344
 
 
345
    Deletes bond interfaces if they are not configured.
 
346
 
 
347
    :param interfaces: interfaces dictionary from `parse_ip_addr()`.
 
348
    :param proc_net: path to /proc/net
 
349
    :param sys_class_net: path to /sys/class/net
299
350
    """
 
351
    interfaces = annotate_with_proc_net_bonding_original_macs(
 
352
        interfaces, proc_net=proc_net)
 
353
    bogus_interfaces = []
300
354
    for name in interfaces:
301
355
        iface = interfaces[name]
302
 
        iftype = get_interface_type(name)
 
356
        iftype = get_interface_type(
 
357
            name, sys_class_net=sys_class_net, proc_net=proc_net)
303
358
        interfaces[name]['type'] = iftype
304
359
        if iftype == 'ethernet.bond':
305
 
            iface['bonded_interfaces'] = get_bonded_interfaces(name)
 
360
            bond_parents = get_bonded_interfaces(
 
361
                name, sys_class_net=sys_class_net)
 
362
            if len(bond_parents) > 0:
 
363
                iface['bonded_interfaces'] = bond_parents
 
364
            else:
 
365
                # If we found a bond interface with no parents, just pretend
 
366
                # it doesn't exist. The MAAS model assumes bonds must have
 
367
                # backing interfaces.
 
368
                bogus_interfaces.append(name)
306
369
        elif iftype == 'ethernet.vlan':
307
370
            iface['vid'] = get_vid_from_ifname(name)
308
371
        elif iftype == 'ethernet.bridge':
309
 
            iface['bridged_interfaces'] = get_bridged_interfaces(name)
 
372
            iface['bridged_interfaces'] = get_bridged_interfaces(
 
373
                name, sys_class_net=sys_class_net)
 
374
    for name in bogus_interfaces:
 
375
        del interfaces[name]
310
376
    return interfaces
311
377
 
312
378