cloud-init-dev team mailing list archive
-
cloud-init-dev team
-
Mailing list archive
-
Message #03709
[Merge] ~james-hogarth/cloud-init:net-tools-deprecation into cloud-init:master
Scott Moser has proposed merging ~james-hogarth/cloud-init:net-tools-deprecation into cloud-init:master.
Requested reviews:
cloud-init commiters (cloud-init-dev)
Related bugs:
Bug #925145 in cloud-init: "Use ip instead of ifconfig and route"
https://bugs.launchpad.net/cloud-init/+bug/925145
For more details, see:
https://code.launchpad.net/~james-hogarth/cloud-init/+git/cloud-init/+merge/333657
--
Your team cloud-init commiters is requested to review the proposed merge of ~james-hogarth/cloud-init:net-tools-deprecation into cloud-init:master.
diff --git a/cloudinit/netinfo.py b/cloudinit/netinfo.py
index 993b26c..0b76301 100644
--- a/cloudinit/netinfo.py
+++ b/cloudinit/netinfo.py
@@ -9,6 +9,8 @@
# This file is part of cloud-init. See LICENSE file for license information.
import re
+import socket
+import struct
from cloudinit import log as logging
from cloudinit import util
@@ -18,11 +20,62 @@ from cloudinit.simpletable import SimpleTable
LOG = logging.getLogger()
-def netdev_info(empty=""):
+def netdev_cidr_to_mask(cidr):
+ mask = socket.inet_ntoa(
+ struct.pack(">I", (0xffffffff << (32 - int(cidr)) & 0xffffffff)))
+ return mask
+
+
+def netdev_info_iproute(ipaddr_data, iplink_data):
+ # fields that need to be returned in devs for each dev
+ fields = ("hwaddr", "up", "addr", "bcast", "mask", "scope")
+ devs = {}
+ for line in str(ipaddr_data).splitlines():
+ details = line.lower().strip().split()
+ curdev = details[1]
+ fieldpost = ""
+ if curdev not in devs:
+ devs[curdev] = {}
+ if details[2] == "inet6":
+ fieldpost = "6"
+ for i in range(len(details)):
+ if details[i] == "inet" or details[i] == "inet6":
+ addr = ""
+ if fieldpost == "":
+ (addr, cidr) = details[i + 1].split("/")
+ devs[curdev]["mask"] = netdev_cidr_to_mask(cidr)
+ else:
+ addr = details[i + 1]
+ devs[curdev]["addr" + fieldpost] = addr
+ if details[i] == "scope":
+ devs[curdev]["scope" + fieldpost] = details[i + 1]
+ if details[i] == "brd":
+ devs[curdev]["bcast" + fieldpost] = details[i + 1]
+
+ for line in str(iplink_data).splitlines():
+ details = line.lower().strip().split()
+ curdev = details[1][:-1]
+ for i in range(len(details)):
+ if details[i] == curdev + ":":
+ flags = details[i + 1].strip("<>").split(",")
+ if "lower_up" in flags and "up" in flags:
+ devs[curdev]["up"] = True
+ if details[i] == "link/ether":
+ devs[curdev]["hwaddr"] = details[i + 1]
+
+ # Run through all devs and ensure all fields are defined
+ for dev in devs:
+ for field in fields:
+ if field not in devs[dev]:
+ devs[dev][field] = ""
+
+ return devs
+
+
+def netdev_info_ifconfig(ifconfig_data):
fields = ("hwaddr", "addr", "bcast", "mask")
- (ifcfg_out, _err) = util.subp(["ifconfig", "-a"], rcs=[0, 1])
devs = {}
- for line in str(ifcfg_out).splitlines():
+ for line in str(ifconfig_data).splitlines():
if len(line) == 0:
continue
if line[0] not in ("\t", " "):
@@ -74,6 +127,20 @@ def netdev_info(empty=""):
pass
elif toks[i].startswith("%s" % origfield):
devs[curdev][target] = toks[i][len(field) + 1:]
+ return devs
+
+
+def netdev_info(empty=""):
+ devs = {}
+ try:
+ # Try iproute first of all
+ (ipaddr_out, _err) = util.subp(["ip", "-o", "addr", "list"])
+ (iplink_out, _err) = util.subp(["ip", "-o", "link", "list"])
+ devs = netdev_info_iproute(ipaddr_out, iplink_out)
+ except util.ProcessExecutionError:
+ # Fall back to net-tools if iproute2 is not present
+ (ifcfg_out, _err) = util.subp(["ifconfig", "-a"], rcs=[0, 1])
+ devs = netdev_info_ifconfig(ifcfg_out)
if empty != "":
for (_devname, dev) in devs.items():
@@ -84,14 +151,73 @@ def netdev_info(empty=""):
return devs
-def route_info():
- (route_out, _err) = util.subp(["netstat", "-rn"], rcs=[0, 1])
+def netdev_route_info_iproute(iproute_data):
+ routes = {}
+ routes['ipv4'] = []
+ routes['ipv6'] = []
+ entries = iproute_data.splitlines()
+ for line in entries:
+ entry = {}
+ if not line:
+ continue
+ toks = line.split()
+ if toks[0] == "default":
+ entry['destination'] = "0.0.0.0"
+ entry['genmask'] = "0.0.0.0"
+ entry['flags'] = "UG"
+ else:
+ (addr, cidr) = toks[0].split("/")
+ entry['destination'] = addr
+ entry['genmask'] = netdev_cidr_to_mask(cidr)
+ entry['gateway'] = "0.0.0.0"
+ entry['flags'] = "U"
+ for i in range(len(toks)):
+ if toks[i] == "via":
+ entry['gateway'] = toks[i + 1]
+ if toks[i] == "dev":
+ entry["iface"] = toks[i + 1]
+ if toks[i] == "metric":
+ entry['metric'] = toks[i + 1]
+ routes['ipv4'].append(entry)
+ try:
+ (iproute_data6, _err6) = util.subp(["ip", "-o", "-6", "route", "list"],
+ rcs=[0, 1])
+ except util.ProcessExecutionError:
+ pass
+ else:
+ entries6 = iproute_data6.splitlines()
+ for line in entries6:
+ entry = {}
+ if not line:
+ continue
+ toks = line.split()
+ if toks[0] == "default":
+ entry['destination'] = "::/0"
+ entry['flags'] = "UG"
+ else:
+ entry['destination'] = toks[0]
+ entry['gateway'] = "::"
+ entry['flags'] = "U"
+ for i in range(len(toks)):
+ if toks[i] == "via":
+ entry['gateway'] = toks[i + 1]
+ entry['flags'] = "UG"
+ if toks[i] == "dev":
+ entry["iface"] = toks[i + 1]
+ if toks[i] == "metric":
+ entry['metric'] = toks[i + 1]
+ if toks[i] == "expires":
+ entry['flags'] = entry['flags'] + 'e'
+ routes['ipv6'].append(entry)
+ return routes
+
+def netdev_route_info_netstat(route_data):
routes = {}
routes['ipv4'] = []
routes['ipv6'] = []
- entries = route_out.splitlines()[1:]
+ entries = route_data.splitlines()
for line in entries:
if not line:
continue
@@ -101,8 +227,8 @@ def route_info():
# default 10.65.0.1 UGS 0 34920 vtnet0
#
# Linux netstat shows 2 more:
- # Destination Gateway Genmask Flags MSS Window irtt Iface
- # 0.0.0.0 10.65.0.1 0.0.0.0 UG 0 0 0 eth0
+ # Destination Gateway Genmask Flags Metric Ref Use Iface
+ # 0.0.0.0 10.65.0.1 0.0.0.0 UG 0 0 0 eth0
if (len(toks) < 6 or toks[0] == "Kernel" or
toks[0] == "Destination" or toks[0] == "Internet" or
toks[0] == "Internet6" or toks[0] == "Routing"):
@@ -125,31 +251,52 @@ def route_info():
routes['ipv4'].append(entry)
try:
- (route_out6, _err6) = util.subp(["netstat", "-A", "inet6", "-n"],
- rcs=[0, 1])
+ (route_data6, _err6) = util.subp(["netstat", "-A", "inet6", "-rn"],
+ rcs=[0, 1])
except util.ProcessExecutionError:
pass
else:
- entries6 = route_out6.splitlines()[1:]
+ entries6 = route_data6.splitlines()
for line in entries6:
if not line:
continue
toks = line.split()
- if (len(toks) < 6 or toks[0] == "Kernel" or
+ if (len(toks) < 7 or toks[0] == "Kernel" or
+ toks[0] == "Destination" or toks[0] == "Internet" or
toks[0] == "Proto" or toks[0] == "Active"):
continue
entry = {
- 'proto': toks[0],
- 'recv-q': toks[1],
- 'send-q': toks[2],
- 'local address': toks[3],
- 'foreign address': toks[4],
- 'state': toks[5],
+ 'destination': toks[0],
+ 'gateway': toks[1],
+ 'flags': toks[2],
+ 'metric': toks[3],
+ 'ref': toks[4],
+ 'use': toks[5],
+ 'iface': toks[6],
}
+ # skip lo interface on ipv6
+ if entry['iface'] == "lo":
+ continue
+ # strip /128 from address if it's included
+ if entry['destination'].endswith('/128'):
+ entry['destination'] = entry['destination'][:-4]
routes['ipv6'].append(entry)
return routes
+def route_info():
+ routes = {}
+ try:
+ # Try iproute first of all
+ (iproute_out, _err) = util.subp(["ip", "-o", "route", "list"])
+ routes = netdev_route_info_iproute(iproute_out)
+ except util.ProcessExecutionError:
+ # Fall back to net-tools if iproute2 is not present
+ (route_out, _err) = util.subp(["netstat", "-rne"], rcs=[0, 1])
+ routes = netdev_route_info_netstat(route_out)
+ return routes
+
+
def getgateway():
try:
routes = route_info()
@@ -193,27 +340,26 @@ def route_pformat():
else:
if routes.get('ipv4'):
fields_v4 = ['Route', 'Destination', 'Gateway',
- 'Genmask', 'Interface', 'Flags']
+ 'Genmask', 'Interface', 'Flags', 'Metric']
tbl_v4 = SimpleTable(fields_v4)
for (n, r) in enumerate(routes.get('ipv4')):
route_id = str(n)
tbl_v4.add_row([route_id, r['destination'],
r['gateway'], r['genmask'],
- r['iface'], r['flags']])
+ r['iface'], r['flags'], r['metric']])
route_s = tbl_v4.get_string()
max_len = len(max(route_s.splitlines(), key=len))
header = util.center("Route IPv4 info", "+", max_len)
lines.extend([header, route_s])
if routes.get('ipv6'):
- fields_v6 = ['Route', 'Proto', 'Recv-Q', 'Send-Q',
- 'Local Address', 'Foreign Address', 'State']
+ fields_v6 = ['Route', 'Destination', 'Gateway', 'Interface',
+ 'Flags', 'Metric']
tbl_v6 = SimpleTable(fields_v6)
for (n, r) in enumerate(routes.get('ipv6')):
route_id = str(n)
- tbl_v6.add_row([route_id, r['proto'],
- r['recv-q'], r['send-q'],
- r['local address'], r['foreign address'],
- r['state']])
+ tbl_v6.add_row([route_id, r['destination'],
+ r['gateway'], r['iface'],
+ r['flags'], r['metric']])
route_s = tbl_v6.get_string()
max_len = len(max(route_s.splitlines(), key=len))
header = util.center("Route IPv6 info", "+", max_len)
diff --git a/cloudinit/tests/test_netinfo.py b/cloudinit/tests/test_netinfo.py
index 7dea2e4..744c338 100644
--- a/cloudinit/tests/test_netinfo.py
+++ b/cloudinit/tests/test_netinfo.py
@@ -2,6 +2,7 @@
"""Tests netinfo module functions and classes."""
+from cloudinit import util
from cloudinit.netinfo import netdev_pformat, route_pformat
from cloudinit.tests.helpers import CiTestCase, mock
@@ -27,80 +28,141 @@ lo Link encap:Local Loopback
collisions:0 txqueuelen:1
"""
-SAMPLE_ROUTE_OUT = '\n'.join([
- '0.0.0.0 192.168.2.1 0.0.0.0 UG 0 0 0'
- ' enp0s25',
- '0.0.0.0 192.168.2.1 0.0.0.0 UG 0 0 0'
- ' wlp3s0',
- '192.168.2.0 0.0.0.0 255.255.255.0 U 0 0 0'
- ' enp0s25'])
+# Intentionally disabling line length check on mock data for clarity
+SAMPLE_IPADDR_OUT = '\n'.join([
+ '1: lo inet 127.0.0.1/8 scope host lo\ valid_lft forever preferred_lft forever', # noqa: E501
+ '1: lo inet6 ::1/128 scope host \ valid_lft forever preferred_lft forever', # noqa: E501
+ '2: enp0s25 inet 192.168.2.18/24 brd 192.168.2.255 scope global dynamic enp0s25\ valid_lft 84174sec preferred_lft 84174sec', # noqa: E501
+ '2: enp0s25 inet6 fe80::8107:2b92:867e:f8a6/64 scope link \ valid_lft forever preferred_lft forever']) # noqa: E501
+SAMPLE_IPLINK_OUT = '\n'.join([
+ '1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000\ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00', # noqa: E501
+ '2: enp0s25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000\ link/ether 50:7b:9d:2c:af:91 brd ff:ff:ff:ff:ff:ff']) # noqa: E501
+
+SAMPLE_ROUTE_OUT_V4 = '\n'.join([
+ 'Kernel IP routing table', # noqa: E501
+ 'Destination Gateway Genmask Flags Metric Ref Use Iface', # noqa: E501
+ '0.0.0.0 192.168.2.1 0.0.0.0 UG 100 0 0 enp0s25', # noqa: E501
+ '0.0.0.0 192.168.2.1 0.0.0.0 UG 150 0 0 wlp3s0', # noqa: E501
+ '192.168.2.0 0.0.0.0 255.255.255.0 U 100 0 0 enp0s25']) # noqa: E501
+
+SAMPLE_ROUTE_OUT_V6 = '\n'.join([
+ 'Kernel IPv6 routing table', # noqa: E501
+ 'Destination Next Hop Flag Met Re Use If', # noqa: E501
+ '2a00:abcd:82ae:cd33::657/128 :: Ue 256 1 0 enp0s25', # noqa: E501
+ '2a00:abcd:82ae:cd33::/64 :: U 100 1 0 enp0s25', # noqa: E501
+ '2a00:abcd:82ae:cd33::/56 fe80::32ee:54de:cd43:b4e1 UG 100 1 0 enp0s25', # noqa: E501
+ 'fd81:123f:654::657/128 :: U 256 1 0 enp0s25', # noqa: E501
+ 'fd81:123f:654::/64 :: U 100 1 0 enp0s25', # noqa: E501
+ 'fd81:123f:654::/48 fe80::32ee:54de:cd43:b4e1 UG 100 1 0 enp0s25', # noqa: E501
+ 'fe80::abcd:ef12:bc34:da21/128 :: U 100 1 2 enp0s25', # noqa: E501
+ 'fe80::/64 :: U 256 1 16880 enp0s25', # noqa: E501
+ '::/0 fe80::32ee:54de:cd43:b4e1 UG 100 1 0 enp0s25', # noqa: E501
+ '::/0 :: !n -1 1424956 lo', # noqa: E501
+ '::1/128 :: Un 0 4 26289 lo']) # noqa: E501
+
+SAMPLE_IPROUTE_OUT_V4 = '\n'.join([
+ 'default via 192.168.2.1 dev enp0s25 proto static metric 100', # noqa: E501
+ 'default via 192.168.2.1 dev wlp3s0 proto static metric 150', # noqa: E501
+ '192.168.2.0/24 dev enp0s25 proto kernel scope link src 192.168.2.18 metric 100']) # noqa: E501
+
+SAMPLE_IPROUTE_OUT_V6 = '\n'.join([
+ '2a00:abcd:82ae:cd33::657 dev enp0s25 proto kernel metric 256 expires 2334sec pref medium', # noqa: E501
+ '2a00:abcd:82ae:cd33::/64 dev enp0s25 proto ra metric 100 pref medium', # noqa: E501
+ '2a00:abcd:82ae:cd33::/56 via fe80::32ee:54de:cd43:b4e1 dev enp0s25 proto ra metric 100 pref medium', # noqa: E501
+ 'fd81:123f:654::657 dev enp0s25 proto kernel metric 256 pref medium', # noqa: E501
+ 'fd81:123f:654::/64 dev enp0s25 proto ra metric 100 pref medium', # noqa: E501
+ 'fd81:123f:654::/48 via fe80::32ee:54de:cd43:b4e1 dev enp0s25 proto ra metric 100 pref medium', # noqa: E501
+ 'fe80::abcd:ef12:bc34:da21 dev enp0s25 proto static metric 100 pref medium', # noqa: E501
+ 'fe80::/64 dev enp0s25 proto kernel metric 256 pref medium', # noqa: E501
+ 'default via fe80::32ee:54de:cd43:b4e1 dev enp0s25 proto static metric 100 pref medium']) # noqa: E501
NETDEV_FORMATTED_OUT = '\n'.join([
- '+++++++++++++++++++++++++++++++++++++++Net device info+++++++++++++++++++'
- '++++++++++++++++++++',
- '+---------+------+------------------------------+---------------+-------+'
- '-------------------+',
- '| Device | Up | Address | Mask | Scope |'
- ' Hw-Address |',
- '+---------+------+------------------------------+---------------+-------+'
- '-------------------+',
- '| enp0s25 | True | 192.168.2.18 | 255.255.255.0 | . |'
- ' 50:7b:9d:2c:af:91 |',
- '| enp0s25 | True | fe80::8107:2b92:867e:f8a6/64 | . | link |'
- ' 50:7b:9d:2c:af:91 |',
- '| lo | True | 127.0.0.1 | 255.0.0.0 | . |'
- ' . |',
- '| lo | True | ::1/128 | . | host |'
- ' . |',
- '+---------+------+------------------------------+---------------+-------+'
- '-------------------+'])
+ '+++++++++++++++++++++++++++++++++++++++Net device info+++++++++++++++++++++++++++++++++++++++', # noqa: E501
+ '+---------+------+------------------------------+---------------+-------+-------------------+', # noqa: E501
+ '| Device | Up | Address | Mask | Scope | Hw-Address |', # noqa: E501
+ '+---------+------+------------------------------+---------------+-------+-------------------+', # noqa: E501
+ '| enp0s25 | True | 192.168.2.18 | 255.255.255.0 | . | 50:7b:9d:2c:af:91 |', # noqa: E501
+ '| enp0s25 | True | fe80::8107:2b92:867e:f8a6/64 | . | link | 50:7b:9d:2c:af:91 |', # noqa: E501
+ '| lo | True | 127.0.0.1 | 255.0.0.0 | . | . |', # noqa: E501
+ '| lo | True | ::1/128 | . | host | . |', # noqa: E501
+ '+---------+------+------------------------------+---------------+-------+-------------------+']) # noqa: E501
ROUTE_FORMATTED_OUT = '\n'.join([
- '+++++++++++++++++++++++++++++Route IPv4 info++++++++++++++++++++++++++'
- '+++',
- '+-------+-------------+-------------+---------------+-----------+-----'
- '--+',
- '| Route | Destination | Gateway | Genmask | Interface | Flags'
- ' |',
- '+-------+-------------+-------------+---------------+-----------+'
- '-------+',
- '| 0 | 0.0.0.0 | 192.168.2.1 | 0.0.0.0 | wlp3s0 |'
- ' UG |',
- '| 1 | 192.168.2.0 | 0.0.0.0 | 255.255.255.0 | enp0s25 |'
- ' U |',
- '+-------+-------------+-------------+---------------+-----------+'
- '-------+',
- '++++++++++++++++++++++++++++++++++++++++Route IPv6 info++++++++++'
- '++++++++++++++++++++++++++++++',
- '+-------+-------------+-------------+---------------+---------------+'
- '-----------------+-------+',
- '| Route | Proto | Recv-Q | Send-Q | Local Address |'
- ' Foreign Address | State |',
- '+-------+-------------+-------------+---------------+---------------+'
- '-----------------+-------+',
- '| 0 | 0.0.0.0 | 192.168.2.1 | 0.0.0.0 | UG |'
- ' 0 | 0 |',
- '| 1 | 192.168.2.0 | 0.0.0.0 | 255.255.255.0 | U |'
- ' 0 | 0 |',
- '+-------+-------------+-------------+---------------+---------------+'
- '-----------------+-------+'])
+ '+++++++++++++++++++++++++++++++++Route IPv4 info++++++++++++++++++++++++++++++++++', # noqa: E501
+ '+-------+-------------+-------------+---------------+-----------+-------+--------+', # noqa: E501
+ '| Route | Destination | Gateway | Genmask | Interface | Flags | Metric |', # noqa: E501
+ '+-------+-------------+-------------+---------------+-----------+-------+--------+', # noqa: E501
+ '| 0 | 0.0.0.0 | 192.168.2.1 | 0.0.0.0 | enp0s25 | UG | 100 |', # noqa: E501
+ '| 1 | 0.0.0.0 | 192.168.2.1 | 0.0.0.0 | wlp3s0 | UG | 150 |', # noqa: E501
+ '| 2 | 192.168.2.0 | 0.0.0.0 | 255.255.255.0 | enp0s25 | U | 100 |', # noqa: E501
+ '+-------+-------------+-------------+---------------+-----------+-------+--------+', # noqa: E501
+ '+++++++++++++++++++++++++++++++++++++++Route IPv6 info++++++++++++++++++++++++++++++++++++++++', # noqa: E501
+ '+-------+---------------------------+---------------------------+-----------+-------+--------+', # noqa: E501
+ '| Route | Destination | Gateway | Interface | Flags | Metric |', # noqa: E501
+ '+-------+---------------------------+---------------------------+-----------+-------+--------+', # noqa: E501
+ '| 0 | 2a00:abcd:82ae:cd33::657 | :: | enp0s25 | Ue | 256 |', # noqa: E501
+ '| 1 | 2a00:abcd:82ae:cd33::/64 | :: | enp0s25 | U | 100 |', # noqa: E501
+ '| 2 | 2a00:abcd:82ae:cd33::/56 | fe80::32ee:54de:cd43:b4e1 | enp0s25 | UG | 100 |', # noqa: E501
+ '| 3 | fd81:123f:654::657 | :: | enp0s25 | U | 256 |', # noqa: E501
+ '| 4 | fd81:123f:654::/64 | :: | enp0s25 | U | 100 |', # noqa: E501
+ '| 5 | fd81:123f:654::/48 | fe80::32ee:54de:cd43:b4e1 | enp0s25 | UG | 100 |', # noqa: E501
+ '| 6 | fe80::abcd:ef12:bc34:da21 | :: | enp0s25 | U | 100 |', # noqa: E501
+ '| 7 | fe80::/64 | :: | enp0s25 | U | 256 |', # noqa: E501
+ '| 8 | ::/0 | fe80::32ee:54de:cd43:b4e1 | enp0s25 | UG | 100 |', # noqa: E501
+ '+-------+---------------------------+---------------------------+-----------+-------+--------+']) # noqa: E501
class TestNetInfo(CiTestCase):
maxDiff = None
- @mock.patch('cloudinit.netinfo.util.subp')
- def test_netdev_pformat(self, m_subp):
+ def netdev_nettools_selector(*args, **kwargs):
+ # pylint:disable=no-method-argument
+ if 'ip' in args[0]:
+ raise util.ProcessExecutionError
+ if 'ifconfig' in args[0]:
+ return (SAMPLE_IFCONFIG_OUT, '')
+ if 'netstat' in args[0] and 'inet6' not in args[0]:
+ return (SAMPLE_ROUTE_OUT_V4, '')
+ if 'netstat' in args[0] and 'inet6' in args[0]:
+ return (SAMPLE_ROUTE_OUT_V6, '')
+
+ def netdev_iproute_selector(*args, **kwargs):
+ # pylint:disable=no-method-argument
+ if 'ip' in args[0] and 'addr' in args[0]:
+ return (SAMPLE_IPADDR_OUT, '')
+ if 'ip' in args[0] and 'link' in args[0]:
+ return (SAMPLE_IPLINK_OUT, '')
+ if 'ip' in args[0] and 'route' in args[0] and '-6' not in args[0]:
+ return (SAMPLE_IPROUTE_OUT_V4, '')
+ if 'ip' in args[0] and 'route' in args[0] and '-6' in args[0]:
+ return (SAMPLE_IPROUTE_OUT_V6, '')
+
+ @mock.patch('cloudinit.netinfo.util.subp',
+ side_effect=netdev_nettools_selector)
+ def test_netdev_nettools_pformat(self, m_subp):
+ """netdev_pformat properly rendering network device information."""
+ content = netdev_pformat()
+ self.assertEqual(NETDEV_FORMATTED_OUT, content)
+
+ @mock.patch('cloudinit.netinfo.util.subp',
+ side_effect=netdev_iproute_selector)
+ def test_netdev_iproute_pformat(self, m_subp):
"""netdev_pformat properly rendering network device information."""
- m_subp.return_value = (SAMPLE_IFCONFIG_OUT, '')
content = netdev_pformat()
self.assertEqual(NETDEV_FORMATTED_OUT, content)
- @mock.patch('cloudinit.netinfo.util.subp')
- def test_route_pformat(self, m_subp):
+ @mock.patch('cloudinit.netinfo.util.subp',
+ side_effect=netdev_nettools_selector)
+ def test_route_nettools_pformat(self, m_subp):
+ """netdev_pformat properly rendering network device information."""
+ content = route_pformat()
+ self.assertEqual(ROUTE_FORMATTED_OUT, content)
+
+ @mock.patch('cloudinit.netinfo.util.subp',
+ side_effect=netdev_iproute_selector)
+ def test_route_iproute_pformat(self, m_subp):
"""netdev_pformat properly rendering network device information."""
- m_subp.return_value = (SAMPLE_ROUTE_OUT, '')
content = route_pformat()
self.assertEqual(ROUTE_FORMATTED_OUT, content)
Follow ups