summaryrefslogtreecommitdiff
path: root/cloudinit/net
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/net')
-rw-r--r--cloudinit/net/__init__.py4
-rwxr-xr-xcloudinit/net/cmdline.py9
-rw-r--r--cloudinit/net/dhcp.py43
-rw-r--r--cloudinit/net/network_state.py20
4 files changed, 66 insertions, 10 deletions
diff --git a/cloudinit/net/__init__.py b/cloudinit/net/__init__.py
index a1b0db10..c015e793 100644
--- a/cloudinit/net/__init__.py
+++ b/cloudinit/net/__init__.py
@@ -18,7 +18,7 @@ SYS_CLASS_NET = "/sys/class/net/"
DEFAULT_PRIMARY_INTERFACE = 'eth0'
-def _natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
+def natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
"""Sorting for Humans: natural sort order. Can be use as the key to sort
functions.
This will sort ['eth0', 'ens3', 'ens10', 'ens12', 'ens8', 'ens0'] as
@@ -224,7 +224,7 @@ def find_fallback_nic(blacklist_drivers=None):
# if eth0 exists use it above anything else, otherwise get the interface
# that we can read 'first' (using the sorted defintion of first).
- names = list(sorted(potential_interfaces, key=_natural_sort_key))
+ names = list(sorted(potential_interfaces, key=natural_sort_key))
if DEFAULT_PRIMARY_INTERFACE in names:
names.remove(DEFAULT_PRIMARY_INTERFACE)
names.insert(0, DEFAULT_PRIMARY_INTERFACE)
diff --git a/cloudinit/net/cmdline.py b/cloudinit/net/cmdline.py
index 38b27a52..7b2cc9db 100755
--- a/cloudinit/net/cmdline.py
+++ b/cloudinit/net/cmdline.py
@@ -116,10 +116,11 @@ def config_from_klibc_net_cfg(files=None, mac_addrs=None):
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')))
+ "device '{name}' was defined multiple times ({files})"
+ " but had differing mac addresses: {old} -> {new}.".format(
+ name=name, files=' '.join(names[name]['files']),
+ old=prev.get('mac_address'),
+ new=entry.get('mac_address')))
prev['subnets'].extend(entry['subnets'])
names[name]['files'].append(cfg_file)
else:
diff --git a/cloudinit/net/dhcp.py b/cloudinit/net/dhcp.py
index 875a4609..087c0c03 100644
--- a/cloudinit/net/dhcp.py
+++ b/cloudinit/net/dhcp.py
@@ -10,7 +10,9 @@ import os
import re
import signal
-from cloudinit.net import find_fallback_nic, get_devicelist
+from cloudinit.net import (
+ EphemeralIPv4Network, find_fallback_nic, get_devicelist)
+from cloudinit.net.network_state import mask_and_ipv4_to_bcast_addr as bcip
from cloudinit import temp_utils
from cloudinit import util
from six import StringIO
@@ -29,6 +31,45 @@ class InvalidDHCPLeaseFileError(Exception):
pass
+class NoDHCPLeaseError(Exception):
+ """Raised when unable to get a DHCP lease."""
+ pass
+
+
+class EphemeralDHCPv4(object):
+ def __init__(self, iface=None):
+ self.iface = iface
+ self._ephipv4 = None
+
+ def __enter__(self):
+ try:
+ leases = maybe_perform_dhcp_discovery(self.iface)
+ except InvalidDHCPLeaseFileError:
+ raise NoDHCPLeaseError()
+ if not leases:
+ raise NoDHCPLeaseError()
+ lease = leases[-1]
+ LOG.debug("Received dhcp lease on %s for %s/%s",
+ lease['interface'], lease['fixed-address'],
+ lease['subnet-mask'])
+ nmap = {'interface': 'interface', 'ip': 'fixed-address',
+ 'prefix_or_mask': 'subnet-mask',
+ 'broadcast': 'broadcast-address',
+ 'router': 'routers'}
+ kwargs = dict([(k, lease.get(v)) for k, v in nmap.items()])
+ if not kwargs['broadcast']:
+ kwargs['broadcast'] = bcip(kwargs['prefix_or_mask'], kwargs['ip'])
+ ephipv4 = EphemeralIPv4Network(**kwargs)
+ ephipv4.__enter__()
+ self._ephipv4 = ephipv4
+ return lease
+
+ def __exit__(self, excp_type, excp_value, excp_traceback):
+ if not self._ephipv4:
+ return
+ self._ephipv4.__exit__(excp_type, excp_value, excp_traceback)
+
+
def maybe_perform_dhcp_discovery(nic=None):
"""Perform dhcp discovery if nic valid and dhclient command exists.
diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py
index e9e2cf4e..fe667d88 100644
--- a/cloudinit/net/network_state.py
+++ b/cloudinit/net/network_state.py
@@ -474,8 +474,9 @@ class NetworkStateInterpreter(object):
elif bridge_stp in ['off', '0', 0]:
bridge_stp = False
else:
- raise ValueError("Cannot convert bridge_stp value"
- "(%s) to boolean", bridge_stp)
+ raise ValueError(
+ 'Cannot convert bridge_stp value ({stp}) to'
+ ' boolean'.format(stp=bridge_stp))
iface.update({'bridge_stp': bridge_stp})
interfaces.update({iface['name']: iface})
@@ -692,7 +693,8 @@ class NetworkStateInterpreter(object):
elif cmd_type == "bond":
self.handle_bond(v1_cmd)
else:
- raise ValueError('Unknown command type: %s', cmd_type)
+ raise ValueError('Unknown command type: {cmd_type}'.format(
+ cmd_type=cmd_type))
def _v2_to_v1_ipcfg(self, cfg):
"""Common ipconfig extraction from v2 to v1 subnets array."""
@@ -959,4 +961,16 @@ def mask_to_net_prefix(mask):
return ipv4_mask_to_net_prefix(mask)
+def mask_and_ipv4_to_bcast_addr(mask, ip):
+ """Calculate the broadcast address from the subnet mask and ip addr.
+
+ Supports ipv4 only."""
+ ip_bin = int(''.join([bin(int(x) + 256)[3:] for x in ip.split('.')]), 2)
+ mask_dec = ipv4_mask_to_net_prefix(mask)
+ bcast_bin = ip_bin | (2**(32 - mask_dec) - 1)
+ bcast_str = '.'.join([str(bcast_bin >> (i << 3) & 0xFF)
+ for i in range(4)[::-1]])
+ return bcast_str
+
+
# vi: ts=4 expandtab