~curtin-dev/curtin/trunk

« back to all changes in this revision

Viewing changes to tests/vmtests/test_network_enisource.py

  • Committer: Ryan Harper
  • Date: 2016-08-29 21:06:46 UTC
  • mfrom: (416.4.28 trunk.more-ipv6)
  • Revision ID: ryan.harper@canonical.com-20160829210646-1f9pv8r691iggedz
curtin/net: overhaul of eni rendering to handle mixed ipv4/ipv6 configs

To ensure complete ipv4/ipv6 support for advanced and stacked
configurations update how curtin.net renders /etc/network/interfaces for
different releases (precise -> yakkety). ifupdown has subtle issues with
various networking features and curtin needs to ensure consistent
behavior.

- Propery handle emitting the 'auto' control tag for stacked interfaces,
  like vlans over bonds
- Workaround LP:1609367 by rendering ifupdown hooks to handle the various
  cases. This works generically in all ubuntu releases 
- Add vmtests for mtu settings
- Drop the use of ipv4 alias interfaces (eth0:1, eth0:2) and instead just
  add additional e/n/i stanzas. ifupdown already uses iproute2's /sbin/ip
  which supports adding additional ip addresses to an interface without the
  use of the v4-only interface alias structure. This provides consistent
  behavior for all types of interfaces (physical, vlan, bonds, and stacked
  interfaces) across all releases. Two side-effects: 1) users can no longer
  `ifdown eth0:1` to remove a single ip address from an interface; if down
  eth0 will take _all_ ip addresses on that interface. 2) ifconfig output
  only shows *one* ipv4 address, so users will need to use /sbin/ip addr
  show <interface> to see all ip addresses assigned to an interface.
- Add vmtests for alias settings
- Restructure all of the common network testcases into a single class
  TestNetworkTestBaseAbs, all varients testing network inherit from this
  class and override only the config file and any special case test-cases
  and file collection
- Global replace of testcase use of 'with open' and instead use
  load_collect_file()
  - Fix falsepositive uefi and multipath test this replacement exposed.
- Add ip_a_to_dict parser for `/sbin/ip a` output
  - drop ifconfig_a parser

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from . import logger, helpers
 
2
from .releases import base_vm_classes as relbase
 
3
from .test_network import TestNetworkBaseTestsAbs
 
4
 
 
5
import os
 
6
import subprocess
 
7
import yaml
 
8
 
 
9
 
 
10
class TestNetworkENISource(TestNetworkBaseTestsAbs):
 
11
    """ Curtin now emits a source /etc/network/interfaces.d/*.cfg
 
12
        line.  This test exercises this feature by emitting additional
 
13
        network configuration in /etc/network/interfaces.d/interface2.cfg
 
14
 
 
15
        This relies on the network_config.yaml of the TestClass to
 
16
        define a spare nic with no configuration.  This ensures that
 
17
        a udev rule for interface2 is emitted so we can reference the interface
 
18
        in our injected configuration.
 
19
 
 
20
        Note, ifupdown allows multiple stanzas with the same iface name
 
21
        and combines the options together during ifup.  We rely on this
 
22
        feature allowing etc/network/interfaces to have an unconfigured
 
23
        iface interface2 inet manual line, and then defer the configuration
 
24
        to /etc/network/interfaces.d/interface2.cfg
 
25
 
 
26
        This testcase then uses curtin.net.deb_parse_config method to
 
27
        extract information about what curtin wrote and compare that
 
28
        with what was actually configured (which we capture via ifconfig)
 
29
    """
 
30
 
 
31
    conf_file = "examples/tests/network_source.yaml"
 
32
 
 
33
    def test_source_cfg_exists(self):
 
34
        """Test that our curthooks wrote our injected config."""
 
35
        self.output_files_exist(["interfaces.d/interface2.cfg"])
 
36
 
 
37
    def test_etc_network_interfaces_source_cfg(self):
 
38
        """ Compare injected configuration as parsed by curtin matches
 
39
            how ifup configured the interface."""
 
40
        # interfaces uses absolute paths, fix for test-case
 
41
        interfaces = os.path.join(self.td.collect, "interfaces")
 
42
        cmd = ['sed', '-i.orig', '-e', 's,/etc/network/,,g',
 
43
               '{}'.format(interfaces)]
 
44
        subprocess.check_call(cmd, stderr=subprocess.STDOUT)
 
45
 
 
46
        curtin_ifaces = self.parse_deb_config(interfaces)
 
47
        logger.debug('parsed eni dict:\n{}'.format(
 
48
            yaml.dump(curtin_ifaces, default_flow_style=False, indent=4)))
 
49
        print('parsed eni dict:\n{}'.format(
 
50
            yaml.dump(curtin_ifaces, default_flow_style=False, indent=4)))
 
51
 
 
52
        ip_a = self.load_collect_file("ip_a")
 
53
        logger.debug('ip a:\n{}'.format(ip_a))
 
54
 
 
55
        ip_a_dict = helpers.ip_a_to_dict(ip_a)
 
56
        logger.debug('parsed ip_a dict:\n{}'.format(
 
57
            yaml.dump(ip_a_dict, default_flow_style=False, indent=4)))
 
58
        print('parsed ip_a dict:\n{}'.format(
 
59
            yaml.dump(ip_a_dict, default_flow_style=False, indent=4)))
 
60
 
 
61
        iface = 'interface2'
 
62
        self.assertTrue(iface in curtin_ifaces)
 
63
 
 
64
        expected_address = curtin_ifaces[iface].get('address', None)
 
65
        self.assertIsNotNone(expected_address)
 
66
 
 
67
        # handle CIDR notation
 
68
        def _nocidr(addr):
 
69
            return addr.split("/")[0]
 
70
 
 
71
        [actual_address] = [ip.get('address') for ip in
 
72
                            ip_a_dict[iface].get('inet4', [])]
 
73
        self.assertEqual(_nocidr(expected_address), _nocidr(actual_address))
 
74
 
 
75
 
 
76
class PreciseTestNetworkENISource(relbase.precise, TestNetworkENISource):
 
77
    __test__ = False
 
78
    # not working, still debugging though; possible older ifupdown doesn't
 
79
    # like the multiple iface method.
 
80
 
 
81
 
 
82
class TrustyTestNetworkENISource(relbase.trusty, TestNetworkENISource):
 
83
    __test__ = True
 
84
 
 
85
 
 
86
class XenialTestNetworkENISource(relbase.xenial, TestNetworkENISource):
 
87
    __test__ = True
 
88
 
 
89
 
 
90
class YakketyTestNetworkENISource(relbase.yakkety, TestNetworkENISource):
 
91
    __test__ = True