forked from pool/cloud-init
Robert Schweikert
fb31094f97
+ Add cloud-init-skip-ovf-tests.patch + Add cloud-init-no-python-linux-dist.patch + Add 0001-switch-to-using-iproute2-tools.patch + Add 0001-Support-chrony-configuration-lp-1731619.patch + Add 0002-Disable-method-deprecation-warning-for-pylint.patch + Add 0003-Distro-dependent-chrony-config-file.patch + removed cloud-init-add-variant-cloudcfg.patch replaced by cloud-init-no-python-linux-dist.patch + removed zypp_add_repos.diff included upstream + removed zypp_add_repo_test.patch included upstream + removed cloud-init-hosts-template.patch included upstream + removed cloud-init-more-tasks.patch included upstream + removed cloud-init-final-no-apt.patch included upstream + removed cloud-init-ntp-conf-suse.patch included upstream + removed cloud-init-break-cycle-local-service.patch included upstream + removed cloud-init-reproduce-build.patch included upstream + For the complete changelog see https://launchpad.net/cloud-init/trunk/17.2 OBS-URL: https://build.opensuse.org/package/show/Cloud:Tools/cloud-init?expand=0&rev=105
683 lines
25 KiB
Diff
683 lines
25 KiB
Diff
From 48c4dcd464d8c6daccf09b3dccc664ad347b34ce Mon Sep 17 00:00:00 2001
|
|
From: Robert Schweikert <rjschwei@suse.com>
|
|
Date: Mon, 18 Dec 2017 13:34:21 -0500
|
|
Subject: [PATCH] - switch to using iproute2 tools + ifconfig, netstat and
|
|
other tools are being deprecated, switch to using tools that are part of
|
|
iproute2 for implementations that support these tools
|
|
|
|
---
|
|
cloudinit/config/cc_disable_ec2_metadata.py | 14 +-
|
|
.../config/tests/test_disable_ec2_metadata.py | 72 +++++
|
|
cloudinit/netinfo.py | 302 +++++++++++++++------
|
|
cloudinit/tests/test_netinfo.py | 174 +++++++++++-
|
|
4 files changed, 474 insertions(+), 88 deletions(-)
|
|
create mode 100644 cloudinit/config/tests/test_disable_ec2_metadata.py
|
|
|
|
diff --git a/cloudinit/config/cc_disable_ec2_metadata.py b/cloudinit/config/cc_disable_ec2_metadata.py
|
|
index c56319b5..8a166ddf 100644
|
|
--- a/cloudinit/config/cc_disable_ec2_metadata.py
|
|
+++ b/cloudinit/config/cc_disable_ec2_metadata.py
|
|
@@ -32,13 +32,23 @@ from cloudinit.settings import PER_ALWAYS
|
|
|
|
frequency = PER_ALWAYS
|
|
|
|
-REJECT_CMD = ['route', 'add', '-host', '169.254.169.254', 'reject']
|
|
+REJECT_CMD_IF = ['route', 'add', '-host', '169.254.169.254', 'reject']
|
|
+REJECT_CMD_IP = ['ip', 'route', 'add', 'prohibit', '169.254.169.254']
|
|
|
|
|
|
def handle(name, cfg, _cloud, log, _args):
|
|
disabled = util.get_cfg_option_bool(cfg, "disable_ec2_metadata", False)
|
|
if disabled:
|
|
- util.subp(REJECT_CMD, capture=False)
|
|
+ reject_cmd = None
|
|
+ if util.which('ifconfig'):
|
|
+ reject_cmd = REJECT_CMD_IF
|
|
+ elif util.which('ip'):
|
|
+ reject_cmd = REJECT_CMD_IP
|
|
+ else:
|
|
+ log.error(('Neither "route" nor "ip" command found, unable to '
|
|
+ 'manipulate routing table'))
|
|
+ return
|
|
+ util.subp(reject_cmd, capture=False)
|
|
else:
|
|
log.debug(("Skipping module named %s,"
|
|
" disabling the ec2 route not enabled"), name)
|
|
diff --git a/cloudinit/config/tests/test_disable_ec2_metadata.py b/cloudinit/config/tests/test_disable_ec2_metadata.py
|
|
new file mode 100644
|
|
index 00000000..bade814e
|
|
--- /dev/null
|
|
+++ b/cloudinit/config/tests/test_disable_ec2_metadata.py
|
|
@@ -0,0 +1,72 @@
|
|
+# This file is part of cloud-init. See LICENSE file for license information.
|
|
+
|
|
+"""Tests cc_disable_ec2_metadata handler"""
|
|
+
|
|
+import cloudinit.config.cc_disable_ec2_metadata as ec2_meta
|
|
+
|
|
+from cloudinit.tests.helpers import CiTestCase, mock
|
|
+
|
|
+import logging
|
|
+
|
|
+LOG = logging.getLogger(__name__)
|
|
+
|
|
+DISABLE_CFG = {'disable_ec2_metadata': 'true'}
|
|
+
|
|
+
|
|
+class TestEC2MetadataRoute(CiTestCase):
|
|
+
|
|
+ @mock.patch('cloudinit.config.cc_disable_ec2_metadata.util.which')
|
|
+ @mock.patch('cloudinit.config.cc_disable_ec2_metadata.util.subp')
|
|
+ def test_disable_ifconfig(self, m_subp, m_which):
|
|
+ """Set the route if ifconfig command is available"""
|
|
+ m_subp.side_effect = command_check_ifconfig
|
|
+ m_which.side_effect = side_effect_use_ifconfig
|
|
+ ec2_meta.handle('foo', DISABLE_CFG, None, LOG, None)
|
|
+
|
|
+ @mock.patch('cloudinit.config.cc_disable_ec2_metadata.util.which')
|
|
+ @mock.patch('cloudinit.config.cc_disable_ec2_metadata.util.subp')
|
|
+ def test_disable_ip(self, m_subp, m_which):
|
|
+ """Set the route if ip command is available"""
|
|
+ m_subp.side_effect = command_check_ip
|
|
+ m_which.side_effect = side_effect_use_ip
|
|
+ ec2_meta.handle('foo', DISABLE_CFG, None, LOG, None)
|
|
+
|
|
+ @mock.patch('cloudinit.config.cc_disable_ec2_metadata.util.which')
|
|
+ @mock.patch('cloudinit.config.cc_disable_ec2_metadata.util.subp')
|
|
+ def test_disable_no_tool(self, m_subp, m_which):
|
|
+ """Set the route if ip command is available"""
|
|
+ m_subp.side_effect = command_dont_reach
|
|
+ m_which.side_effect = side_effect_has_no_tool
|
|
+ ec2_meta.handle('foo', DISABLE_CFG, None, LOG, None)
|
|
+
|
|
+
|
|
+def side_effect_use_ifconfig(tool):
|
|
+ if tool == 'ifconfig':
|
|
+ return True
|
|
+ else:
|
|
+ return False
|
|
+
|
|
+
|
|
+def side_effect_use_ip(tool):
|
|
+ if tool == 'ip':
|
|
+ return True
|
|
+ else:
|
|
+ return False
|
|
+
|
|
+
|
|
+def side_effect_has_no_tool(tool):
|
|
+ return False
|
|
+
|
|
+
|
|
+def command_check_ifconfig(cmd, capture):
|
|
+ assert(cmd == ['route', 'add', '-host', '169.254.169.254', 'reject'])
|
|
+
|
|
+
|
|
+def command_check_ip(cmd, capture):
|
|
+ assert(cmd == ['ip', 'route', 'add', 'prohibit', '169.254.169.254'])
|
|
+
|
|
+
|
|
+def command_dont_reach(cmd, capture):
|
|
+ assert('Test should not have reached this location' == 0)
|
|
+
|
|
+# vi: ts=4 expandtab
|
|
diff --git a/cloudinit/netinfo.py b/cloudinit/netinfo.py
|
|
index 993b26cf..baad3f92 100644
|
|
--- a/cloudinit/netinfo.py
|
|
+++ b/cloudinit/netinfo.py
|
|
@@ -19,6 +19,117 @@ LOG = logging.getLogger()
|
|
|
|
|
|
def netdev_info(empty=""):
|
|
+ if util.which('ifconfig'):
|
|
+ return _netdev_info_from_ifconfig(empty)
|
|
+ elif util.which('ip'):
|
|
+ return _netdev_info_from_ip(empty)
|
|
+ else:
|
|
+ LOG.error(('Neither "ifconfig" nor "ip" command found, unable to '
|
|
+ 'collect network device information'))
|
|
+ return {}
|
|
+
|
|
+
|
|
+def route_info():
|
|
+ if util.which('netstat'):
|
|
+ return _route_info_from_netstat()
|
|
+ elif util.which('ip'):
|
|
+ return _route_info_from_ip()
|
|
+ else:
|
|
+ LOG.error(('Neither "netstat" nor "ip" command found, unable to '
|
|
+ 'collect routing information'))
|
|
+ return {}
|
|
+
|
|
+
|
|
+def getgateway():
|
|
+ try:
|
|
+ routes = route_info()
|
|
+ except Exception:
|
|
+ pass
|
|
+ else:
|
|
+ for r in routes.get('ipv4', []):
|
|
+ if r['flags'].find("G") >= 0:
|
|
+ return "%s[%s]" % (r['gateway'], r['iface'])
|
|
+ return None
|
|
+
|
|
+
|
|
+def netdev_pformat():
|
|
+ lines = []
|
|
+ try:
|
|
+ netdev = netdev_info(empty=".")
|
|
+ except Exception:
|
|
+ lines.append(util.center("Net device info failed", '!', 80))
|
|
+ else:
|
|
+ fields = ['Device', 'Up', 'Address', 'Mask', 'Scope', 'Hw-Address']
|
|
+ tbl = SimpleTable(fields)
|
|
+ for (dev, d) in sorted(netdev.items()):
|
|
+ tbl.add_row([dev, d["up"], d["addr"], d["mask"], ".", d["hwaddr"]])
|
|
+ if d.get('addr6'):
|
|
+ tbl.add_row([dev, d["up"],
|
|
+ d["addr6"], ".", d.get("scope6"), d["hwaddr"]])
|
|
+ netdev_s = tbl.get_string()
|
|
+ max_len = len(max(netdev_s.splitlines(), key=len))
|
|
+ header = util.center("Net device info", "+", max_len)
|
|
+ lines.extend([header, netdev_s])
|
|
+ return "\n".join(lines)
|
|
+
|
|
+
|
|
+def route_pformat():
|
|
+ lines = []
|
|
+ try:
|
|
+ routes = route_info()
|
|
+ except Exception as e:
|
|
+ lines.append(util.center('Route info failed', '!', 80))
|
|
+ util.logexc(LOG, "Route info failed: %s" % e)
|
|
+ else:
|
|
+ if routes.get('ipv4'):
|
|
+ fields_v4 = ['Route', 'Destination', 'Gateway',
|
|
+ 'Genmask', 'Interface', 'Flags']
|
|
+ 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']])
|
|
+ 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']
|
|
+ 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']])
|
|
+ route_s = tbl_v6.get_string()
|
|
+ max_len = len(max(route_s.splitlines(), key=len))
|
|
+ header = util.center("Route IPv6 info", "+", max_len)
|
|
+ lines.extend([header, route_s])
|
|
+ return "\n".join(lines)
|
|
+
|
|
+
|
|
+def debug_info(prefix='ci-info: '):
|
|
+ lines = []
|
|
+ netdev_lines = netdev_pformat().splitlines()
|
|
+ if prefix:
|
|
+ for line in netdev_lines:
|
|
+ lines.append("%s%s" % (prefix, line))
|
|
+ else:
|
|
+ lines.extend(netdev_lines)
|
|
+ route_lines = route_pformat().splitlines()
|
|
+ if prefix:
|
|
+ for line in route_lines:
|
|
+ lines.append("%s%s" % (prefix, line))
|
|
+ else:
|
|
+ lines.extend(route_lines)
|
|
+ return "\n".join(lines)
|
|
+
|
|
+
|
|
+def _netdev_info_from_ifconfig(empty=""):
|
|
+ """Use legacy ifconfig output"""
|
|
fields = ("hwaddr", "addr", "bcast", "mask")
|
|
(ifcfg_out, _err) = util.subp(["ifconfig", "-a"], rcs=[0, 1])
|
|
devs = {}
|
|
@@ -84,7 +195,54 @@ def netdev_info(empty=""):
|
|
return devs
|
|
|
|
|
|
-def route_info():
|
|
+def _netdev_info_from_ip(empty=""):
|
|
+ """Use ip to get network information"""
|
|
+ fields = ("hwaddr", "addr", "bcast", "mask")
|
|
+ (ipdata_out, _err) = util.subp(["ip", "a"], rcs=[0, 1])
|
|
+ devs = {}
|
|
+ this_device = None
|
|
+ for line in str(ipdata_out).splitlines():
|
|
+ if len(line) == 0:
|
|
+ continue
|
|
+ if line[0].isdigit():
|
|
+ prts = line.strip().split(':')
|
|
+ this_device = prts[1].strip()
|
|
+ devs[this_device] = {}
|
|
+ for field in fields:
|
|
+ devs[this_device][field] = ''
|
|
+ devs[this_device]['up'] = False
|
|
+ status_info = re.match('(<)(.*)(>)', prts[-1].strip()).group(2)
|
|
+ status_info = status_info.lower().split(',')
|
|
+ if 'up' in status_info:
|
|
+ devs[this_device]['up'] = True
|
|
+ if 'broadcast' in status_info and 'multicast' in status_info:
|
|
+ devs[this_device]['bcast'] = 'multicast'
|
|
+ continue
|
|
+ conf_data = line.strip()
|
|
+ conf_data_prts = conf_data.split()
|
|
+ if conf_data.startswith('inet '):
|
|
+ devs[this_device]['addr'] = conf_data_prts[1]
|
|
+ if 'brd' in conf_data_prts:
|
|
+ loc = conf_data_prts.index('brd')
|
|
+ devs[this_device]['bcast'] = conf_data_prts[loc + 1]
|
|
+ if conf_data.startswith('inet6'):
|
|
+ devs[this_device]['addr6'] = conf_data_prts[1]
|
|
+ if 'scope' in conf_data_prts:
|
|
+ loc = conf_data_prts.index('scope')
|
|
+ devs[this_device]['scope6'] = conf_data_prts[loc + 1]
|
|
+ if conf_data.startswith('link/ether'):
|
|
+ devs[this_device]['hwaddr'] = conf_data_prts[1]
|
|
+
|
|
+ if empty != "":
|
|
+ for (_devname, dev) in devs.items():
|
|
+ for field in dev:
|
|
+ if dev[field] == "":
|
|
+ dev[field] = empty
|
|
+
|
|
+ return devs
|
|
+
|
|
+
|
|
+def _route_info_from_netstat():
|
|
(route_out, _err) = util.subp(["netstat", "-rn"], rcs=[0, 1])
|
|
|
|
routes = {}
|
|
@@ -150,91 +308,69 @@ def route_info():
|
|
return routes
|
|
|
|
|
|
-def getgateway():
|
|
- try:
|
|
- routes = route_info()
|
|
- except Exception:
|
|
- pass
|
|
- else:
|
|
- for r in routes.get('ipv4', []):
|
|
- if r['flags'].find("G") >= 0:
|
|
- return "%s[%s]" % (r['gateway'], r['iface'])
|
|
- return None
|
|
-
|
|
-
|
|
-def netdev_pformat():
|
|
- lines = []
|
|
- try:
|
|
- netdev = netdev_info(empty=".")
|
|
- except Exception:
|
|
- lines.append(util.center("Net device info failed", '!', 80))
|
|
- else:
|
|
- fields = ['Device', 'Up', 'Address', 'Mask', 'Scope', 'Hw-Address']
|
|
- tbl = SimpleTable(fields)
|
|
- for (dev, d) in sorted(netdev.items()):
|
|
- tbl.add_row([dev, d["up"], d["addr"], d["mask"], ".", d["hwaddr"]])
|
|
- if d.get('addr6'):
|
|
- tbl.add_row([dev, d["up"],
|
|
- d["addr6"], ".", d.get("scope6"), d["hwaddr"]])
|
|
- netdev_s = tbl.get_string()
|
|
- max_len = len(max(netdev_s.splitlines(), key=len))
|
|
- header = util.center("Net device info", "+", max_len)
|
|
- lines.extend([header, netdev_s])
|
|
- return "\n".join(lines)
|
|
+def _route_info_from_ip():
|
|
+ """Detremine route information from ip route command"""
|
|
+ routes = {}
|
|
+ routes['ipv4'] = []
|
|
+ routes['ipv6'] = []
|
|
|
|
+ # IPv4
|
|
+ (route_out, _err) = util.subp(['ip', '-4', 'route', 'list'], rcs=[0, 1])
|
|
|
|
-def route_pformat():
|
|
- lines = []
|
|
- try:
|
|
- routes = route_info()
|
|
- except Exception as e:
|
|
- lines.append(util.center('Route info failed', '!', 80))
|
|
- util.logexc(LOG, "Route info failed: %s" % e)
|
|
- else:
|
|
- if routes.get('ipv4'):
|
|
- fields_v4 = ['Route', 'Destination', 'Gateway',
|
|
- 'Genmask', 'Interface', 'Flags']
|
|
- 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']])
|
|
- 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']
|
|
- 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']])
|
|
- route_s = tbl_v6.get_string()
|
|
- max_len = len(max(route_s.splitlines(), key=len))
|
|
- header = util.center("Route IPv6 info", "+", max_len)
|
|
- lines.extend([header, route_s])
|
|
- return "\n".join(lines)
|
|
+ entries = route_out.splitlines()
|
|
+ for line in entries:
|
|
+ route_info = line.strip().split()
|
|
+ dest = route_info[0]
|
|
+ if route_info[0] == 'default':
|
|
+ dest = '0.0.0.0'
|
|
+ flags = ''
|
|
+ gw = '0.0.0.0'
|
|
+ if 'via' in route_info:
|
|
+ loc = route_info.index('via')
|
|
+ # The NH (Next Hop) is basically equivalent to the gateway
|
|
+ gw = route_info[loc + 1]
|
|
+ flags = 'G'
|
|
+ loc = route_info.index('dev')
|
|
+ dev = route_info[loc + 1]
|
|
+ entry = {
|
|
+ 'destination': dest,
|
|
+ 'gateway': gw,
|
|
+ 'genmask': '',
|
|
+ 'flags': flags,
|
|
+ 'metric': '0',
|
|
+ 'ref': '0',
|
|
+ 'use': '0',
|
|
+ 'iface': dev
|
|
+ }
|
|
+ routes['ipv4'].append(entry)
|
|
|
|
+ # IPv6
|
|
+ (route_out, _err) = util.subp(['ip', '-6', 'route', 'list'], rcs=[0, 1])
|
|
|
|
-def debug_info(prefix='ci-info: '):
|
|
- lines = []
|
|
- netdev_lines = netdev_pformat().splitlines()
|
|
- if prefix:
|
|
- for line in netdev_lines:
|
|
- lines.append("%s%s" % (prefix, line))
|
|
- else:
|
|
- lines.extend(netdev_lines)
|
|
- route_lines = route_pformat().splitlines()
|
|
- if prefix:
|
|
- for line in route_lines:
|
|
- lines.append("%s%s" % (prefix, line))
|
|
- else:
|
|
- lines.extend(route_lines)
|
|
- return "\n".join(lines)
|
|
+ entries = route_out.splitlines()
|
|
+ for line in entries:
|
|
+ route_info = line.strip().split()
|
|
+ ip = route_info[0]
|
|
+ if ip == 'default':
|
|
+ ip = '::'
|
|
+ proto = 'tcp6'
|
|
+ if 'proto' in route_info:
|
|
+ loc = route_info.index('proto')
|
|
+ proto = route_info[loc + 1]
|
|
+ gw = ''
|
|
+ if 'via' in route_info:
|
|
+ loc = route_info.index('via')
|
|
+ # The NH (Next Hop) is basically equivalent to the gateway
|
|
+ gw = route_info[loc + 1]
|
|
+ entry = {
|
|
+ 'proto': proto,
|
|
+ 'recv-q': '0',
|
|
+ 'send-q': '0',
|
|
+ 'local address': ip,
|
|
+ 'foreign address': gw,
|
|
+ 'state': '',
|
|
+ }
|
|
+ routes['ipv6'].append(entry)
|
|
+ return routes
|
|
|
|
# vi: ts=4 expandtab
|
|
diff --git a/cloudinit/tests/test_netinfo.py b/cloudinit/tests/test_netinfo.py
|
|
index 7dea2e41..3dc557cc 100644
|
|
--- a/cloudinit/tests/test_netinfo.py
|
|
+++ b/cloudinit/tests/test_netinfo.py
|
|
@@ -2,7 +2,7 @@
|
|
|
|
"""Tests netinfo module functions and classes."""
|
|
|
|
-from cloudinit.netinfo import netdev_pformat, route_pformat
|
|
+from cloudinit.netinfo import getgateway, netdev_pformat, route_pformat
|
|
from cloudinit.tests.helpers import CiTestCase, mock
|
|
|
|
|
|
@@ -27,6 +27,48 @@ lo Link encap:Local Loopback
|
|
collisions:0 txqueuelen:1
|
|
"""
|
|
|
|
+SAMPLE_IP_A_OUT = (
|
|
+ '1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN '
|
|
+ 'group default qlen 1000\n'
|
|
+ 'link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n'
|
|
+ 'inet 127.0.0.1/8 scope host lo\n'
|
|
+ ' valid_lft forever preferred_lft forever\n'
|
|
+ 'inet6 ::1/128 scope host\n'
|
|
+ ' valid_lft forever preferred_lft forever\n'
|
|
+ '2: wlp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state '
|
|
+ 'UP group default qlen 1000\n'
|
|
+ 'link/ether 84:3a:4b:09:6f:ec brd ff:ff:ff:ff:ff:ff\n'
|
|
+ 'inet 192.168.1.101/24 brd 192.168.1.255 scope global wlp3s0\n'
|
|
+ ' valid_lft forever preferred_lft forever\n'
|
|
+ 'inet 192.168.1.3/24 brd 192.168.1.255 scope global secondary wlp3s0\n'
|
|
+ ' valid_lft forever preferred_lft forever\n'
|
|
+ 'inet6 fe80::863a:4bff:fe09:6fec/64 scope link\n'
|
|
+ ' valid_lft forever preferred_lft forever'
|
|
+)
|
|
+
|
|
+SAMPLE_ROUTE_INFO = {
|
|
+ 'ipv4': [
|
|
+ {
|
|
+ 'genmask': '0.0.0.0',
|
|
+ 'use': '0',
|
|
+ 'iface': 'eth1',
|
|
+ 'flags': 'UG',
|
|
+ 'metric': '0',
|
|
+ 'destination': '0.0.0.0',
|
|
+ 'ref': '0',
|
|
+ 'gateway': '192.168.1.1'},
|
|
+ {
|
|
+ 'genmask': '255.0.0.0',
|
|
+ 'use': '0',
|
|
+ 'iface': 'eth2',
|
|
+ 'flags': 'UG',
|
|
+ 'metric': '0',
|
|
+ 'destination': '10.0.0.0',
|
|
+ 'ref': '0',
|
|
+ 'gateway': '10.163.8.1'}
|
|
+ ]
|
|
+}
|
|
+
|
|
SAMPLE_ROUTE_OUT = '\n'.join([
|
|
'0.0.0.0 192.168.2.1 0.0.0.0 UG 0 0 0'
|
|
' enp0s25',
|
|
@@ -35,6 +77,20 @@ SAMPLE_ROUTE_OUT = '\n'.join([
|
|
'192.168.2.0 0.0.0.0 255.255.255.0 U 0 0 0'
|
|
' enp0s25'])
|
|
|
|
+SAMPLE_ROUTE_OUT_IP_V4 = '\n'.join([
|
|
+ 'default via 192.168.1.1 dev br0',
|
|
+ '10.0.0.0/8 via 10.163.8.1 dev tun0',
|
|
+ '10.163.8.1 dev tun0 proto kernel scope link src 10.163.8.118 ',
|
|
+ '137.65.0.0/16 via 10.163.8.1 dev tun0'])
|
|
+
|
|
+SAMPLE_ROUTE_OUT_IP_V6 = '\n'.join([
|
|
+ '2621:111:80c0:8080:12:160:68:53 dev eth0 proto kernel metric 256 expires '
|
|
+ '9178sec pref medium',
|
|
+ '2621:111:80c0:8080::/64 dev eth0 proto ra metric 100 pref medium',
|
|
+ 'fe80::1 dev eth0 proto static metric 100 pref medium',
|
|
+ 'fe80::/64 dev eth0 proto kernel metric 256 pref medium',
|
|
+ 'default via fe80::1 dev eth0 proto static metric 100 pref medium',
|
|
+ '2620:113:80c0:8000::/50 dev tun0 metric 1024 pref medium'])
|
|
|
|
NETDEV_FORMATTED_OUT = '\n'.join([
|
|
'+++++++++++++++++++++++++++++++++++++++Net device info+++++++++++++++++++'
|
|
@@ -56,6 +112,26 @@ NETDEV_FORMATTED_OUT = '\n'.join([
|
|
'+---------+------+------------------------------+---------------+-------+'
|
|
'-------------------+'])
|
|
|
|
+NETDEV_FORMATTED_OUT_IP = '\n'.join([
|
|
+ '++++++++++++++++++++++++++++++++++Net device info++++++++++++++++++++++'
|
|
+ '++++++++++++',
|
|
+ '+--------+------+------------------------------+------+-------+----------'
|
|
+ '---------+',
|
|
+ '| Device | Up | Address | Mask | Scope | Hw-Ad'
|
|
+ 'dress |',
|
|
+ '+--------+------+------------------------------+------+-------+----------'
|
|
+ '---------+',
|
|
+ '| lo | True | 127.0.0.1/8 | . | . | .'
|
|
+ ' |',
|
|
+ '| lo | True | ::1/128 | . | host | .'
|
|
+ ' |',
|
|
+ '| wlp3s0 | True | 192.168.1.3/24 | . | . | 84:3a:4b:'
|
|
+ '09:6f:ec |',
|
|
+ '| wlp3s0 | True | fe80::863a:4bff:fe09:6fec/64 | . | link | 84:3a:4b:'
|
|
+ '09:6f:ec |',
|
|
+ '+--------+------+------------------------------+------+-------+----------'
|
|
+ '---------+'])
|
|
+
|
|
ROUTE_FORMATTED_OUT = '\n'.join([
|
|
'+++++++++++++++++++++++++++++Route IPv4 info++++++++++++++++++++++++++'
|
|
'+++',
|
|
@@ -86,21 +162,113 @@ ROUTE_FORMATTED_OUT = '\n'.join([
|
|
'+-------+-------------+-------------+---------------+---------------+'
|
|
'-----------------+-------+'])
|
|
|
|
+ROUTE_FORMATTED_OUT_IP = '\n'.join([
|
|
+ '+++++++++++++++++++++++++++Route IPv4 info+++++++++++++++++++++++++++',
|
|
+ '+-------+---------------+-------------+---------+-----------+-------+',
|
|
+ '| Route | Destination | Gateway | Genmask | Interface | Flags |',
|
|
+ '+-------+---------------+-------------+---------+-----------+-------+',
|
|
+ '| 0 | 0.0.0.0 | 192.168.1.1 | | br0 | G |',
|
|
+ '| 1 | 10.0.0.0/8 | 10.163.8.1 | | tun0 | G |',
|
|
+ '| 2 | 10.163.8.1 | 0.0.0.0 | | tun0 | |',
|
|
+ '| 3 | 137.65.0.0/16 | 10.163.8.1 | | tun0 | G |',
|
|
+ '+-------+---------------+-------------+---------+-----------+-------+',
|
|
+ '++++++++++++++++++++++++++++++++++++++++Route IPv6 info++++++++++++++'
|
|
+ '+++++++++++++++++++++++++++',
|
|
+ '+-------+--------+--------+--------+---------------------------------'
|
|
+ '+-----------------+-------+',
|
|
+ '| Route | Proto | Recv-Q | Send-Q | Local Address '
|
|
+ '| Foreign Address | State |',
|
|
+ '+-------+--------+--------+--------+---------------------------------'
|
|
+ '+-----------------+-------+',
|
|
+ '| 0 | kernel | 0 | 0 | 2621:111:80c0:8080:12:160:68:53 '
|
|
+ '| | |',
|
|
+ '| 1 | ra | 0 | 0 | 2621:111:80c0:8080::/64 '
|
|
+ '| | |',
|
|
+ '| 2 | static | 0 | 0 | fe80::1 '
|
|
+ '| | |',
|
|
+ '| 3 | kernel | 0 | 0 | fe80::/64 '
|
|
+ '| | |',
|
|
+ '| 4 | static | 0 | 0 | :: '
|
|
+ '| fe80::1 | |',
|
|
+ '| 5 | tcp6 | 0 | 0 | 2620:113:80c0:8000::/50 '
|
|
+ '| | |',
|
|
+ '+-------+--------+--------+--------+---------------------------------'
|
|
+ '+-----------------+-------+'])
|
|
+
|
|
|
|
class TestNetInfo(CiTestCase):
|
|
|
|
maxDiff = None
|
|
|
|
+ @mock.patch('cloudinit.netinfo.route_info')
|
|
+ def test_getdateway_route(self, m_route_info):
|
|
+ """getgateway finds the first gateway"""
|
|
+ m_route_info.return_value = SAMPLE_ROUTE_INFO
|
|
+ gateway = getgateway()
|
|
+ self.assertEqual('192.168.1.1[eth1]', gateway)
|
|
+
|
|
+ @mock.patch('cloudinit.netinfo.util.which')
|
|
@mock.patch('cloudinit.netinfo.util.subp')
|
|
- def test_netdev_pformat(self, m_subp):
|
|
+ def test_netdev_pformat_ifconfig(self, m_subp, m_which):
|
|
"""netdev_pformat properly rendering network device information."""
|
|
m_subp.return_value = (SAMPLE_IFCONFIG_OUT, '')
|
|
+ m_which.side_effect = side_effect_use_ifconfig
|
|
content = netdev_pformat()
|
|
self.assertEqual(NETDEV_FORMATTED_OUT, content)
|
|
|
|
+ @mock.patch('cloudinit.netinfo.util.which')
|
|
@mock.patch('cloudinit.netinfo.util.subp')
|
|
- def test_route_pformat(self, m_subp):
|
|
+ def test_netdev_pformat_ip(self, m_subp, m_which):
|
|
+ """netdev_pformat properly rendering network device information."""
|
|
+ m_subp.return_value = (SAMPLE_IP_A_OUT, '')
|
|
+ m_which.side_effect = side_effect_use_ip
|
|
+ content = netdev_pformat()
|
|
+ self.assertEqual(NETDEV_FORMATTED_OUT_IP, content)
|
|
+
|
|
+ @mock.patch('cloudinit.netinfo.util.which')
|
|
+ @mock.patch('cloudinit.netinfo.util.subp')
|
|
+ def test_route_pformat_netstat(self, m_subp, m_which):
|
|
"""netdev_pformat properly rendering network device information."""
|
|
m_subp.return_value = (SAMPLE_ROUTE_OUT, '')
|
|
+ m_which.side_effect = side_effect_use_netstat
|
|
content = route_pformat()
|
|
self.assertEqual(ROUTE_FORMATTED_OUT, content)
|
|
+
|
|
+ @mock.patch('cloudinit.netinfo.util.which')
|
|
+ @mock.patch('cloudinit.netinfo.util.subp')
|
|
+ def test_route_pformat_ip(self, m_subp, m_which):
|
|
+ """netdev_pformat properly rendering network device information."""
|
|
+ m_subp.side_effect = side_effect_return_route_info
|
|
+ m_which.side_effect = side_effect_use_ip
|
|
+ content = route_pformat()
|
|
+ self.assertEqual(ROUTE_FORMATTED_OUT_IP, content)
|
|
+
|
|
+
|
|
+def side_effect_use_ifconfig(tool):
|
|
+ if tool == 'ifconfig':
|
|
+ return True
|
|
+ else:
|
|
+ return False
|
|
+
|
|
+
|
|
+def side_effect_use_ip(tool):
|
|
+ if tool == 'ip':
|
|
+ return True
|
|
+ else:
|
|
+ return False
|
|
+
|
|
+
|
|
+def side_effect_use_netstat(tool):
|
|
+ if tool == 'netstat':
|
|
+ return True
|
|
+ else:
|
|
+ return False
|
|
+
|
|
+
|
|
+def side_effect_return_route_info(cmd, rcs=None):
|
|
+ if '-4' in list(cmd):
|
|
+ return (SAMPLE_ROUTE_OUT_IP_V4, 0)
|
|
+ else:
|
|
+ return (SAMPLE_ROUTE_OUT_IP_V6, 0)
|
|
+
|
|
+# vi: ts=4 expandtab
|
|
--
|
|
2.13.6
|
|
|