summaryrefslogtreecommitdiff
path: root/cloudinit/net
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/net')
-rwxr-xr-x[-rw-r--r--]cloudinit/net/__init__.py187
-rwxr-xr-x[-rw-r--r--]cloudinit/net/cmdline.py53
-rw-r--r--cloudinit/net/eni.py16
-rw-r--r--cloudinit/net/network_state.py19
-rw-r--r--cloudinit/net/renderer.py21
-rw-r--r--cloudinit/net/sysconfig.py16
-rw-r--r--cloudinit/net/udev.py17
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):