diff options
Diffstat (limited to 'cloudinit/net')
-rwxr-xr-x[-rw-r--r--] | cloudinit/net/__init__.py | 187 | ||||
-rwxr-xr-x[-rw-r--r--] | cloudinit/net/cmdline.py | 53 | ||||
-rw-r--r-- | cloudinit/net/eni.py | 16 | ||||
-rw-r--r-- | cloudinit/net/network_state.py | 19 | ||||
-rw-r--r-- | cloudinit/net/renderer.py | 21 | ||||
-rw-r--r-- | cloudinit/net/sysconfig.py | 16 | ||||
-rw-r--r-- | cloudinit/net/udev.py | 17 |
7 files changed, 136 insertions, 193 deletions
diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py index 7e58bfea..ea649cc2 100644..100755 --- a/cloudinit/net/__init__.py +++ b/cloudinit/net/__init__.py @@ -1,20 +1,9 @@ -# Copyright (C) 2013-2014 Canonical Ltd. +# Copyright (C) 2013-2014 Canonical Ltd. # -# Author: Scott Moser <scott.moser@canonical.com> -# Author: Blake Rouse <blake.rouse@canonical.com> +# Author: Scott Moser <scott.moser@canonical.com> +# Author: Blake Rouse <blake.rouse@canonical.com> # -# Curtin is free software: you can redistribute it and/or modify it under -# the terms of the GNU Affero General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# Curtin is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for -# more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Curtin. If not, see <http://www.gnu.org/licenses/>. +# This file is part of cloud-init. See LICENSE file for license information. import errno import logging @@ -32,25 +21,53 @@ def sys_dev_path(devname, path=""): return SYS_CLASS_NET + devname + "/" + path -def read_sys_net(devname, path, translate=None, enoent=None, keyerror=None): +def read_sys_net(devname, path, translate=None, + on_enoent=None, on_keyerror=None, + on_einval=None): + dev_path = sys_dev_path(devname, path) try: - contents = util.load_file(sys_dev_path(devname, path)) + contents = util.load_file(dev_path) except (OSError, IOError) as e: - if getattr(e, 'errno', None) in (errno.ENOENT, errno.ENOTDIR): - if enoent is not None: - return enoent + e_errno = getattr(e, 'errno', None) + if e_errno in (errno.ENOENT, errno.ENOTDIR): + if on_enoent is not None: + return on_enoent(e) + if e_errno in (errno.EINVAL,): + if on_einval is not None: + return on_einval(e) raise contents = contents.strip() if translate is None: return contents try: - return translate.get(contents) - except KeyError: - LOG.debug("found unexpected value '%s' in '%s/%s'", contents, - devname, path) - if keyerror is not None: - return keyerror - raise + return translate[contents] + except KeyError as e: + if on_keyerror is not None: + return on_keyerror(e) + else: + LOG.debug("Found unexpected (not translatable) value" + " '%s' in '%s", contents, dev_path) + raise + + +def read_sys_net_safe(iface, field, translate=None): + def on_excp_false(e): + return False + return read_sys_net(iface, field, + on_keyerror=on_excp_false, + on_enoent=on_excp_false, + on_einval=on_excp_false, + translate=translate) + + +def read_sys_net_int(iface, field): + val = read_sys_net_safe(iface, field) + if val is False: + return None + try: + return int(val) + except TypeError: + return None def is_up(devname): @@ -58,8 +75,7 @@ def is_up(devname): # operstate as up for the purposes of network configuration. See # Documentation/networking/operstates.txt in the kernel source. translate = {'up': True, 'unknown': True, 'down': False} - return read_sys_net(devname, "operstate", enoent=False, keyerror=False, - translate=translate) + return read_sys_net_safe(devname, "operstate", translate=translate) def is_wireless(devname): @@ -70,21 +86,14 @@ def is_connected(devname): # is_connected isn't really as simple as that. 2 is # 'physically connected'. 3 is 'not connected'. but a wlan interface will # always show 3. - try: - iflink = read_sys_net(devname, "iflink", enoent=False) - if iflink == "2": - return True - if not is_wireless(devname): - return False - LOG.debug("'%s' is wireless, basing 'connected' on carrier", devname) - - return read_sys_net(devname, "carrier", enoent=False, keyerror=False, - translate={'0': False, '1': True}) - - except IOError as e: - if e.errno == errno.EINVAL: - return False - raise + iflink = read_sys_net_safe(devname, "iflink") + if iflink == "2": + return True + if not is_wireless(devname): + return False + LOG.debug("'%s' is wireless, basing 'connected' on carrier", devname) + return read_sys_net_safe(devname, "carrier", + translate={'0': False, '1': True}) def is_physical(devname): @@ -109,25 +118,9 @@ def is_disabled_cfg(cfg): return cfg.get('config') == "disabled" -def sys_netdev_info(name, field): - if not os.path.exists(os.path.join(SYS_CLASS_NET, name)): - raise OSError("%s: interface does not exist in %s" % - (name, SYS_CLASS_NET)) - fname = os.path.join(SYS_CLASS_NET, name, field) - if not os.path.exists(fname): - raise OSError("%s: could not find sysfs entry: %s" % (name, fname)) - data = util.load_file(fname) - if data[-1] == '\n': - data = data[:-1] - return data - - def generate_fallback_config(): """Determine which attached net dev is most likely to have a connection and generate network state to run dhcp on that interface""" - # by default use eth0 as primary interface - nconf = {'config': [], 'version': 1} - # get list of interfaces that could have connections invalid_interfaces = set(['lo']) potential_interfaces = set(get_devicelist()) @@ -142,30 +135,21 @@ def generate_fallback_config(): if os.path.exists(sys_dev_path(interface, "bridge")): # skip any bridges continue - try: - carrier = int(sys_netdev_info(interface, 'carrier')) - if carrier: - connected.append(interface) - continue - except OSError: - pass + carrier = read_sys_net_int(interface, 'carrier') + if carrier: + connected.append(interface) + continue # check if nic is dormant or down, as this may make a nick appear to # not have a carrier even though it could acquire one when brought # online by dhclient - try: - dormant = int(sys_netdev_info(interface, 'dormant')) - if dormant: - possibly_connected.append(interface) - continue - except OSError: - pass - try: - operstate = sys_netdev_info(interface, 'operstate') - if operstate in ['dormant', 'down', 'lowerlayerdown', 'unknown']: - possibly_connected.append(interface) - continue - except OSError: - pass + dormant = read_sys_net_int(interface, 'dormant') + if dormant: + possibly_connected.append(interface) + continue + operstate = read_sys_net_safe(interface, 'operstate') + if operstate in ['dormant', 'down', 'lowerlayerdown', 'unknown']: + possibly_connected.append(interface) + continue # don't bother with interfaces that might not be connected if there are # some that definitely are @@ -173,23 +157,30 @@ def generate_fallback_config(): potential_interfaces = connected else: potential_interfaces = possibly_connected - # if there are no interfaces, give up - if not potential_interfaces: - return + # if eth0 exists use it above anything else, otherwise get the interface - # that looks 'first' - if DEFAULT_PRIMARY_INTERFACE in potential_interfaces: - name = DEFAULT_PRIMARY_INTERFACE + # that we can read 'first' (using the sorted defintion of first). + names = list(sorted(potential_interfaces)) + if DEFAULT_PRIMARY_INTERFACE in names: + names.remove(DEFAULT_PRIMARY_INTERFACE) + names.insert(0, DEFAULT_PRIMARY_INTERFACE) + target_name = None + target_mac = None + for name in names: + mac = read_sys_net_safe(name, 'address') + if mac: + target_name = name + target_mac = mac + break + if target_mac and target_name: + nconf = {'config': [], 'version': 1} + nconf['config'].append( + {'type': 'physical', 'name': target_name, + 'mac_address': target_mac, 'subnets': [{'type': 'dhcp'}]}) + return nconf else: - name = sorted(potential_interfaces)[0] - - mac = sys_netdev_info(name, 'address') - target_name = name - - nconf['config'].append( - {'type': 'physical', 'name': target_name, - 'mac_address': mac, 'subnets': [{'type': 'dhcp'}]}) - return nconf + # can't read any interfaces addresses (or there are none); give up + return None def apply_network_config_names(netcfg, strict_present=True, strict_busy=True): @@ -352,7 +343,7 @@ def get_interface_mac(ifname): # for a bond slave, get the nic's hwaddress, not the address it # is using because its part of a bond. path = "bonding_slave/perm_hwaddr" - return read_sys_net(ifname, path, enoent=False) + return read_sys_net_safe(ifname, path) def get_interfaces_by_mac(devs=None): @@ -373,4 +364,4 @@ def get_interfaces_by_mac(devs=None): ret[mac] = name return ret -# vi: ts=4 expandtab syntax=python +# vi: ts=4 expandtab diff --git a/cloudinit/net/cmdline.py b/cloudinit/net/cmdline.py index 4075a279..7c5d11a7 100644..100755 --- a/cloudinit/net/cmdline.py +++ b/cloudinit/net/cmdline.py @@ -1,20 +1,9 @@ -# Copyright (C) 2013-2014 Canonical Ltd. +# Copyright (C) 2013-2014 Canonical Ltd. # -# Author: Scott Moser <scott.moser@canonical.com> -# Author: Blake Rouse <blake.rouse@canonical.com> +# Author: Scott Moser <scott.moser@canonical.com> +# Author: Blake Rouse <blake.rouse@canonical.com> # -# Curtin is free software: you can redistribute it and/or modify it under -# the terms of the GNU Affero General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# Curtin is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for -# more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Curtin. If not, see <http://www.gnu.org/licenses/>. +# This file is part of cloud-init. See LICENSE file for license information. import base64 import glob @@ -26,7 +15,7 @@ import sys import six from . import get_devicelist -from . import sys_netdev_info +from . import read_sys_net_safe from cloudinit import util @@ -57,7 +46,7 @@ def _load_shell_content(content, add_empty=False, empty_val=None): def _klibc_to_config_entry(content, mac_addrs=None): - """Convert a klibc writtent shell content file to a 'config' entry + """Convert a klibc written shell content file to a 'config' entry When ip= is seen on the kernel command line in debian initramfs and networking is brought up, ipconfig will populate /run/net-<name>.cfg. @@ -140,7 +129,7 @@ def _klibc_to_config_entry(content, mac_addrs=None): def config_from_klibc_net_cfg(files=None, mac_addrs=None): if files is None: - files = glob.glob('/run/net*.conf') + files = glob.glob('/run/net-*.conf') + glob.glob('/run/net6-*.conf') entries = [] names = {} @@ -148,12 +137,19 @@ def config_from_klibc_net_cfg(files=None, mac_addrs=None): name, entry = _klibc_to_config_entry(util.load_file(cfg_file), mac_addrs=mac_addrs) if name in names: - raise ValueError( - "device '%s' defined multiple times: %s and %s" % ( - name, names[name], cfg_file)) + prev = names[name]['entry'] + if prev.get('mac_address') != entry.get('mac_address'): + raise ValueError( + "device '%s' was defined multiple times (%s)" + " but had differing mac addresses: %s -> %s.", + (name, ' '.join(names[name]['files']), + prev.get('mac_address'), entry.get('mac_address'))) + prev['subnets'].extend(entry['subnets']) + names[name]['files'].append(cfg_file) + else: + names[name] = {'files': [cfg_file], 'entry': entry} + entries.append(entry) - names[name] = cfg_file - entries.append(entry) return {'config': entries, 'version': 1} @@ -199,11 +195,16 @@ def read_kernel_cmdline_config(files=None, mac_addrs=None, cmdline=None): if data64: return util.load_yaml(_b64dgz(data64)) - if 'ip=' not in cmdline: + if 'ip=' not in cmdline and 'ip6=' not in cmdline: return None if mac_addrs is None: - mac_addrs = dict((k, sys_netdev_info(k, 'address')) - for k in get_devicelist()) + mac_addrs = {} + for k in get_devicelist(): + mac_addr = read_sys_net_safe(k, 'address') + if mac_addr: + mac_addrs[k] = mac_addr return config_from_klibc_net_cfg(files=files, mac_addrs=mac_addrs) + +# vi: ts=4 expandtab diff --git a/cloudinit/net/eni.py b/cloudinit/net/eni.py index cd533ddb..b06ffac9 100644 --- a/cloudinit/net/eni.py +++ b/cloudinit/net/eni.py @@ -1,16 +1,4 @@ -# vi: ts=4 expandtab -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 3, as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# This file is part of cloud-init. See LICENSE file for license information. import copy import glob @@ -502,3 +490,5 @@ def network_state_to_eni(network_state, header=None, render_hwaddress=False): contents = renderer._render_interfaces( network_state, render_hwaddress=render_hwaddress) return header + contents + +# vi: ts=4 expandtab diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py index 8ca5106f..11ef585b 100644 --- a/cloudinit/net/network_state.py +++ b/cloudinit/net/network_state.py @@ -1,19 +1,8 @@ -# Copyright (C) 2013-2014 Canonical Ltd. +# Copyright (C) 2013-2014 Canonical Ltd. # -# Author: Ryan Harper <ryan.harper@canonical.com> +# Author: Ryan Harper <ryan.harper@canonical.com> # -# Curtin is free software: you can redistribute it and/or modify it under -# the terms of the GNU Affero General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# Curtin is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for -# more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Curtin. If not, see <http://www.gnu.org/licenses/>. +# This file is part of cloud-init. See LICENSE file for license information. import copy import functools @@ -452,3 +441,5 @@ def mask2cidr(mask): return ipv4mask2cidr(mask) else: return mask + +# vi: ts=4 expandtab diff --git a/cloudinit/net/renderer.py b/cloudinit/net/renderer.py index 310cbe0d..3a192436 100644 --- a/cloudinit/net/renderer.py +++ b/cloudinit/net/renderer.py @@ -1,20 +1,9 @@ -# Copyright (C) 2013-2014 Canonical Ltd. +# Copyright (C) 2013-2014 Canonical Ltd. # -# Author: Scott Moser <scott.moser@canonical.com> -# Author: Blake Rouse <blake.rouse@canonical.com> +# Author: Scott Moser <scott.moser@canonical.com> +# Author: Blake Rouse <blake.rouse@canonical.com> # -# Curtin is free software: you can redistribute it and/or modify it under -# the terms of the GNU Affero General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# Curtin is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for -# more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Curtin. If not, see <http://www.gnu.org/licenses/>. +# This file is part of cloud-init. See LICENSE file for license information. import six @@ -46,3 +35,5 @@ class Renderer(object): content.write(generate_udev_rule(iface['name'], iface['mac_address'])) return content.getvalue() + +# vi: ts=4 expandtab diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py index c53acf71..9be74070 100644 --- a/cloudinit/net/sysconfig.py +++ b/cloudinit/net/sysconfig.py @@ -1,16 +1,4 @@ -# vi: ts=4 expandtab -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 3, as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# This file is part of cloud-init. See LICENSE file for license information. import os import re @@ -398,3 +386,5 @@ class Renderer(renderer.Renderer): netrules_content = self._render_persistent_net(network_state) netrules_path = os.path.join(target, self.netrules_path) util.write_file(netrules_path, netrules_content) + +# vi: ts=4 expandtab diff --git a/cloudinit/net/udev.py b/cloudinit/net/udev.py index 09188295..fd2fd8c7 100644 --- a/cloudinit/net/udev.py +++ b/cloudinit/net/udev.py @@ -1,19 +1,8 @@ -# Copyright (C) 2015 Canonical Ltd. +# Copyright (C) 2015 Canonical Ltd. # -# Author: Ryan Harper <ryan.harper@canonical.com> +# Author: Ryan Harper <ryan.harper@canonical.com> # -# Curtin is free software: you can redistribute it and/or modify it under -# the terms of the GNU Affero General Public License as published by the -# Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# Curtin is distributed in the hope that it will be useful, but WITHOUT ANY -# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for -# more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with Curtin. If not, see <http://www.gnu.org/licenses/>. +# This file is part of cloud-init. See LICENSE file for license information. def compose_udev_equality(key, value): |