diff options
-rw-r--r-- | cloudinit/net/dhcp.py | 42 | ||||
-rw-r--r-- | cloudinit/net/network_state.py | 2 | ||||
-rw-r--r-- | cloudinit/net/tests/test_dhcp.py | 65 | ||||
-rw-r--r-- | cloudinit/net/tests/test_network_state.py | 47 | ||||
-rw-r--r-- | doc-requirements.txt | 3 | ||||
-rw-r--r-- | doc/rtd/index.rst | 1 | ||||
-rw-r--r-- | doc/rtd/topics/bugs.rst | 108 |
7 files changed, 259 insertions, 9 deletions
diff --git a/cloudinit/net/dhcp.py b/cloudinit/net/dhcp.py index 17379918..c033cc8e 100644 --- a/cloudinit/net/dhcp.py +++ b/cloudinit/net/dhcp.py @@ -92,9 +92,12 @@ class EphemeralDHCPv4(object): nmap = {'interface': 'interface', 'ip': 'fixed-address', 'prefix_or_mask': 'subnet-mask', 'broadcast': 'broadcast-address', - 'static_routes': 'rfc3442-classless-static-routes', + 'static_routes': [ + 'rfc3442-classless-static-routes', + 'classless-static-routes' + ], 'router': 'routers'} - kwargs = dict([(k, self.lease.get(v)) for k, v in nmap.items()]) + kwargs = self.extract_dhcp_options_mapping(nmap) if not kwargs['broadcast']: kwargs['broadcast'] = bcip(kwargs['prefix_or_mask'], kwargs['ip']) if kwargs['static_routes']: @@ -107,6 +110,25 @@ class EphemeralDHCPv4(object): self._ephipv4 = ephipv4 return self.lease + def extract_dhcp_options_mapping(self, nmap): + result = {} + for internal_reference, lease_option_names in nmap.items(): + if isinstance(lease_option_names, list): + self.get_first_option_value( + internal_reference, + lease_option_names, + result + ) + else: + result[internal_reference] = self.lease.get(lease_option_names) + return result + + def get_first_option_value(self, internal_mapping, + lease_option_names, result): + for different_names in lease_option_names: + if not result.get(internal_mapping): + result[internal_mapping] = self.lease.get(different_names) + def maybe_perform_dhcp_discovery(nic=None): """Perform dhcp discovery if nic valid and dhclient command exists. @@ -281,24 +303,30 @@ def parse_static_routes(rfc3442): """ parse rfc3442 format and return a list containing tuple of strings. The tuple is composed of the network_address (including net length) and - gateway for a parsed static route. + gateway for a parsed static route. It can parse two formats of rfc3442, + one from dhcpcd and one from dhclient (isc). - @param rfc3442: string in rfc3442 format + @param rfc3442: string in rfc3442 format (isc or dhcpd) @returns: list of tuple(str, str) for all valid parsed routes until the first parsing error. E.g. - sr = parse_state_routes("32,169,254,169,254,130,56,248,255,0,130,56,240,1") - sr = [ + sr=parse_static_routes("32,169,254,169,254,130,56,248,255,0,130,56,240,1") + sr=[ ("169.254.169.254/32", "130.56.248.255"), ("0.0.0.0/0", "130.56.240.1") ] + sr2 = parse_static_routes("24.191.168.128 192.168.128.1,0 192.168.128.1") + sr2 = [ + ("191.168.128.0/24", "192.168.128.1"), ("0.0.0.0/0", "192.168.128.1") + ] + Python version of isc-dhclient's hooks: /etc/dhcp/dhclient-exit-hooks.d/rfc3442-classless-routes """ # raw strings from dhcp lease may end in semi-colon rfc3442 = rfc3442.rstrip(";") - tokens = rfc3442.split(',') + tokens = [tok for tok in re.split(r"[, .]", rfc3442) if tok] static_routes = [] def _trunc_error(cidr, required, remain): diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py index 7d206a1a..f3e8e250 100644 --- a/cloudinit/net/network_state.py +++ b/cloudinit/net/network_state.py @@ -73,7 +73,7 @@ def parse_net_config_data(net_config, skip_broken=True): # pass the whole net-config as-is config = net_config - if version and config: + if version and config is not None: nsi = NetworkStateInterpreter(version=version, config=config) nsi.parse_config(skip_broken=skip_broken) state = nsi.get_network_state() diff --git a/cloudinit/net/tests/test_dhcp.py b/cloudinit/net/tests/test_dhcp.py index 91f503c9..c3fa1e04 100644 --- a/cloudinit/net/tests/test_dhcp.py +++ b/cloudinit/net/tests/test_dhcp.py @@ -90,6 +90,32 @@ class TestDHCPRFC3442(CiTestCase): write_file(lease_file, content) self.assertItemsEqual(expected, parse_dhcp_lease_file(lease_file)) + def test_parse_lease_finds_classless_static_routes(self): + """ + parse_dhcp_lease_file returns classless-static-routes + for Centos lease format. + """ + lease_file = self.tmp_path('leases') + content = dedent(""" + lease { + interface "wlp3s0"; + fixed-address 192.168.2.74; + option subnet-mask 255.255.255.0; + option routers 192.168.2.1; + option classless-static-routes 0 130.56.240.1; + renew 4 2017/07/27 18:02:30; + expire 5 2017/07/28 07:08:15; + } + """) + expected = [ + {'interface': 'wlp3s0', 'fixed-address': '192.168.2.74', + 'subnet-mask': '255.255.255.0', 'routers': '192.168.2.1', + 'classless-static-routes': '0 130.56.240.1', + 'renew': '4 2017/07/27 18:02:30', + 'expire': '5 2017/07/28 07:08:15'}] + write_file(lease_file, content) + self.assertItemsEqual(expected, parse_dhcp_lease_file(lease_file)) + @mock.patch('cloudinit.net.dhcp.EphemeralIPv4Network') @mock.patch('cloudinit.net.dhcp.maybe_perform_dhcp_discovery') def test_obtain_lease_parses_static_routes(self, m_maybe, m_ipv4): @@ -112,6 +138,31 @@ class TestDHCPRFC3442(CiTestCase): 'router': '192.168.2.1'} m_ipv4.assert_called_with(**expected_kwargs) + @mock.patch('cloudinit.net.dhcp.EphemeralIPv4Network') + @mock.patch('cloudinit.net.dhcp.maybe_perform_dhcp_discovery') + def test_obtain_centos_lease_parses_static_routes(self, m_maybe, m_ipv4): + """ + EphemeralDHPCv4 parses rfc3442 routes for EphemeralIPv4Network + for Centos Lease format + """ + lease = [ + {'interface': 'wlp3s0', 'fixed-address': '192.168.2.74', + 'subnet-mask': '255.255.255.0', 'routers': '192.168.2.1', + 'classless-static-routes': '0 130.56.240.1', + 'renew': '4 2017/07/27 18:02:30', + 'expire': '5 2017/07/28 07:08:15'}] + m_maybe.return_value = lease + eph = net.dhcp.EphemeralDHCPv4() + eph.obtain_lease() + expected_kwargs = { + 'interface': 'wlp3s0', + 'ip': '192.168.2.74', + 'prefix_or_mask': '255.255.255.0', + 'broadcast': '192.168.2.255', + 'static_routes': [('0.0.0.0/0', '130.56.240.1')], + 'router': '192.168.2.1'} + m_ipv4.assert_called_with(**expected_kwargs) + class TestDHCPParseStaticRoutes(CiTestCase): @@ -181,6 +232,20 @@ class TestDHCPParseStaticRoutes(CiTestCase): logs = self.logs.getvalue() self.assertIn(rfc3442, logs.splitlines()[0]) + def test_redhat_format(self): + redhat_format = "24.191.168.128 192.168.128.1,0 192.168.128.1" + self.assertEqual(sorted([ + ("191.168.128.0/24", "192.168.128.1"), + ("0.0.0.0/0", "192.168.128.1") + ]), sorted(parse_static_routes(redhat_format))) + + def test_redhat_format_with_a_space_too_much_after_comma(self): + redhat_format = "24.191.168.128 192.168.128.1, 0 192.168.128.1" + self.assertEqual(sorted([ + ("191.168.128.0/24", "192.168.128.1"), + ("0.0.0.0/0", "192.168.128.1") + ]), sorted(parse_static_routes(redhat_format))) + class TestDHCPDiscoveryClean(CiTestCase): with_logs = True diff --git a/cloudinit/net/tests/test_network_state.py b/cloudinit/net/tests/test_network_state.py new file mode 100644 index 00000000..fcb4a995 --- /dev/null +++ b/cloudinit/net/tests/test_network_state.py @@ -0,0 +1,47 @@ +# This file is part of cloud-init. See LICENSE file for license information. + +import mock +from cloudinit.net import network_state +from cloudinit.tests.helpers import CiTestCase + +netstate_path = 'cloudinit.net.network_state' + + +class TestNetworkStateParseConfig(CiTestCase): + + def setUp(self): + super(TestNetworkStateParseConfig, self).setUp() + nsi_path = netstate_path + '.NetworkStateInterpreter' + self.add_patch(nsi_path, 'm_nsi') + + def test_missing_version_returns_none(self): + ncfg = {} + self.assertEqual(None, network_state.parse_net_config_data(ncfg)) + + def test_unknown_versions_returns_none(self): + ncfg = {'version': 13.2} + self.assertEqual(None, network_state.parse_net_config_data(ncfg)) + + def test_version_2_passes_self_as_config(self): + ncfg = {'version': 2, 'otherconfig': {}, 'somemore': [1, 2, 3]} + network_state.parse_net_config_data(ncfg) + self.assertEqual([mock.call(version=2, config=ncfg)], + self.m_nsi.call_args_list) + + def test_valid_config_gets_network_state(self): + ncfg = {'version': 2, 'otherconfig': {}, 'somemore': [1, 2, 3]} + result = network_state.parse_net_config_data(ncfg) + self.assertNotEqual(None, result) + + def test_empty_v1_config_gets_network_state(self): + ncfg = {'version': 1, 'config': []} + result = network_state.parse_net_config_data(ncfg) + self.assertNotEqual(None, result) + + def test_empty_v2_config_gets_network_state(self): + ncfg = {'version': 2} + result = network_state.parse_net_config_data(ncfg) + self.assertNotEqual(None, result) + + +# vi: ts=4 expandtab diff --git a/doc-requirements.txt b/doc-requirements.txt index 2d4ca7be..e8977de9 100644 --- a/doc-requirements.txt +++ b/doc-requirements.txt @@ -1,4 +1,5 @@ doc8 m2r sphinx -sphinx_rtd_theme
\ No newline at end of file +sphinx_rtd_theme +pyyaml diff --git a/doc/rtd/index.rst b/doc/rtd/index.rst index d2662edf..5d90c131 100644 --- a/doc/rtd/index.rst +++ b/doc/rtd/index.rst @@ -40,6 +40,7 @@ Having trouble? We would like to help! topics/boot.rst topics/cli.rst topics/faq.rst + topics/bugs.rst .. toctree:: :hidden: diff --git a/doc/rtd/topics/bugs.rst b/doc/rtd/topics/bugs.rst new file mode 100644 index 00000000..4b60776b --- /dev/null +++ b/doc/rtd/topics/bugs.rst @@ -0,0 +1,108 @@ +.. _reporting_bugs: + +Reporting Bugs +************** + +The following documents: + +1) How to collect information for reporting bugs +2) How to file bugs to the upstream cloud-init project or for distro specific + packages + +Collect Logs +============ + +To aid in debugging, please collect the necessary logs. To do so, run the +`collect-logs` subcommand to produce a tarfile that you can easily upload: + +.. code-block:: shell-session + + $ cloud-init collect-logs + Wrote /home/ubuntu/cloud-init.tar.gz + +If your version of cloud-init does not have the `collect-logs` subcommand, +then please manually collect the base log files by doing the following: + +.. code-block:: shell-session + + $ dmesg > dmesg.txt + $ sudo journalctl -o short-precise > journal.txt + $ sudo tar -cvf cloud-init.tar dmesg.txt journal.txt /run/cloud-init \ + /var/log/cloud-init.log /var/log/cloud-init-output.log + +Report Upstream Bug +=================== + +Bugs for upstream cloud-init are tracked using Launchpad. To file a bug: + +1. Collect the necessary debug logs as described above +2. `Create a Launchpad account`_ or login to your existing account +3. `Report an upstream cloud-init bug`_ + +If debug logs are not provided, you will be asked for them before any +further time is spent debugging. If you are unable to obtain the required +logs please explain why in the bug. + +If your bug is for a specific distro using cloud-init, please first consider +reporting it with the upstream distro or confirm that it still occurs +with the latest upstream cloud-init code. See below for details on specific +distro reporting. + +Distro Specific Issues +====================== + +For issues specific to your distro please use one of the following distro +specific reporting mechanisms: + +Ubuntu +------ + +To report a bug on Ubuntu use the `ubuntu-bug` command on the affected +system to automatically collect the necessary logs and file a bug on +Launchpad: + +.. code-block:: shell-session + + $ ubuntu-bug cloud-init + +If that does not work or is not an option, please collect the logs using the +commands in the above Collect Logs section and then report the bug on the +`Ubuntu bug tracker`_. Make sure to attach your collected logs! + +Debian +------ + +To file a bug against the Debian package fo cloud-init please use the +`Debian bug tracker`_ to file against 'Package: cloud-init'. See the +`Debian bug reporting wiki`_ wiki page for more details. + +Red Hat, CentOS, & Fedora +------------------------- + +To file a bug against the Red Hat or Fedora packages of cloud-init please use +the `Red Hat bugzilla`_. + +SUSE & openSUSE +--------------- + +To file a bug against the SuSE packages of cloud-init please use the +`SUSE bugzilla`_. + +Arch +---- + +To file a bug against the Arch package of cloud-init please use the +`Arch Linux Bugtracker`_. See the `Arch bug reporting wiki`_ for more +details. + +.. _Create a Launchpad account: https://help.launchpad.net/YourAccount/NewAccount +.. _Report an upstream cloud-init bug: https://bugs.launchpad.net/cloud-init/+filebug +.. _Ubuntu bug tracker: https://bugs.launchpad.net/ubuntu/+source/cloud-init/+filebug +.. _Debian bug tracker: https://bugs.debian.org/cgi-bin/pkgreport.cgi?pkg=cloud-init;dist=unstable +.. _Debian bug reporting wiki: https://www.debian.org/Bugs/Reporting +.. _Red Hat bugzilla: https://bugzilla.redhat.com/ +.. _SUSE bugzilla: https://bugzilla.suse.com/index.cgi +.. _Arch Linux Bugtracker: https://bugs.archlinux.org/ +.. _Arch bug reporting wiki: https://wiki.archlinux.org/index.php/Bug_reporting_guidelines + +.. vi: textwidth=79 |