~curtin-dev/curtin/trunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
from . import logger, helpers
from .releases import base_vm_classes as relbase
from .test_network import TestNetworkBaseTestsAbs

import os
import subprocess
import yaml


class TestNetworkENISource(TestNetworkBaseTestsAbs):
    """ Curtin now emits a source /etc/network/interfaces.d/*.cfg
        line.  This test exercises this feature by emitting additional
        network configuration in /etc/network/interfaces.d/interface2.cfg

        This relies on the network_config.yaml of the TestClass to
        define a spare nic with no configuration.  This ensures that
        a udev rule for interface2 is emitted so we can reference the interface
        in our injected configuration.

        Note, ifupdown allows multiple stanzas with the same iface name
        and combines the options together during ifup.  We rely on this
        feature allowing etc/network/interfaces to have an unconfigured
        iface interface2 inet manual line, and then defer the configuration
        to /etc/network/interfaces.d/interface2.cfg

        This testcase then uses curtin.net.deb_parse_config method to
        extract information about what curtin wrote and compare that
        with what was actually configured (which we capture via ifconfig)
    """

    conf_file = "examples/tests/network_source.yaml"

    def test_source_cfg_exists(self):
        """Test that our curthooks wrote our injected config."""
        self.output_files_exist(["interfaces.d/interface2.cfg"])

    def test_etc_network_interfaces_source_cfg(self):
        """ Compare injected configuration as parsed by curtin matches
            how ifup configured the interface."""
        # interfaces uses absolute paths, fix for test-case
        interfaces = os.path.join(self.td.collect, "interfaces")
        cmd = ['sed', '-i.orig', '-e', 's,/etc/network/,,g',
               '{}'.format(interfaces)]
        subprocess.check_call(cmd, stderr=subprocess.STDOUT)

        curtin_ifaces = self.parse_deb_config(interfaces)
        logger.debug('parsed eni dict:\n{}'.format(
            yaml.dump(curtin_ifaces, default_flow_style=False, indent=4)))
        print('parsed eni dict:\n{}'.format(
            yaml.dump(curtin_ifaces, default_flow_style=False, indent=4)))

        ip_a = self.load_collect_file("ip_a")
        logger.debug('ip a:\n{}'.format(ip_a))

        ip_a_dict = helpers.ip_a_to_dict(ip_a)
        logger.debug('parsed ip_a dict:\n{}'.format(
            yaml.dump(ip_a_dict, default_flow_style=False, indent=4)))
        print('parsed ip_a dict:\n{}'.format(
            yaml.dump(ip_a_dict, default_flow_style=False, indent=4)))

        iface = 'interface2'
        self.assertTrue(iface in curtin_ifaces)

        expected_address = curtin_ifaces[iface].get('address', None)
        self.assertIsNotNone(expected_address)

        # handle CIDR notation
        def _nocidr(addr):
            return addr.split("/")[0]

        [actual_address] = [ip.get('address') for ip in
                            ip_a_dict[iface].get('inet4', [])]
        self.assertEqual(_nocidr(expected_address), _nocidr(actual_address))


class PreciseTestNetworkENISource(relbase.precise, TestNetworkENISource):
    __test__ = False
    # not working, still debugging though; possible older ifupdown doesn't
    # like the multiple iface method.


class TrustyTestNetworkENISource(relbase.trusty, TestNetworkENISource):
    __test__ = True


class XenialTestNetworkENISource(relbase.xenial, TestNetworkENISource):
    __test__ = True


class YakketyTestNetworkENISource(relbase.yakkety, TestNetworkENISource):
    __test__ = True