Merge lp:~raharper/curtin/trunk.more-ipv6 into lp:~curtin-dev/curtin/trunk
- trunk.more-ipv6
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 421 |
Proposed branch: | lp:~raharper/curtin/trunk.more-ipv6 |
Merge into: | lp:~curtin-dev/curtin/trunk |
Diff against target: |
3212 lines (+1750/-914) 30 files modified
curtin/commands/apply_net.py (+155/-1) curtin/commands/curthooks.py (+4/-51) curtin/net/__init__.py (+67/-30) curtin/net/network_state.py (+45/-1) examples/network-ipv6-bond-vlan.yaml (+56/-0) examples/tests/basic_network_static_ipv6.yaml (+22/-0) examples/tests/network_alias.yaml (+125/-0) examples/tests/network_mtu.yaml (+88/-0) examples/tests/network_source_ipv6.yaml (+31/-0) examples/tests/vlan_network_ipv6.yaml (+92/-0) tests/unittests/test_net.py (+54/-13) tests/vmtests/helpers.py (+129/-166) tests/vmtests/test_basic.py (+17/-39) tests/vmtests/test_bcache_basic.py (+5/-8) tests/vmtests/test_bonding.py (+0/-205) tests/vmtests/test_mdadm_bcache.py (+9/-11) tests/vmtests/test_multipath.py (+5/-13) tests/vmtests/test_network.py (+205/-352) tests/vmtests/test_network_alias.py (+40/-0) tests/vmtests/test_network_bonding.py (+63/-0) tests/vmtests/test_network_enisource.py (+91/-0) tests/vmtests/test_network_ipv6.py (+53/-0) tests/vmtests/test_network_ipv6_enisource.py (+26/-0) tests/vmtests/test_network_ipv6_static.py (+42/-0) tests/vmtests/test_network_ipv6_vlan.py (+34/-0) tests/vmtests/test_network_mtu.py (+155/-0) tests/vmtests/test_network_static.py (+44/-0) tests/vmtests/test_network_vlan.py (+77/-0) tests/vmtests/test_raid5_bcache.py (+5/-8) tests/vmtests/test_uefi_basic.py (+11/-16) |
To merge this branch: | bzr merge lp:~raharper/curtin/trunk.more-ipv6 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Scott Moser (community) | Approve | ||
Server Team CI bot | continuous-integration | Approve | |
Review via email: mp+304027@code.launchpad.net |
Commit message
curtin/net: overhaul of eni rendering to handle mixed ipv4/ipv6 configurations
To ensure complete ipv4/ipv6 support for advanced and stacked configurations update
how curtin.net renders /etc/network/
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
- 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.
- Restructure all of the common network testcases into a single class TestNetworkTest
- Global replace of testcase use of 'with open' and instead use load_collect_file()
- Add ip_a_to_dict parser for `/sbin/ip a` output
Description of the change
curtin/net: overhaul eni rendering to handle mixed ipv4/ipv6 configurations
To ensure complete ipv4/ipv6 support for advanced and stacked
configurations update how curtin.net renders /etc/network/
different releases (precise -> yakkety) ifupdown has subtle issues with
various networking features and curtin needs to ensure consistent
behavior.
- Properly 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.
- 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 'ip addr show <interface>' to see all ip addresses
assigned to an interface.
- Restructure all of the common network testcases into a single class
TestNetworkTe
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_
- Add ip_a_to_dict parser for `/sbin/ip a` output
Server Team CI bot (server-team-bot) wrote : | # |
- 437. By Ryan Harper
-
merge from trunk
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:437
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Christian Ehrhardt (paelzer) wrote : | # |
Hi Ryan,
great work - a few minor comments and discussion points added inline.
Ryan Harper (raharper) wrote : | # |
On Fri, Aug 26, 2016 at 3:59 AM, ChristianEhrhardt <
<email address hidden>> wrote:
> Hi Ryan,
> great work - a few minor comments and discussion points added inline.
>
Thanks for the review!
>
> Diff comments:
>
> > === modified file 'curtin/
> > --- curtin/
> > +++ curtin/
> > @@ -26,6 +26,54 @@
> >
> > LOG = log.LOG
> >
> > +IFUPDOWN_
>
> Maybe play it even safer with -e to better fail than echoing potentially
> weird stuff to the mtu file.
>
> Same for line 23
>
Yeah, actually should drop the -x; I'll look at other ifupdown hooks and
see what they use.
>
> > +# injected by curtin installer
> > +
> > +[ "$IFACE" != "lo" ] || exit 0
> > +
> > +# Trigger only if MTU configured
> > +[ -n "$IF_MTU" ] || exit 0
> > +
> > +CUR_DEV_
> > +CUR_IPV6_
> > +[ -n "$CUR_DEV_MTU" ] && echo $CUR_DEV_MTU > /run/network/
> > +[ -n "$CUR_IPV6_MTU" ] && echo $CUR_IPV6_MTU >
> /run/network/
> > +exit 0
> > +"""
> > +
> > +IFUPDOWN_
> > +# injected by curtin installer
> > +
> > +[ "$IFACE" != "lo" ] || exit 0
> > +
> > +# Trigger only if MTU configured
> > +[ -n "$IF_MTU" ] || exit 0
> > +
> > +PRE_DEV_MTU=$(cat /run/network/
> > +CUR_DEV_
> > +PRE_IPV6_MTU=$(cat /run/network/
>
> I'm not sure if ${var} references are compliant, but here it would really
> enhance readability and maintainability. e.g. at ${IFACE}_ipv6 which would
> make is clearer where var and string meet.
>
It appears that ${VAR} works; most of the hooks don't use that; not
entirely sure why. I can update.
>
> > +CUR_IPV6_
> > +
> > +if [ "$ADDRFAM" = "inet6" ]; then
> > + # We need to check the underlying interface MTU and
> > + # raise it if the IPV6 mtu is larger
> > + if [ $CUR_DEV_MTU -lt $IF_MTU ]; then
> > + /bin/ip link set $IFACE mtu $IF_MTU
> > + fi
> > + /sbin/sysctl -q -e -w net.ipv6.
>
> I'm not sure if it would be wiser to put all the binary paths into an
> initial INIT phase.
> Probably with a which or anything like it, just for the case that ip,
> sysctl or others ever move to a different place.
> A which would make it fully dynamic, a central definition easier to
> maintain at one place.
> Open for discussion thou since it can have drawbacks like resolving to the
> wrong one in the worst case.
>
All of the existing tooks (and ifupdown itself) call via full path.
>
> > +
> > +elif [ "$ADDRFAM" = "inet" ]; then
> > + # handle the clobber case where inet mtu changes v6 mtu
> > + # ifupdown will already have set dev mtu, so lower mtu
> > + # if needed. If v6 mtu was larger, it get's clamped down
> > + # to the dev MTU value.
> > + if [ $PRE_IPV6_MTU -lt $CUR_IPV6_MTU ]; then
> > + /sbin/sysctl -q -e -w net.ipv6.
> > + fi
> > +fi
> > +exit 0
> > +"""
> > +
> >
> > ...
Scott Moser (smoser) wrote : | # |
NITPICK: i reformatted your commit message to keep line lengths < 74 and some spelling.
i like the general idea, and the fact that you added some good vmtest tests.
Ryan Harper (raharper) wrote : | # |
On Fri, Aug 26, 2016 at 9:23 AM, Scott Moser <email address hidden> wrote:
> NITPICK: i reformatted your commit message to keep line lengths < 74 and
> some spelling.
>
>
> i like the general idea, and the fact that you added some good vmtest
> tests.
>
> Diff comments:
>
> > === modified file 'curtin/
> > --- curtin/
> > +++ curtin/
> > @@ -26,6 +26,54 @@
> >
> > LOG = log.LOG
> >
> > +IFUPDOWN_
>
> as much as i *hate* set -e, i agree here. either we need to run with -e,
> or otherwise correctly exit failure on failure rather than barreling on.
>
ACK
>
> > +# injected by curtin installer
> > +
> > +[ "$IFACE" != "lo" ] || exit 0
> > +
> > +# Trigger only if MTU configured
> > +[ -n "$IF_MTU" ] || exit 0
> > +
> > +CUR_DEV_
>
> [ ! -e /sys/class/
> /sys/class/
>
Nice, will change.
>
> > +CUR_IPV6_
> > +[ -n "$CUR_DEV_MTU" ] && echo $CUR_DEV_MTU > /run/network/
> > +[ -n "$CUR_IPV6_MTU" ] && echo $CUR_IPV6_MTU >
> /run/network/
> > +exit 0
> > +"""
> > +
> > +IFUPDOWN_
> > +# injected by curtin installer
> > +
> > +[ "$IFACE" != "lo" ] || exit 0
> > +
> > +# Trigger only if MTU configured
> > +[ -n "$IF_MTU" ] || exit 0
> > +
> > +PRE_DEV_MTU=$(cat /run/network/
>
> +one on christian's {} i know its obnoxious, but there are times when
> doing something like above ends up referencing a variable like '$IFACE_dev'
> rather than ${IFACE}_dev. i have never been able to find out a pattern on
> it, but its why i alays surround variables names. Also makes search and
> replace for var names easier.
>
I entirely agree with you; I was keeping with existing style of ifupdown
hooks. But I'm happy to do things safer.
>
> > +CUR_DEV_
>
> read CUR_DEV_MTU < /sys/class/
> is lots faster and equivalent.
>
> i realize this is picking on the order of hundreths of a second, but this
> blocks network coming up which blocks boot.
>
Understood, worth a change.
>
> > +PRE_IPV6_MTU=$(cat /run/network/
> > +CUR_IPV6_
>
> echo > /sys/class/net .... ? rather than sysctl ? is there a reason for
> one over the other ?
> i thought you recently told me sysctl was just doing that.
>
I can replace the sysctl -n with a read; it's somewhat non-obvious
that sysctl net. implies /proc/sys/net . But it saves the exec overhead
>
> > +
> > +if [ "$ADDRFAM" = "inet6" ]; then
> > + # We need to check the underlying interface MTU and
> > + # raise it if the IPV6 mtu is larger
> > + if [ $CUR_DEV_MTU -lt $IF_MTU ]; then
> > + /bin/ip link set $IFACE mtu $IF_MTU
> > + fi
> > + /sbin/sysctl -q -e -w net.ipv6.
>
> even better, just do not hard code paths.
> if /sbin/ is not in your path you are probably foobarred in other ways
> you've not considered.
> if /...
- 438. By Ryan Harper
-
Addressed review comments
curtin.
commands. apply_net:
- use /bin/sh -e for hooks
- use ${VAR} for references
- replace execs with read and echo
- remove hardcoded paths to binaries
- make use of util.target_path for safer path construction
- move debugging output to log.debugcurtin.net:
- iface_start_entry: drop unused index parameter
- drop unused functions: subnet_is_ipv4, list_ipv4_subnets,
iface_add_postup
- Add comment to document how we skip repeated 'auto $IFACE' lines
and why we don'ttests.vmtests.
helpers
- drop unused ifconfig-a parsing
- update ip_a_to_dict comment to display parsed outputtests.vmtests.
test_network_ enisource
- replace use of ifconfig_to_dict with ip_a_to_dict - 439. By Ryan Harper
-
Fix use of util.target_path(); workaround dash bug lp:598275
- 440. By Ryan Harper
-
fix trailing whitespace
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:439
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:440
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
- 441. By Ryan Harper
-
mtuhook: switch to bash to use read vs. sysctl exec
- 442. By Ryan Harper
-
vmtest:multipath fix up readlink on holders data
- 443. By Ryan Harper
-
vmtests:uefi fix broke test, handle variance in sys/firmware/efi contents
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:443
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
- 444. By Ryan Harper
-
merge from trunk
Server Team CI bot (server-team-bot) wrote : | # |
PASSED: Continuous integration, rev:444
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
Scott Moser (smoser) wrote : | # |
This looks as good to me as something can look for something so large.
You have added some tests and such, so LGTM.
Preview Diff
1 | === modified file 'curtin/commands/apply_net.py' | |||
2 | --- curtin/commands/apply_net.py 2016-08-22 19:39:06 +0000 | |||
3 | +++ curtin/commands/apply_net.py 2016-08-29 20:01:45 +0000 | |||
4 | @@ -26,6 +26,57 @@ | |||
5 | 26 | 26 | ||
6 | 27 | LOG = log.LOG | 27 | LOG = log.LOG |
7 | 28 | 28 | ||
8 | 29 | IFUPDOWN_IPV6_MTU_PRE_HOOK = """#!/bin/bash -e | ||
9 | 30 | # injected by curtin installer | ||
10 | 31 | |||
11 | 32 | [ "${IFACE}" != "lo" ] || exit 0 | ||
12 | 33 | |||
13 | 34 | # Trigger only if MTU configured | ||
14 | 35 | [ -n "${IF_MTU}" ] || exit 0 | ||
15 | 36 | |||
16 | 37 | read CUR_DEV_MTU </sys/class/net/${IFACE}/mtu ||: | ||
17 | 38 | read CUR_IPV6_MTU </proc/sys/net/ipv6/conf/${IFACE}/mtu ||: | ||
18 | 39 | [ -n "${CUR_DEV_MTU}" ] && echo ${CUR_DEV_MTU} > /run/network/${IFACE}_dev.mtu | ||
19 | 40 | [ -n "${CUR_IPV6_MTU}" ] && | ||
20 | 41 | echo ${CUR_IPV6_MTU} > /run/network/${IFACE}_ipv6.mtu | ||
21 | 42 | exit 0 | ||
22 | 43 | """ | ||
23 | 44 | |||
24 | 45 | IFUPDOWN_IPV6_MTU_POST_HOOK = """#!/bin/bash -e | ||
25 | 46 | # injected by curtin installer | ||
26 | 47 | |||
27 | 48 | [ "${IFACE}" != "lo" ] || exit 0 | ||
28 | 49 | |||
29 | 50 | # Trigger only if MTU configured | ||
30 | 51 | [ -n "${IF_MTU}" ] || exit 0 | ||
31 | 52 | |||
32 | 53 | read PRE_DEV_MTU </run/network/${IFACE}_dev.mtu ||: | ||
33 | 54 | read CUR_DEV_MTU </sys/class/net/${IFACE}/mtu ||: | ||
34 | 55 | read PRE_IPV6_MTU </run/network/${IFACE}_ipv6.mtu ||: | ||
35 | 56 | read CUR_IPV6_MTU </proc/sys/net/ipv6/conf/${IFACE}/mtu ||: | ||
36 | 57 | |||
37 | 58 | if [ "${ADDRFAM}" = "inet6" ]; then | ||
38 | 59 | # We need to check the underlying interface MTU and | ||
39 | 60 | # raise it if the IPV6 mtu is larger | ||
40 | 61 | if [ ${CUR_DEV_MTU} -lt ${IF_MTU} ]; then | ||
41 | 62 | ip link set ${IFACE} mtu ${IF_MTU} | ||
42 | 63 | fi | ||
43 | 64 | # sysctl -q -e -w net.ipv6.conf.${IFACE}.mtu=${IF_MTU} | ||
44 | 65 | echo ${IF_MTU} >/proc/sys/net/ipv6/conf/${IFACE}/mtu ||: | ||
45 | 66 | |||
46 | 67 | elif [ "${ADDRFAM}" = "inet" ]; then | ||
47 | 68 | # handle the clobber case where inet mtu changes v6 mtu. | ||
48 | 69 | # ifupdown will already have set dev mtu, so lower mtu | ||
49 | 70 | # if needed. If v6 mtu was larger, it get's clamped down | ||
50 | 71 | # to the dev MTU value. | ||
51 | 72 | if [ ${PRE_IPV6_MTU} -lt ${CUR_IPV6_MTU} ]; then | ||
52 | 73 | # sysctl -q -e -w net.ipv6.conf.${IFACE}.mtu=${PRE_IPV6_MTU} | ||
53 | 74 | echo ${PRE_IPV6_MTU} >/proc/sys/net/ipv6/conf/${IFACE}/mtu ||: | ||
54 | 75 | fi | ||
55 | 76 | fi | ||
56 | 77 | exit 0 | ||
57 | 78 | """ | ||
58 | 79 | |||
59 | 29 | 80 | ||
60 | 30 | def apply_net(target, network_state=None, network_config=None): | 81 | def apply_net(target, network_state=None, network_config=None): |
61 | 31 | if network_state is None and network_config is None: | 82 | if network_state is None and network_config is None: |
62 | @@ -45,6 +96,108 @@ | |||
63 | 45 | 96 | ||
64 | 46 | net.render_network_state(target=target, network_state=ns) | 97 | net.render_network_state(target=target, network_state=ns) |
65 | 47 | 98 | ||
66 | 99 | _maybe_remove_legacy_eth0(target) | ||
67 | 100 | LOG.info('Attempting to remove ipv6 privacy extensions') | ||
68 | 101 | _disable_ipv6_privacy_extensions(target) | ||
69 | 102 | _patch_ifupdown_ipv6_mtu_hook(target) | ||
70 | 103 | |||
71 | 104 | |||
72 | 105 | def _patch_ifupdown_ipv6_mtu_hook(target, | ||
73 | 106 | prehookfn="etc/network/if-pre-up.d/mtuipv6", | ||
74 | 107 | posthookfn="etc/network/if-up.d/mtuipv6"): | ||
75 | 108 | |||
76 | 109 | contents = { | ||
77 | 110 | 'prehook': IFUPDOWN_IPV6_MTU_PRE_HOOK, | ||
78 | 111 | 'posthook': IFUPDOWN_IPV6_MTU_POST_HOOK, | ||
79 | 112 | } | ||
80 | 113 | |||
81 | 114 | hookfn = { | ||
82 | 115 | 'prehook': prehookfn, | ||
83 | 116 | 'posthook': posthookfn, | ||
84 | 117 | } | ||
85 | 118 | |||
86 | 119 | for hook in ['prehook', 'posthook']: | ||
87 | 120 | fn = hookfn[hook] | ||
88 | 121 | cfg = util.target_path(target, path=fn) | ||
89 | 122 | LOG.info('Injecting fix for ipv6 mtu settings: %s', cfg) | ||
90 | 123 | util.write_file(cfg, contents[hook], mode=0o755) | ||
91 | 124 | |||
92 | 125 | |||
93 | 126 | def _disable_ipv6_privacy_extensions(target, | ||
94 | 127 | path="etc/sysctl.d/10-ipv6-privacy.conf"): | ||
95 | 128 | |||
96 | 129 | """Ubuntu server image sets a preference to use IPv6 privacy extensions | ||
97 | 130 | by default; this races with the cloud-image desire to disable them. | ||
98 | 131 | Resolve this by allowing the cloud-image setting to win. """ | ||
99 | 132 | |||
100 | 133 | cfg = util.target_path(target, path=path) | ||
101 | 134 | if not os.path.exists(cfg): | ||
102 | 135 | LOG.warn('Failed to find ipv6 privacy conf file %s', cfg) | ||
103 | 136 | return | ||
104 | 137 | |||
105 | 138 | bmsg = "Disabling IPv6 privacy extensions config may not apply." | ||
106 | 139 | try: | ||
107 | 140 | contents = util.load_file(cfg) | ||
108 | 141 | known_contents = ["net.ipv6.conf.all.use_tempaddr = 2", | ||
109 | 142 | "net.ipv6.conf.default.use_tempaddr = 2"] | ||
110 | 143 | lines = [f.strip() for f in contents.splitlines() | ||
111 | 144 | if not f.startswith("#")] | ||
112 | 145 | if lines == known_contents: | ||
113 | 146 | LOG.info('deleting file: %s', cfg) | ||
114 | 147 | util.del_file(cfg) | ||
115 | 148 | msg = "removed %s with known contents" % cfg | ||
116 | 149 | curtin_contents = '\n'.join( | ||
117 | 150 | ["# IPv6 Privacy Extensions (RFC 4941)", | ||
118 | 151 | "# Disabled by curtin", | ||
119 | 152 | "# net.ipv6.conf.all.use_tempaddr = 2", | ||
120 | 153 | "# net.ipv6.conf.default.use_tempaddr = 2"]) | ||
121 | 154 | util.write_file(cfg, curtin_contents) | ||
122 | 155 | else: | ||
123 | 156 | LOG.info('skipping, content didnt match') | ||
124 | 157 | LOG.debug("found content:\n%s", lines) | ||
125 | 158 | LOG.debug("expected contents:\n%s", known_contents) | ||
126 | 159 | msg = (bmsg + " '%s' exists with user configured content." % cfg) | ||
127 | 160 | except: | ||
128 | 161 | msg = bmsg + " %s exists, but could not be read." % cfg | ||
129 | 162 | LOG.exception(msg) | ||
130 | 163 | return | ||
131 | 164 | |||
132 | 165 | |||
133 | 166 | def _maybe_remove_legacy_eth0(target, | ||
134 | 167 | path="etc/network/interfaces.d/eth0.cfg"): | ||
135 | 168 | """Ubuntu cloud images previously included a 'eth0.cfg' that had | ||
136 | 169 | hard coded content. That file would interfere with the rendered | ||
137 | 170 | configuration if it was present. | ||
138 | 171 | |||
139 | 172 | if the file does not exist do nothing. | ||
140 | 173 | If the file exists: | ||
141 | 174 | - with known content, remove it and warn | ||
142 | 175 | - with unknown content, leave it and warn | ||
143 | 176 | """ | ||
144 | 177 | |||
145 | 178 | cfg = util.target_path(target, path=path) | ||
146 | 179 | if not os.path.exists(cfg): | ||
147 | 180 | LOG.warn('Failed to find legacy network conf file %s', cfg) | ||
148 | 181 | return | ||
149 | 182 | |||
150 | 183 | bmsg = "Dynamic networking config may not apply." | ||
151 | 184 | try: | ||
152 | 185 | contents = util.load_file(cfg) | ||
153 | 186 | known_contents = ["auto eth0", "iface eth0 inet dhcp"] | ||
154 | 187 | lines = [f.strip() for f in contents.splitlines() | ||
155 | 188 | if not f.startswith("#")] | ||
156 | 189 | if lines == known_contents: | ||
157 | 190 | util.del_file(cfg) | ||
158 | 191 | msg = "removed %s with known contents" % cfg | ||
159 | 192 | else: | ||
160 | 193 | msg = (bmsg + " '%s' exists with user configured content." % cfg) | ||
161 | 194 | except: | ||
162 | 195 | msg = bmsg + " %s exists, but could not be read." % cfg | ||
163 | 196 | LOG.exception(msg) | ||
164 | 197 | return | ||
165 | 198 | |||
166 | 199 | LOG.warn(msg) | ||
167 | 200 | |||
168 | 48 | 201 | ||
169 | 49 | def apply_net_main(args): | 202 | def apply_net_main(args): |
170 | 50 | # curtin apply_net [--net-state=/config/netstate.yml] [--target=/] | 203 | # curtin apply_net [--net-state=/config/netstate.yml] [--target=/] |
171 | @@ -76,6 +229,7 @@ | |||
172 | 76 | apply_net(target=state['target'], | 229 | apply_net(target=state['target'], |
173 | 77 | network_state=state['network_state'], | 230 | network_state=state['network_state'], |
174 | 78 | network_config=state['network_config']) | 231 | network_config=state['network_config']) |
175 | 232 | |||
176 | 79 | except Exception: | 233 | except Exception: |
177 | 80 | LOG.exception('failed to apply network config') | 234 | LOG.exception('failed to apply network config') |
178 | 81 | return 1 | 235 | return 1 |
179 | @@ -91,7 +245,7 @@ | |||
180 | 91 | 'metavar': 'NETSTATE', 'action': 'store', | 245 | 'metavar': 'NETSTATE', 'action': 'store', |
181 | 92 | 'default': os.environ.get('OUTPUT_NETWORK_STATE')}), | 246 | 'default': os.environ.get('OUTPUT_NETWORK_STATE')}), |
182 | 93 | (('-t', '--target'), | 247 | (('-t', '--target'), |
184 | 94 | {'help': ('target filesystem root to add swap file to. ' | 248 | {'help': ('target filesystem root to configure networking to. ' |
185 | 95 | 'default is env["TARGET_MOUNT_POINT"]'), | 249 | 'default is env["TARGET_MOUNT_POINT"]'), |
186 | 96 | 'metavar': 'TARGET', 'action': 'store', | 250 | 'metavar': 'TARGET', 'action': 'store', |
187 | 97 | 'default': os.environ.get('TARGET_MOUNT_POINT')}), | 251 | 'default': os.environ.get('TARGET_MOUNT_POINT')}), |
188 | 98 | 252 | ||
189 | === modified file 'curtin/commands/curthooks.py' | |||
190 | --- curtin/commands/curthooks.py 2016-07-29 17:19:20 +0000 | |||
191 | +++ curtin/commands/curthooks.py 2016-08-29 20:01:45 +0000 | |||
192 | @@ -28,9 +28,8 @@ | |||
193 | 28 | from curtin.log import LOG | 28 | from curtin.log import LOG |
194 | 29 | from curtin import swap | 29 | from curtin import swap |
195 | 30 | from curtin import util | 30 | from curtin import util |
196 | 31 | from curtin import net | ||
197 | 32 | from curtin.reporter import events | 31 | from curtin.reporter import events |
199 | 33 | from curtin.commands import apt_config | 32 | from curtin.commands import apply_net, apt_config |
200 | 34 | 33 | ||
201 | 35 | from . import populate_one_subcmd | 34 | from . import populate_one_subcmd |
202 | 36 | 35 | ||
203 | @@ -109,42 +108,6 @@ | |||
204 | 109 | shutil.move(local_conf, local_conf + ".old") | 108 | shutil.move(local_conf, local_conf + ".old") |
205 | 110 | 109 | ||
206 | 111 | 110 | ||
207 | 112 | def _maybe_remove_legacy_eth0(target, | ||
208 | 113 | path="/etc/network/interfaces.d/eth0.cfg"): | ||
209 | 114 | """Ubuntu cloud images previously included a 'eth0.cfg' that had | ||
210 | 115 | hard coded content. That file would interfere with the rendered | ||
211 | 116 | configuration if it was present. | ||
212 | 117 | |||
213 | 118 | if the file does not exist do nothing. | ||
214 | 119 | If the file exists: | ||
215 | 120 | - with known content, remove it and warn | ||
216 | 121 | - with unknown content, leave it and warn | ||
217 | 122 | """ | ||
218 | 123 | |||
219 | 124 | cfg = os.path.sep.join([target, path]) | ||
220 | 125 | if not os.path.exists(cfg): | ||
221 | 126 | LOG.warn('Failed to find legacy conf file %s', cfg) | ||
222 | 127 | return | ||
223 | 128 | |||
224 | 129 | bmsg = "Dynamic networking config may not apply." | ||
225 | 130 | try: | ||
226 | 131 | contents = util.load_file(cfg) | ||
227 | 132 | known_contents = ["auto eth0", "iface eth0 inet dhcp"] | ||
228 | 133 | lines = [f.strip() for f in contents.splitlines() | ||
229 | 134 | if not f.startswith("#")] | ||
230 | 135 | if lines == known_contents: | ||
231 | 136 | util.del_file(cfg) | ||
232 | 137 | msg = "removed %s with known contents" % cfg | ||
233 | 138 | else: | ||
234 | 139 | msg = (bmsg + " '%s' exists with user configured content." % cfg) | ||
235 | 140 | except: | ||
236 | 141 | msg = bmsg + " %s exists, but could not be read." % cfg | ||
237 | 142 | LOG.exception(msg) | ||
238 | 143 | return | ||
239 | 144 | |||
240 | 145 | LOG.warn(msg) | ||
241 | 146 | |||
242 | 147 | |||
243 | 148 | def setup_zipl(cfg, target): | 111 | def setup_zipl(cfg, target): |
244 | 149 | if platform.machine() != 's390x': | 112 | if platform.machine() != 's390x': |
245 | 150 | return | 113 | return |
246 | @@ -411,7 +374,6 @@ | |||
247 | 411 | 374 | ||
248 | 412 | 375 | ||
249 | 413 | def apply_networking(target, state): | 376 | def apply_networking(target, state): |
250 | 414 | netstate = state.get('network_state') | ||
251 | 415 | netconf = state.get('network_config') | 377 | netconf = state.get('network_config') |
252 | 416 | interfaces = state.get('interfaces') | 378 | interfaces = state.get('interfaces') |
253 | 417 | 379 | ||
254 | @@ -422,22 +384,13 @@ | |||
255 | 422 | return True | 384 | return True |
256 | 423 | return False | 385 | return False |
257 | 424 | 386 | ||
268 | 425 | ns = None | 387 | if is_valid_src(netconf): |
269 | 426 | if is_valid_src(netstate): | 388 | LOG.info("applying network_config") |
270 | 427 | LOG.debug("applying network_state") | 389 | apply_net.apply_net(target, network_state=None, network_config=netconf) |
261 | 428 | ns = net.network_state.from_state_file(netstate) | ||
262 | 429 | elif is_valid_src(netconf): | ||
263 | 430 | LOG.debug("applying network_config") | ||
264 | 431 | ns = net.parse_net_config(netconf) | ||
265 | 432 | |||
266 | 433 | if ns is not None: | ||
267 | 434 | net.render_network_state(target=target, network_state=ns) | ||
271 | 435 | else: | 390 | else: |
272 | 436 | LOG.debug("copying interfaces") | 391 | LOG.debug("copying interfaces") |
273 | 437 | copy_interfaces(interfaces, target) | 392 | copy_interfaces(interfaces, target) |
274 | 438 | 393 | ||
275 | 439 | _maybe_remove_legacy_eth0(target) | ||
276 | 440 | |||
277 | 441 | 394 | ||
278 | 442 | def copy_interfaces(interfaces, target): | 395 | def copy_interfaces(interfaces, target): |
279 | 443 | if not interfaces: | 396 | if not interfaces: |
280 | 444 | 397 | ||
281 | === modified file 'curtin/net/__init__.py' | |||
282 | --- curtin/net/__init__.py 2016-06-16 17:43:17 +0000 | |||
283 | +++ curtin/net/__init__.py 2016-08-29 20:01:45 +0000 | |||
284 | @@ -299,7 +299,7 @@ | |||
285 | 299 | mac = iface.get('mac_address', '') | 299 | mac = iface.get('mac_address', '') |
286 | 300 | # len(macaddr) == 2 * 6 + 5 == 17 | 300 | # len(macaddr) == 2 * 6 + 5 == 17 |
287 | 301 | if ifname and mac and len(mac) == 17: | 301 | if ifname and mac and len(mac) == 17: |
289 | 302 | content += generate_udev_rule(ifname, mac) | 302 | content += generate_udev_rule(ifname, mac.lower()) |
290 | 303 | 303 | ||
291 | 304 | return content | 304 | return content |
292 | 305 | 305 | ||
293 | @@ -349,7 +349,7 @@ | |||
294 | 349 | 'subnets', | 349 | 'subnets', |
295 | 350 | 'type', | 350 | 'type', |
296 | 351 | ] | 351 | ] |
298 | 352 | if iface['type'] not in ['bond', 'bridge']: | 352 | if iface['type'] not in ['bond', 'bridge', 'vlan']: |
299 | 353 | ignore_map.append('mac_address') | 353 | ignore_map.append('mac_address') |
300 | 354 | 354 | ||
301 | 355 | for key, value in iface.items(): | 355 | for key, value in iface.items(): |
302 | @@ -361,26 +361,52 @@ | |||
303 | 361 | return content | 361 | return content |
304 | 362 | 362 | ||
305 | 363 | 363 | ||
308 | 364 | def render_route(route): | 364 | def render_route(route, indent=""): |
309 | 365 | content = "up route add" | 365 | """When rendering routes for an iface, in some cases applying a route |
310 | 366 | may result in the route command returning non-zero which produces | ||
311 | 367 | some confusing output for users manually using ifup/ifdown[1]. To | ||
312 | 368 | that end, we will optionally include an '|| true' postfix to each | ||
313 | 369 | route line allowing users to work with ifup/ifdown without using | ||
314 | 370 | --force option. | ||
315 | 371 | |||
316 | 372 | We may at somepoint not want to emit this additional postfix, and | ||
317 | 373 | add a 'strict' flag to this function. When called with strict=True, | ||
318 | 374 | then we will not append the postfix. | ||
319 | 375 | |||
320 | 376 | 1. http://askubuntu.com/questions/168033/ | ||
321 | 377 | how-to-set-static-routes-in-ubuntu-server | ||
322 | 378 | """ | ||
323 | 379 | content = [] | ||
324 | 380 | up = indent + "post-up route add" | ||
325 | 381 | down = indent + "pre-down route del" | ||
326 | 382 | or_true = " || true" | ||
327 | 366 | mapping = { | 383 | mapping = { |
328 | 367 | 'network': '-net', | 384 | 'network': '-net', |
329 | 368 | 'netmask': 'netmask', | 385 | 'netmask': 'netmask', |
330 | 369 | 'gateway': 'gw', | 386 | 'gateway': 'gw', |
331 | 370 | 'metric': 'metric', | 387 | 'metric': 'metric', |
332 | 371 | } | 388 | } |
342 | 372 | for k in ['network', 'netmask', 'gateway', 'metric']: | 389 | if route['network'] == '0.0.0.0' and route['netmask'] == '0.0.0.0': |
343 | 373 | if k in route: | 390 | default_gw = " default gw %s" % route['gateway'] |
344 | 374 | content += " %s %s" % (mapping[k], route[k]) | 391 | content.append(up + default_gw + or_true) |
345 | 375 | 392 | content.append(down + default_gw + or_true) | |
346 | 376 | content += '\n' | 393 | elif route['network'] == '::' and route['netmask'] == 0: |
347 | 377 | return content | 394 | # ipv6! |
348 | 378 | 395 | default_gw = " -A inet6 default gw %s" % route['gateway'] | |
349 | 379 | 396 | content.append(up + default_gw + or_true) | |
350 | 380 | def iface_start_entry(iface, index): | 397 | content.append(down + default_gw + or_true) |
351 | 398 | else: | ||
352 | 399 | route_line = "" | ||
353 | 400 | for k in ['network', 'netmask', 'gateway', 'metric']: | ||
354 | 401 | if k in route: | ||
355 | 402 | route_line += " %s %s" % (mapping[k], route[k]) | ||
356 | 403 | content.append(up + route_line + or_true) | ||
357 | 404 | content.append(down + route_line + or_true) | ||
358 | 405 | return "\n".join(content) | ||
359 | 406 | |||
360 | 407 | |||
361 | 408 | def iface_start_entry(iface): | ||
362 | 381 | fullname = iface['name'] | 409 | fullname = iface['name'] |
363 | 382 | if index != 0: | ||
364 | 383 | fullname += ":%s" % index | ||
365 | 384 | 410 | ||
366 | 385 | control = iface['control'] | 411 | control = iface['control'] |
367 | 386 | if control == "auto": | 412 | if control == "auto": |
368 | @@ -397,6 +423,16 @@ | |||
369 | 397 | "iface {fullname} {inet} {mode}\n").format(**subst) | 423 | "iface {fullname} {inet} {mode}\n").format(**subst) |
370 | 398 | 424 | ||
371 | 399 | 425 | ||
372 | 426 | def subnet_is_ipv6(subnet): | ||
373 | 427 | # 'static6' or 'dhcp6' | ||
374 | 428 | if subnet['type'].endswith('6'): | ||
375 | 429 | # This is a request for DHCPv6. | ||
376 | 430 | return True | ||
377 | 431 | elif subnet['type'] == 'static' and ":" in subnet['address']: | ||
378 | 432 | return True | ||
379 | 433 | return False | ||
380 | 434 | |||
381 | 435 | |||
382 | 400 | def render_interfaces(network_state): | 436 | def render_interfaces(network_state): |
383 | 401 | ''' Given state, emit etc/network/interfaces content ''' | 437 | ''' Given state, emit etc/network/interfaces content ''' |
384 | 402 | 438 | ||
385 | @@ -424,42 +460,43 @@ | |||
386 | 424 | content += "\n" | 460 | content += "\n" |
387 | 425 | subnets = iface.get('subnets', {}) | 461 | subnets = iface.get('subnets', {}) |
388 | 426 | if subnets: | 462 | if subnets: |
390 | 427 | for index, subnet in zip(range(0, len(subnets)), subnets): | 463 | for index, subnet in enumerate(subnets): |
391 | 428 | if content[-2:] != "\n\n": | 464 | if content[-2:] != "\n\n": |
392 | 429 | content += "\n" | 465 | content += "\n" |
393 | 430 | iface['index'] = index | 466 | iface['index'] = index |
394 | 431 | iface['mode'] = subnet['type'] | 467 | iface['mode'] = subnet['type'] |
395 | 432 | iface['control'] = subnet.get('control', 'auto') | 468 | iface['control'] = subnet.get('control', 'auto') |
396 | 433 | subnet_inet = 'inet' | 469 | subnet_inet = 'inet' |
402 | 434 | if iface['mode'].endswith('6'): | 470 | if subnet_is_ipv6(subnet): |
398 | 435 | # This is a request for DHCPv6. | ||
399 | 436 | subnet_inet += '6' | ||
400 | 437 | elif iface['mode'] == 'static' and ":" in subnet['address']: | ||
401 | 438 | # This is a static IPv6 address. | ||
403 | 439 | subnet_inet += '6' | 471 | subnet_inet += '6' |
404 | 440 | iface['inet'] = subnet_inet | 472 | iface['inet'] = subnet_inet |
406 | 441 | if iface['mode'].startswith('dhcp'): | 473 | if subnet['type'].startswith('dhcp'): |
407 | 442 | iface['mode'] = 'dhcp' | 474 | iface['mode'] = 'dhcp' |
408 | 443 | 475 | ||
410 | 444 | content += iface_start_entry(iface, index) | 476 | # do not emit multiple 'auto $IFACE' lines as older (precise) |
411 | 477 | # ifupdown complains | ||
412 | 478 | if "auto %s\n" % (iface['name']) in content: | ||
413 | 479 | iface['control'] = 'alias' | ||
414 | 480 | |||
415 | 481 | content += iface_start_entry(iface) | ||
416 | 445 | content += iface_add_subnet(iface, subnet) | 482 | content += iface_add_subnet(iface, subnet) |
417 | 446 | content += iface_add_attrs(iface, index) | 483 | content += iface_add_attrs(iface, index) |
422 | 447 | if len(subnets) > 1 and index == 0: | 484 | |
423 | 448 | for i in range(1, len(subnets)): | 485 | for route in subnet.get('routes', []): |
424 | 449 | content += " post-up ifup %s:%s\n" % (iface['name'], | 486 | content += render_route(route, indent=" ") + '\n' |
425 | 450 | i) | 487 | |
426 | 451 | else: | 488 | else: |
427 | 452 | # ifenslave docs say to auto the slave devices | 489 | # ifenslave docs say to auto the slave devices |
429 | 453 | if 'bond-master' in iface: | 490 | if 'bond-master' in iface or 'bond-slaves' in iface: |
430 | 454 | content += "auto {name}\n".format(**iface) | 491 | content += "auto {name}\n".format(**iface) |
431 | 455 | content += "iface {name} {inet} {mode}\n".format(**iface) | 492 | content += "iface {name} {inet} {mode}\n".format(**iface) |
433 | 456 | content += iface_add_attrs(iface, index) | 493 | content += iface_add_attrs(iface, 0) |
434 | 457 | 494 | ||
435 | 458 | for route in network_state.get('routes'): | 495 | for route in network_state.get('routes'): |
436 | 459 | content += render_route(route) | 496 | content += render_route(route) |
437 | 460 | 497 | ||
438 | 461 | # global replacements until v2 format | 498 | # global replacements until v2 format |
440 | 462 | content = content.replace('mac_address', 'hwaddress') | 499 | content = content.replace('mac_address', 'hwaddress ether') |
441 | 463 | 500 | ||
442 | 464 | # Play nice with others and source eni config files | 501 | # Play nice with others and source eni config files |
443 | 465 | content += "\nsource /etc/network/interfaces.d/*.cfg\n" | 502 | content += "\nsource /etc/network/interfaces.d/*.cfg\n" |
444 | 466 | 503 | ||
445 | === modified file 'curtin/net/network_state.py' | |||
446 | --- curtin/net/network_state.py 2015-09-18 18:12:02 +0000 | |||
447 | +++ curtin/net/network_state.py 2016-08-29 20:01:45 +0000 | |||
448 | @@ -121,6 +121,18 @@ | |||
449 | 121 | iface = interfaces.get(command['name'], {}) | 121 | iface = interfaces.get(command['name'], {}) |
450 | 122 | for param, val in command.get('params', {}).items(): | 122 | for param, val in command.get('params', {}).items(): |
451 | 123 | iface.update({param: val}) | 123 | iface.update({param: val}) |
452 | 124 | |||
453 | 125 | # convert subnet ipv6 netmask to cidr as needed | ||
454 | 126 | subnets = command.get('subnets') | ||
455 | 127 | if subnets: | ||
456 | 128 | for subnet in subnets: | ||
457 | 129 | if subnet['type'] == 'static': | ||
458 | 130 | if 'netmask' in subnet and ':' in subnet['address']: | ||
459 | 131 | subnet['netmask'] = mask2cidr(subnet['netmask']) | ||
460 | 132 | for route in subnet.get('routes', []): | ||
461 | 133 | if 'netmask' in route: | ||
462 | 134 | route['netmask'] = mask2cidr(route['netmask']) | ||
463 | 135 | |||
464 | 124 | iface.update({ | 136 | iface.update({ |
465 | 125 | 'name': command.get('name'), | 137 | 'name': command.get('name'), |
466 | 126 | 'type': command.get('type'), | 138 | 'type': command.get('type'), |
467 | @@ -130,7 +142,7 @@ | |||
468 | 130 | 'mtu': command.get('mtu'), | 142 | 'mtu': command.get('mtu'), |
469 | 131 | 'address': None, | 143 | 'address': None, |
470 | 132 | 'gateway': None, | 144 | 'gateway': None, |
472 | 133 | 'subnets': command.get('subnets'), | 145 | 'subnets': subnets, |
473 | 134 | }) | 146 | }) |
474 | 135 | self.network_state['interfaces'].update({command.get('name'): iface}) | 147 | self.network_state['interfaces'].update({command.get('name'): iface}) |
475 | 136 | self.dump_network_state() | 148 | self.dump_network_state() |
476 | @@ -141,6 +153,7 @@ | |||
477 | 141 | iface eth0.222 inet static | 153 | iface eth0.222 inet static |
478 | 142 | address 10.10.10.1 | 154 | address 10.10.10.1 |
479 | 143 | netmask 255.255.255.0 | 155 | netmask 255.255.255.0 |
480 | 156 | hwaddress ether BC:76:4E:06:96:B3 | ||
481 | 144 | vlan-raw-device eth0 | 157 | vlan-raw-device eth0 |
482 | 145 | ''' | 158 | ''' |
483 | 146 | required_keys = [ | 159 | required_keys = [ |
484 | @@ -332,6 +345,37 @@ | |||
485 | 332 | return ".".join([str(x) for x in mask]) | 345 | return ".".join([str(x) for x in mask]) |
486 | 333 | 346 | ||
487 | 334 | 347 | ||
488 | 348 | def ipv4mask2cidr(mask): | ||
489 | 349 | if '.' not in mask: | ||
490 | 350 | return mask | ||
491 | 351 | return sum([bin(int(x)).count('1') for x in mask.split('.')]) | ||
492 | 352 | |||
493 | 353 | |||
494 | 354 | def ipv6mask2cidr(mask): | ||
495 | 355 | if ':' not in mask: | ||
496 | 356 | return mask | ||
497 | 357 | |||
498 | 358 | bitCount = [0, 0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, | ||
499 | 359 | 0xff00, 0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, | ||
500 | 360 | 0xfffe, 0xffff] | ||
501 | 361 | cidr = 0 | ||
502 | 362 | for word in mask.split(':'): | ||
503 | 363 | if not word or int(word, 16) == 0: | ||
504 | 364 | break | ||
505 | 365 | cidr += bitCount.index(int(word, 16)) | ||
506 | 366 | |||
507 | 367 | return cidr | ||
508 | 368 | |||
509 | 369 | |||
510 | 370 | def mask2cidr(mask): | ||
511 | 371 | if ':' in mask: | ||
512 | 372 | return ipv6mask2cidr(mask) | ||
513 | 373 | elif '.' in mask: | ||
514 | 374 | return ipv4mask2cidr(mask) | ||
515 | 375 | else: | ||
516 | 376 | return mask | ||
517 | 377 | |||
518 | 378 | |||
519 | 335 | if __name__ == '__main__': | 379 | if __name__ == '__main__': |
520 | 336 | import sys | 380 | import sys |
521 | 337 | import random | 381 | import random |
522 | 338 | 382 | ||
523 | === added file 'examples/network-ipv6-bond-vlan.yaml' | |||
524 | --- examples/network-ipv6-bond-vlan.yaml 1970-01-01 00:00:00 +0000 | |||
525 | +++ examples/network-ipv6-bond-vlan.yaml 2016-08-29 20:01:45 +0000 | |||
526 | @@ -0,0 +1,56 @@ | |||
527 | 1 | network: | ||
528 | 2 | version: 1 | ||
529 | 3 | config: | ||
530 | 4 | - name: interface0 | ||
531 | 5 | type: physical | ||
532 | 6 | mac_address: BC:76:4E:06:96:B3 | ||
533 | 7 | - name: interface1 | ||
534 | 8 | type: physical | ||
535 | 9 | mac_address: BC:76:4E:04:88:41 | ||
536 | 10 | - type: bond | ||
537 | 11 | bond_interfaces: | ||
538 | 12 | - interface0 | ||
539 | 13 | - interface1 | ||
540 | 14 | name: bond0 | ||
541 | 15 | params: | ||
542 | 16 | bond_miimon: 100 | ||
543 | 17 | bond_mode: 802.3ad | ||
544 | 18 | bond_xmit_hash_policy: layer3+4 | ||
545 | 19 | - type: vlan | ||
546 | 20 | name: bond0.108 | ||
547 | 21 | vlan_id: '108' | ||
548 | 22 | vlan_link: bond0 | ||
549 | 23 | subnets: | ||
550 | 24 | - type: static | ||
551 | 25 | address: 65.61.151.38 | ||
552 | 26 | netmask: 255.255.255.252 | ||
553 | 27 | routes: | ||
554 | 28 | - gateway: 65.61.151.37 | ||
555 | 29 | netmask: 0.0.0.0 | ||
556 | 30 | network: 0.0.0.0 | ||
557 | 31 | - type: static | ||
558 | 32 | address: 2001:4800:78ff:1b:be76:4eff:fe06:96b3 | ||
559 | 33 | netmask: 'ffff:ffff:ffff:ffff::' | ||
560 | 34 | routes: | ||
561 | 35 | - gateway: 2001:4800:78ff:1b::1 | ||
562 | 36 | netmask: '::' | ||
563 | 37 | network: '::' | ||
564 | 38 | - type: vlan | ||
565 | 39 | name: bond0.208 | ||
566 | 40 | vlan_id: '208' | ||
567 | 41 | vlan_link: bond0 | ||
568 | 42 | subnets: | ||
569 | 43 | - address: 10.184.225.122 | ||
570 | 44 | netmask: 255.255.255.252 | ||
571 | 45 | type: static | ||
572 | 46 | routes: | ||
573 | 47 | - gateway: 10.184.225.121 | ||
574 | 48 | netmask: 255.240.0.0 | ||
575 | 49 | network: 10.176.0.0 | ||
576 | 50 | - gateway: 10.184.225.121 | ||
577 | 51 | netmask: 255.240.0.0 | ||
578 | 52 | network: 10.208.0.0 | ||
579 | 53 | - type: nameserver | ||
580 | 54 | address: 72.3.128.240 | ||
581 | 55 | - type: nameserver | ||
582 | 56 | address: 72.3.128.241 | ||
583 | 0 | 57 | ||
584 | === added file 'examples/tests/basic_network_static_ipv6.yaml' | |||
585 | --- examples/tests/basic_network_static_ipv6.yaml 1970-01-01 00:00:00 +0000 | |||
586 | +++ examples/tests/basic_network_static_ipv6.yaml 2016-08-29 20:01:45 +0000 | |||
587 | @@ -0,0 +1,22 @@ | |||
588 | 1 | showtrace: true | ||
589 | 2 | network: | ||
590 | 3 | version: 1 | ||
591 | 4 | config: | ||
592 | 5 | # Physical interfaces. | ||
593 | 6 | - type: physical | ||
594 | 7 | name: interface0 | ||
595 | 8 | mac_address: "52:54:00:12:34:00" | ||
596 | 9 | subnets: | ||
597 | 10 | - type: static | ||
598 | 11 | address: 2001:4800:78ff:1b:be76:4eff:fe06:96b3 | ||
599 | 12 | netmask: 'ffff:ffff:ffff:ffff::' | ||
600 | 13 | routes: | ||
601 | 14 | - gateway: 2001:4800:78ff:1b::1 | ||
602 | 15 | netmask: '::' | ||
603 | 16 | network: '::' | ||
604 | 17 | - type: nameserver | ||
605 | 18 | address: | ||
606 | 19 | - 10.0.2.3 | ||
607 | 20 | search: | ||
608 | 21 | - wark.maas | ||
609 | 22 | - foobar.maas | ||
610 | 0 | 23 | ||
611 | === added file 'examples/tests/network_alias.yaml' | |||
612 | --- examples/tests/network_alias.yaml 1970-01-01 00:00:00 +0000 | |||
613 | +++ examples/tests/network_alias.yaml 2016-08-29 20:01:45 +0000 | |||
614 | @@ -0,0 +1,125 @@ | |||
615 | 1 | showtrace: true | ||
616 | 2 | network: | ||
617 | 3 | version: 1 | ||
618 | 4 | config: | ||
619 | 5 | # no-alias: single v4 and v6 on same interface | ||
620 | 6 | - type: physical | ||
621 | 7 | name: interface0 | ||
622 | 8 | mac_address: "52:54:00:12:34:00" | ||
623 | 9 | subnets: | ||
624 | 10 | - type: static | ||
625 | 11 | address: 192.168.1.2/24 | ||
626 | 12 | mtu: 1501 | ||
627 | 13 | - type: static | ||
628 | 14 | address: 2001:4800:78ff:1b:be76:4eff:fe06:ffac | ||
629 | 15 | netmask: 'ffff:ffff:ffff:ffff::' | ||
630 | 16 | mtu: 1480 | ||
631 | 17 | # multi_v4_alias: multiple v4 addrs on same interface | ||
632 | 18 | - type: physical | ||
633 | 19 | name: interface1 | ||
634 | 20 | mac_address: "52:54:00:12:34:02" | ||
635 | 21 | subnets: | ||
636 | 22 | - type: static | ||
637 | 23 | address: 192.168.2.2/22 | ||
638 | 24 | routes: | ||
639 | 25 | - network: 192.168.0.0 | ||
640 | 26 | netmask: 255.255.252.0 | ||
641 | 27 | gateway: 192.168.2.1 | ||
642 | 28 | - type: static | ||
643 | 29 | address: 10.23.23.7/23 | ||
644 | 30 | routes: | ||
645 | 31 | - gateway: 10.23.23.1 | ||
646 | 32 | netmask: 255.255.254.0 | ||
647 | 33 | network: 10.23.22.0 | ||
648 | 34 | # multi_v6_alias: multiple v6 addrs on same interface | ||
649 | 35 | - type: physical | ||
650 | 36 | name: interface2 | ||
651 | 37 | mac_address: "52:54:00:12:34:04" | ||
652 | 38 | subnets: | ||
653 | 39 | - type: static | ||
654 | 40 | address: 2001:4800:78ff:1b:be76:4eff:dead:1000 | ||
655 | 41 | netmask: 'ffff:ffff:ffff:ffff::' | ||
656 | 42 | - type: static | ||
657 | 43 | address: 2001:4800:78ff:1b:be76:4eff:dead:2000 | ||
658 | 44 | netmask: 'ffff:ffff:ffff:ffff::' | ||
659 | 45 | - type: static | ||
660 | 46 | address: 2001:4800:78ff:1b:be76:4eff:dead:3000 | ||
661 | 47 | netmask: 'ffff:ffff:ffff:ffff::' | ||
662 | 48 | # multi_v4_and_v6_alias: multiple v4 and v6 addrs on same interface | ||
663 | 49 | - type: physical | ||
664 | 50 | name: interface3 | ||
665 | 51 | mac_address: "52:54:00:12:34:06" | ||
666 | 52 | subnets: | ||
667 | 53 | - type: static | ||
668 | 54 | address: 192.168.7.7/22 | ||
669 | 55 | routes: | ||
670 | 56 | - network: 192.168.0.0 | ||
671 | 57 | netmask: 255.255.252.0 | ||
672 | 58 | gateway: 192.168.7.1 | ||
673 | 59 | - type: static | ||
674 | 60 | address: 10.99.99.23/23 | ||
675 | 61 | routes: | ||
676 | 62 | - gateway: 10.99.99.1 | ||
677 | 63 | netmask: 255.255.254.0 | ||
678 | 64 | network: 10.99.98.0 | ||
679 | 65 | - type: static | ||
680 | 66 | address: 2001:4800:78ff:1b:be76:4eff:beef:4000 | ||
681 | 67 | netmask: 'ffff:ffff:ffff:ffff::' | ||
682 | 68 | - type: static | ||
683 | 69 | address: 2001:4800:78ff:1b:be76:4eff:beef:5000 | ||
684 | 70 | netmask: 'ffff:ffff:ffff:ffff::' | ||
685 | 71 | - type: static | ||
686 | 72 | address: 2001:4800:78ff:1b:be76:4eff:beef:6000 | ||
687 | 73 | netmask: 'ffff:ffff:ffff:ffff::' | ||
688 | 74 | # multi_v6_and_v4_revorder_alias: multiple v4 and v6 addr, rev order | ||
689 | 75 | - type: physical | ||
690 | 76 | name: interface4 | ||
691 | 77 | mac_address: "52:54:00:12:34:08" | ||
692 | 78 | subnets: | ||
693 | 79 | - type: static | ||
694 | 80 | address: 2001:4800:78ff:1b:be76:4eff:debe:7000 | ||
695 | 81 | netmask: 'ffff:ffff:ffff:ffff::' | ||
696 | 82 | - type: static | ||
697 | 83 | address: 2001:4800:78ff:1b:be76:4eff:debe:8000 | ||
698 | 84 | netmask: 'ffff:ffff:ffff:ffff::' | ||
699 | 85 | - type: static | ||
700 | 86 | address: 2001:4800:78ff:1b:be76:4eff:debe:9000 | ||
701 | 87 | netmask: 'ffff:ffff:ffff:ffff::' | ||
702 | 88 | - type: static | ||
703 | 89 | address: 192.168.100.100/22 | ||
704 | 90 | routes: | ||
705 | 91 | - network: 192.168.0.0 | ||
706 | 92 | netmask: 255.255.252.0 | ||
707 | 93 | gateway: 192.168.100.1 | ||
708 | 94 | - type: static | ||
709 | 95 | address: 10.17.142.2/23 | ||
710 | 96 | routes: | ||
711 | 97 | - gateway: 10.17.142.1 | ||
712 | 98 | netmask: 255.255.254.0 | ||
713 | 99 | network: 10.17.142.0 | ||
714 | 100 | # multi_v6_and_v4_mix_order: multiple v4 and v6 addr, mixed order | ||
715 | 101 | - type: physical | ||
716 | 102 | name: interface5 | ||
717 | 103 | mac_address: "52:54:00:12:34:0a" | ||
718 | 104 | subnets: | ||
719 | 105 | - type: static | ||
720 | 106 | address: 2001:4800:78ff:1b:be76:4eff:baaf:a000 | ||
721 | 107 | netmask: 'ffff:ffff:ffff:ffff::' | ||
722 | 108 | - type: static | ||
723 | 109 | address: 2001:4800:78ff:1b:be76:4eff:baaf:c000 | ||
724 | 110 | netmask: 'ffff:ffff:ffff:ffff::' | ||
725 | 111 | - type: static | ||
726 | 112 | address: 192.168.200.200/22 | ||
727 | 113 | routes: | ||
728 | 114 | - network: 192.168.0.0 | ||
729 | 115 | netmask: 255.255.252.0 | ||
730 | 116 | gateway: 192.168.200.1 | ||
731 | 117 | - type: static | ||
732 | 118 | address: 10.252.2.2/23 | ||
733 | 119 | routes: | ||
734 | 120 | - gateway: 10.252.2.1 | ||
735 | 121 | netmask: 255.255.254.0 | ||
736 | 122 | network: 10.252.2.0 | ||
737 | 123 | - type: static | ||
738 | 124 | address: 2001:4800:78ff:1b:be76:4eff:baaf:b000 | ||
739 | 125 | netmask: 'ffff:ffff:ffff:ffff::' | ||
740 | 0 | 126 | ||
741 | === added file 'examples/tests/network_mtu.yaml' | |||
742 | --- examples/tests/network_mtu.yaml 1970-01-01 00:00:00 +0000 | |||
743 | +++ examples/tests/network_mtu.yaml 2016-08-29 20:01:45 +0000 | |||
744 | @@ -0,0 +1,88 @@ | |||
745 | 1 | showtrace: true | ||
746 | 2 | network: | ||
747 | 3 | version: 1 | ||
748 | 4 | config: | ||
749 | 5 | - type: physical | ||
750 | 6 | name: interface0 | ||
751 | 7 | mac_address: "52:54:00:12:34:00" | ||
752 | 8 | subnets: | ||
753 | 9 | - type: static | ||
754 | 10 | address: 192.168.1.2/24 | ||
755 | 11 | mtu: 1501 | ||
756 | 12 | - type: static | ||
757 | 13 | address: 2001:4800:78ff:1b:be76:4eff:fe06:1000 | ||
758 | 14 | netmask: 'ffff:ffff:ffff:ffff::' | ||
759 | 15 | mtu: 1480 | ||
760 | 16 | - type: physical | ||
761 | 17 | name: interface1 | ||
762 | 18 | mac_address: "52:54:00:12:34:02" | ||
763 | 19 | subnets: | ||
764 | 20 | - type: static | ||
765 | 21 | address: 192.168.2.2/24 | ||
766 | 22 | mtu: 1501 | ||
767 | 23 | - type: static | ||
768 | 24 | address: 2001:4800:78ff:1b:be76:4eff:fe06:2000 | ||
769 | 25 | netmask: 'ffff:ffff:ffff:ffff::' | ||
770 | 26 | mtu: 1501 | ||
771 | 27 | - type: physical | ||
772 | 28 | name: interface2 | ||
773 | 29 | mac_address: "52:54:00:12:34:04" | ||
774 | 30 | subnets: | ||
775 | 31 | - type: static | ||
776 | 32 | address: 192.168.3.2/24 | ||
777 | 33 | - type: static | ||
778 | 34 | address: 2001:4800:78ff:1b:be76:4eff:fe06:3000 | ||
779 | 35 | netmask: 'ffff:ffff:ffff:ffff::' | ||
780 | 36 | mtu: 1501 | ||
781 | 37 | - type: physical | ||
782 | 38 | name: interface3 | ||
783 | 39 | mac_address: "52:54:00:12:34:06" | ||
784 | 40 | subnets: | ||
785 | 41 | - type: manual | ||
786 | 42 | control: manual | ||
787 | 43 | - type: static | ||
788 | 44 | address: 2001:4800:78ff:1b:be76:4eff:fe06:4000 | ||
789 | 45 | netmask: 'ffff:ffff:ffff:ffff::' | ||
790 | 46 | mtu: 9000 | ||
791 | 47 | - type: physical | ||
792 | 48 | name: interface4 | ||
793 | 49 | mac_address: "52:54:00:12:34:08" | ||
794 | 50 | subnets: | ||
795 | 51 | - type: static | ||
796 | 52 | address: 2001:4800:78ff:1b:be76:4eff:fe06:5000 | ||
797 | 53 | netmask: 'ffff:ffff:ffff:ffff::' | ||
798 | 54 | mtu: 1480 | ||
799 | 55 | - type: static | ||
800 | 56 | address: 192.168.5.2/24 | ||
801 | 57 | mtu: 1501 | ||
802 | 58 | - type: physical | ||
803 | 59 | name: interface5 | ||
804 | 60 | mac_address: "52:54:00:12:34:0c" | ||
805 | 61 | subnets: | ||
806 | 62 | - type: static | ||
807 | 63 | address: 2001:4800:78ff:1b:be76:4eff:fe06:6000 | ||
808 | 64 | netmask: 'ffff:ffff:ffff:ffff::' | ||
809 | 65 | mtu: 1501 | ||
810 | 66 | - type: static | ||
811 | 67 | address: 192.168.6.2/24 | ||
812 | 68 | mtu: 1501 | ||
813 | 69 | - type: physical | ||
814 | 70 | name: interface6 | ||
815 | 71 | mac_address: "52:54:00:12:34:0e" | ||
816 | 72 | subnets: | ||
817 | 73 | - type: static | ||
818 | 74 | address: 2001:4800:78ff:1b:be76:4eff:fe06:7000 | ||
819 | 75 | netmask: 'ffff:ffff:ffff:ffff::' | ||
820 | 76 | mtu: 1501 | ||
821 | 77 | - type: static | ||
822 | 78 | address: 192.168.7.2/24 | ||
823 | 79 | - type: physical | ||
824 | 80 | name: interface7 | ||
825 | 81 | mac_address: "52:54:00:12:35:01" | ||
826 | 82 | subnets: | ||
827 | 83 | - type: static | ||
828 | 84 | address: 2001:4800:78ff:1b:be76:4eff:fe06:8000 | ||
829 | 85 | netmask: 'ffff:ffff:ffff:ffff::' | ||
830 | 86 | mtu: 9000 | ||
831 | 87 | - type: manual | ||
832 | 88 | control: manual | ||
833 | 0 | 89 | ||
834 | === added file 'examples/tests/network_source_ipv6.yaml' | |||
835 | --- examples/tests/network_source_ipv6.yaml 1970-01-01 00:00:00 +0000 | |||
836 | +++ examples/tests/network_source_ipv6.yaml 2016-08-29 20:01:45 +0000 | |||
837 | @@ -0,0 +1,31 @@ | |||
838 | 1 | showtrace: true | ||
839 | 2 | network: | ||
840 | 3 | version: 1 | ||
841 | 4 | config: | ||
842 | 5 | # Physical interfaces. | ||
843 | 6 | - type: physical | ||
844 | 7 | name: interface0 | ||
845 | 8 | mac_address: "52:54:00:12:34:00" | ||
846 | 9 | subnets: | ||
847 | 10 | - type: static | ||
848 | 11 | address: 2001:4800:78ff:1b:be76:4eff:fe06:96b3 | ||
849 | 12 | netmask: 'ffff:ffff:ffff:ffff::' | ||
850 | 13 | routes: | ||
851 | 14 | - gateway: 2001:4800:78ff:1b::1 | ||
852 | 15 | netmask: '::' | ||
853 | 16 | network: '::' | ||
854 | 17 | - type: physical | ||
855 | 18 | name: interface2 | ||
856 | 19 | mac_address: "52:54:00:12:34:04" | ||
857 | 20 | - type: nameserver | ||
858 | 21 | address: | ||
859 | 22 | - 10.0.2.3 | ||
860 | 23 | search: | ||
861 | 24 | - wark.maas | ||
862 | 25 | - foobar.maas | ||
863 | 26 | |||
864 | 27 | curthooks_commands: | ||
865 | 28 | # use curtin to inject a eni config file outside of the network yaml | ||
866 | 29 | # this allows us to test user installed configurations outside of | ||
867 | 30 | # curtin's control | ||
868 | 31 | aa_cleanup: ['curtin', 'in-target', '--', 'sh', '-c', "rm -f /etc/network/interfaces.d/eth0.cfg; /bin/echo -e 'auto interface2\niface interface2 inet static\n address 192.168.23.23/24\n' > /etc/network/interfaces.d/interface2.cfg"] | ||
869 | 0 | 32 | ||
870 | === added file 'examples/tests/vlan_network_ipv6.yaml' | |||
871 | --- examples/tests/vlan_network_ipv6.yaml 1970-01-01 00:00:00 +0000 | |||
872 | +++ examples/tests/vlan_network_ipv6.yaml 2016-08-29 20:01:45 +0000 | |||
873 | @@ -0,0 +1,92 @@ | |||
874 | 1 | network: | ||
875 | 2 | config: | ||
876 | 3 | - id: interface0 | ||
877 | 4 | mac_address: d4:be:d9:a8:49:13 | ||
878 | 5 | mtu: 1500 | ||
879 | 6 | name: interface0 | ||
880 | 7 | subnets: | ||
881 | 8 | - address: 2001:4800:78ff:1b:be76:4eff:fe06:96b3 | ||
882 | 9 | netmask: 'ffff:ffff:ffff:ffff::' | ||
883 | 10 | dns_nameservers: | ||
884 | 11 | - 10.245.168.2 | ||
885 | 12 | routes: | ||
886 | 13 | - gateway: 2001:4800:78ff:1b::1 | ||
887 | 14 | netmask: '::' | ||
888 | 15 | network: '::' | ||
889 | 16 | type: static | ||
890 | 17 | type: physical | ||
891 | 18 | - id: interface1 | ||
892 | 19 | mac_address: d4:be:d9:a8:49:15 | ||
893 | 20 | mtu: 1500 | ||
894 | 21 | name: interface1 | ||
895 | 22 | subnets: | ||
896 | 23 | - address: 2001:4800:beef:1b:be76:4eff:fe06:97b0 | ||
897 | 24 | netmask: 'ffff:ffff:ffff:ffff::' | ||
898 | 25 | dns_nameservers: [] | ||
899 | 26 | type: static | ||
900 | 27 | type: physical | ||
901 | 28 | - id: interface2 | ||
902 | 29 | mac_address: d4:be:d9:a8:49:17 | ||
903 | 30 | mtu: 1500 | ||
904 | 31 | name: interface2 | ||
905 | 32 | subnets: | ||
906 | 33 | - type: manual | ||
907 | 34 | control: manual | ||
908 | 35 | type: physical | ||
909 | 36 | - id: interface3 | ||
910 | 37 | mac_address: d4:be:d9:a8:49:19 | ||
911 | 38 | mtu: 1500 | ||
912 | 39 | name: interface3 | ||
913 | 40 | subnets: | ||
914 | 41 | - type: manual | ||
915 | 42 | control: manual | ||
916 | 43 | type: physical | ||
917 | 44 | - id: interface1.2667 | ||
918 | 45 | mtu: 1500 | ||
919 | 46 | name: interface1.2667 | ||
920 | 47 | subnets: | ||
921 | 48 | - address: 2001:4800:dead:1b:be76:4eff:c486:12f7 | ||
922 | 49 | netmask: 'ffff:ffff:ffff:ffff::' | ||
923 | 50 | dns_nameservers: [] | ||
924 | 51 | type: static | ||
925 | 52 | type: vlan | ||
926 | 53 | vlan_id: 2667 | ||
927 | 54 | vlan_link: interface1 | ||
928 | 55 | - id: interface1.2668 | ||
929 | 56 | mtu: 1500 | ||
930 | 57 | name: interface1.2668 | ||
931 | 58 | subnets: | ||
932 | 59 | - address: 2001:4800:feef:1b:be76:4eff:4242:2323 | ||
933 | 60 | netmask: 'ffff:ffff:ffff:ffff::' | ||
934 | 61 | dns_nameservers: [] | ||
935 | 62 | type: static | ||
936 | 63 | type: vlan | ||
937 | 64 | vlan_id: 2668 | ||
938 | 65 | vlan_link: interface1 | ||
939 | 66 | - id: interface1.2669 | ||
940 | 67 | mtu: 1500 | ||
941 | 68 | name: interface1.2669 | ||
942 | 69 | subnets: | ||
943 | 70 | - address: 2001:4800:effe:1b:be76:7634:5f42:79ff | ||
944 | 71 | netmask: 'ffff:ffff:ffff:ffff::' | ||
945 | 72 | dns_nameservers: [] | ||
946 | 73 | type: static | ||
947 | 74 | type: vlan | ||
948 | 75 | vlan_id: 2669 | ||
949 | 76 | vlan_link: interface1 | ||
950 | 77 | - id: interface1.2670 | ||
951 | 78 | mtu: 1500 | ||
952 | 79 | name: interface1.2670 | ||
953 | 80 | subnets: | ||
954 | 81 | - address: 2001:4800:9eaf:1b:be76:7634:321f:bbca | ||
955 | 82 | netmask: 'ffff:ffff:ffff:ffff::' | ||
956 | 83 | dns_nameservers: [] | ||
957 | 84 | type: static | ||
958 | 85 | type: vlan | ||
959 | 86 | vlan_id: 2670 | ||
960 | 87 | vlan_link: interface1 | ||
961 | 88 | - address: 10.245.168.2 | ||
962 | 89 | search: | ||
963 | 90 | - dellstack | ||
964 | 91 | type: nameserver | ||
965 | 92 | version: 1 | ||
966 | 0 | 93 | ||
967 | === modified file 'tests/unittests/test_net.py' | |||
968 | --- tests/unittests/test_net.py 2016-06-16 17:43:17 +0000 | |||
969 | +++ tests/unittests/test_net.py 2016-08-29 20:01:45 +0000 | |||
970 | @@ -473,10 +473,9 @@ | |||
971 | 473 | 473 | ||
972 | 474 | auto eth0 | 474 | auto eth0 |
973 | 475 | iface eth0 inet dhcp | 475 | iface eth0 inet dhcp |
974 | 476 | post-up ifup eth0:1 | ||
975 | 477 | 476 | ||
978 | 478 | auto eth0:1 | 477 | # control-alias eth0 |
979 | 479 | iface eth0:1 inet static | 478 | iface eth0 inet static |
980 | 480 | address 192.168.21.3/24 | 479 | address 192.168.21.3/24 |
981 | 481 | dns-nameservers 8.8.8.8 8.8.4.4 | 480 | dns-nameservers 8.8.8.8 8.8.4.4 |
982 | 482 | dns-search barley.maas sach.maas | 481 | dns-search barley.maas sach.maas |
983 | @@ -515,12 +514,11 @@ | |||
984 | 515 | iface bond0 inet static | 514 | iface bond0 inet static |
985 | 516 | address 10.23.23.2/24 | 515 | address 10.23.23.2/24 |
986 | 517 | bond-mode active-backup | 516 | bond-mode active-backup |
988 | 518 | hwaddress 52:54:00:12:34:06 | 517 | hwaddress ether 52:54:00:12:34:06 |
989 | 519 | bond-slaves none | 518 | bond-slaves none |
990 | 520 | post-up ifup bond0:1 | ||
991 | 521 | 519 | ||
994 | 522 | auto bond0:1 | 520 | # control-alias bond0 |
995 | 523 | iface bond0:1 inet static | 521 | iface bond0 inet static |
996 | 524 | address 10.23.24.2/24 | 522 | address 10.23.24.2/24 |
997 | 525 | 523 | ||
998 | 526 | source /etc/network/interfaces.d/*.cfg | 524 | source /etc/network/interfaces.d/*.cfg |
999 | @@ -551,10 +549,9 @@ | |||
1000 | 551 | address 192.168.14.2/24 | 549 | address 192.168.14.2/24 |
1001 | 552 | gateway 192.168.14.1 | 550 | gateway 192.168.14.1 |
1002 | 553 | mtu 1492 | 551 | mtu 1492 |
1003 | 554 | post-up ifup interface1:1 | ||
1004 | 555 | 552 | ||
1007 | 556 | auto interface1:1 | 553 | # control-alias interface1 |
1008 | 557 | iface interface1:1 inet static | 554 | iface interface1 inet static |
1009 | 558 | address 192.168.14.4/24 | 555 | address 192.168.14.4/24 |
1010 | 559 | 556 | ||
1011 | 560 | allow-hotplug interface2 | 557 | allow-hotplug interface2 |
1012 | @@ -597,10 +594,9 @@ | |||
1013 | 597 | auto eth0 | 594 | auto eth0 |
1014 | 598 | iface eth0 inet6 static | 595 | iface eth0 inet6 static |
1015 | 599 | address fde9:8f83:4a81:1:0:1:0:6/64 | 596 | address fde9:8f83:4a81:1:0:1:0:6/64 |
1016 | 600 | post-up ifup eth0:1 | ||
1017 | 601 | 597 | ||
1020 | 602 | auto eth0:1 | 598 | # control-alias eth0 |
1021 | 603 | iface eth0:1 inet static | 599 | iface eth0 inet static |
1022 | 604 | address 192.168.0.1/24 | 600 | address 192.168.0.1/24 |
1023 | 605 | 601 | ||
1024 | 606 | source /etc/network/interfaces.d/*.cfg | 602 | source /etc/network/interfaces.d/*.cfg |
1025 | @@ -613,5 +609,50 @@ | |||
1026 | 613 | self.assertEqual(sorted(ifaces.split('\n')), | 609 | self.assertEqual(sorted(ifaces.split('\n')), |
1027 | 614 | sorted(net_ifaces.split('\n'))) | 610 | sorted(net_ifaces.split('\n'))) |
1028 | 615 | 611 | ||
1029 | 612 | def test_render_interfaces_ipv4_multiple_alias(self): | ||
1030 | 613 | network_yaml = ''' | ||
1031 | 614 | network: | ||
1032 | 615 | version: 1 | ||
1033 | 616 | config: | ||
1034 | 617 | # multi_v4_alias: multiple v4 addrs on same interface | ||
1035 | 618 | - type: physical | ||
1036 | 619 | name: interface1 | ||
1037 | 620 | mac_address: "52:54:00:12:34:02" | ||
1038 | 621 | subnets: | ||
1039 | 622 | - type: dhcp | ||
1040 | 623 | - type: static | ||
1041 | 624 | address: 192.168.2.2/22 | ||
1042 | 625 | gateway: 192.168.2.1 | ||
1043 | 626 | - type: static | ||
1044 | 627 | address: 10.23.23.7/23 | ||
1045 | 628 | gateway: 10.23.23.1 | ||
1046 | 629 | ''' | ||
1047 | 630 | |||
1048 | 631 | ns = self.get_net_state(network_yaml) | ||
1049 | 632 | ifaces = dedent("""\ | ||
1050 | 633 | auto lo | ||
1051 | 634 | iface lo inet loopback | ||
1052 | 635 | |||
1053 | 636 | auto interface1 | ||
1054 | 637 | iface interface1 inet dhcp | ||
1055 | 638 | |||
1056 | 639 | # control-alias interface1 | ||
1057 | 640 | iface interface1 inet static | ||
1058 | 641 | address 192.168.2.2/22 | ||
1059 | 642 | gateway 192.168.2.1 | ||
1060 | 643 | |||
1061 | 644 | # control-alias interface1 | ||
1062 | 645 | iface interface1 inet static | ||
1063 | 646 | address 10.23.23.7/23 | ||
1064 | 647 | gateway 10.23.23.1 | ||
1065 | 648 | |||
1066 | 649 | source /etc/network/interfaces.d/*.cfg | ||
1067 | 650 | """) | ||
1068 | 651 | net_ifaces = net.render_interfaces(ns.network_state) | ||
1069 | 652 | print(net_ifaces) | ||
1070 | 653 | print(ifaces) | ||
1071 | 654 | self.assertEqual(sorted(ifaces.split('\n')), | ||
1072 | 655 | sorted(net_ifaces.split('\n'))) | ||
1073 | 656 | |||
1074 | 616 | 657 | ||
1075 | 617 | # vi: ts=4 expandtab syntax=python | 658 | # vi: ts=4 expandtab syntax=python |
1076 | 618 | 659 | ||
1077 | === modified file 'tests/vmtests/helpers.py' | |||
1078 | --- tests/vmtests/helpers.py 2016-06-16 18:45:05 +0000 | |||
1079 | +++ tests/vmtests/helpers.py 2016-08-29 20:01:45 +0000 | |||
1080 | @@ -119,170 +119,133 @@ | |||
1081 | 119 | return sorted(releases) | 119 | return sorted(releases) |
1082 | 120 | 120 | ||
1083 | 121 | 121 | ||
1235 | 122 | def _parse_ifconfig_xenial(ifconfig_out): | 122 | def _parse_ip_a(ip_a): |
1236 | 123 | """Parse ifconfig output from xenial or earlier and return a dictionary. | 123 | """ |
1237 | 124 | given content like below, return: | 124 | 2: interface0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc pfifo_fast\ |
1238 | 125 | {'eth0': {'address': '10.8.1.78', 'broadcast': '10.8.1.255', | 125 | state UP group default qlen 1000 |
1239 | 126 | 'inet6': [{'address': 'fe80::216:3eff:fe63:c05d', | 126 | link/ether 52:54:00:12:34:00 brd ff:ff:ff:ff:ff:ff |
1240 | 127 | 'prefixlen': '64', 'scope': 'Link'}, | 127 | inet 192.168.1.2/24 brd 192.168.1.255 scope global interface0 |
1241 | 128 | {'address': 'fdec:2922:2f07:0:216:3eff:fe63:c05d', | 128 | valid_lft forever preferred_lft forever |
1242 | 129 | 'prefixlen': '64', 'scope': 'Global'}], | 129 | inet6 2001:4800:78ff:1b:be76:4eff:fe06:1000/64 scope global |
1243 | 130 | 'interface': 'eth0', 'link_encap': 'Ethernet', | 130 | valid_lft forever preferred_lft forever |
1244 | 131 | 'mac_address': '00:16:3e:63:c0:5d', 'mtu': 1500, | 131 | inet6 fe80::5054:ff:fe12:3400/64 scope link |
1245 | 132 | 'multicast': True, 'netmask': '255.255.255.0', | 132 | valid_lft forever preferred_lft forever |
1246 | 133 | 'running': True, 'up': True}} | 133 | """ |
1247 | 134 | 134 | ifaces = {} | |
1248 | 135 | eth0 Link encap:Ethernet HWaddr 00:16:3e:63:c0:5d | 135 | combined_fields = { |
1249 | 136 | inet addr:10.8.1.78 Bcast:10.8.1.255 Mask:255.255.255.0 | 136 | 'brd': 'broadcast', |
1250 | 137 | inet6 addr: fe80::216:3eff:fe63:c05d/64 Scope:Link | 137 | 'link/ether': 'mac_address', |
1251 | 138 | inet6 addr: fdec:2922:2f07:0:216:3eff:fe63:c05d/64 Scope:Global | 138 | } |
1252 | 139 | UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 | 139 | interface_fields = [ |
1253 | 140 | RX packets:21503 errors:0 dropped:0 overruns:0 frame:0 | 140 | 'group', |
1254 | 141 | TX packets:11346 errors:0 dropped:0 overruns:0 carrier:0 | 141 | 'master', |
1255 | 142 | collisions:0 txqueuelen:1000 | 142 | 'mtu', |
1256 | 143 | RX bytes:31556357 (31.5 MB) TX bytes:870943 (870.9 KB) | 143 | 'qdisc', |
1257 | 144 | """ | 144 | 'qlen', |
1258 | 145 | ifaces = {} | 145 | 'state', |
1259 | 146 | combined_fields = {'addr': 'address', 'Bcast': 'broadcast', | 146 | ] |
1260 | 147 | 'Mask': 'netmask', 'MTU': 'mtu', | 147 | inet_fields = [ |
1261 | 148 | 'encap': 'link_encap'} | 148 | 'valid_lft', |
1262 | 149 | boolmap = {'RUNNING': 'running', 'UP': 'up', 'MULTICAST': 'multicast'} | 149 | 'preferred_left' |
1263 | 150 | 150 | ] | |
1264 | 151 | for line in ifconfig_out.splitlines(): | 151 | boolmap = { |
1265 | 152 | if not line: | 152 | 'BROADCAST': 'broadcast', |
1266 | 153 | continue | 153 | 'LOOPBACK': 'loopback', |
1267 | 154 | if not line.startswith(" "): | 154 | 'LOWER_UP': 'lower_up', |
1268 | 155 | cur_iface = line.split()[0].rstrip(":") | 155 | 'MULTICAST': 'multicast', |
1269 | 156 | cur_data = {'inet6': [], 'interface': cur_iface} | 156 | 'RUNNING': 'running', |
1270 | 157 | for t in boolmap.values(): | 157 | 'UP': 'up', |
1271 | 158 | cur_data[t] = False | 158 | } |
1272 | 159 | ifaces[cur_iface] = cur_data | 159 | |
1273 | 160 | 160 | for line in ip_a.splitlines(): | |
1274 | 161 | toks = line.split() | 161 | if not line: |
1275 | 162 | 162 | continue | |
1276 | 163 | if toks[0] == "inet6": | 163 | |
1277 | 164 | cidr = toks[2] | 164 | toks = line.split() |
1278 | 165 | address, prefixlen = cidr.split("/") | 165 | if not line.startswith(" "): |
1279 | 166 | scope = toks[3].split(":")[1] | 166 | cur_iface = line.split()[1].rstrip(":") |
1280 | 167 | cur_ipv6 = {'address': address, 'scope': scope, | 167 | cur_data = { |
1281 | 168 | 'prefixlen': prefixlen} | 168 | 'inet4': [], |
1282 | 169 | cur_data['inet6'].append(cur_ipv6) | 169 | 'inet6': [], |
1283 | 170 | continue | 170 | 'interface': cur_iface |
1284 | 171 | 171 | } | |
1285 | 172 | for i in range(0, len(toks)): | 172 | # vlan's get a fancy name <iface name>@<vlan_link> |
1286 | 173 | cur_tok = toks[i] | 173 | if '@' in cur_iface: |
1287 | 174 | try: | 174 | cur_iface, vlan_link = cur_iface.split("@") |
1288 | 175 | next_tok = toks[i+1] | 175 | cur_data.update({'interface': cur_iface, |
1289 | 176 | except IndexError: | 176 | 'vlan_link': vlan_link}) |
1290 | 177 | next_tok = None | 177 | for t in boolmap.values(): |
1291 | 178 | 178 | # <BROADCAST,MULTICAST,UP,LOWER_UP> | |
1292 | 179 | if cur_tok == "HWaddr": | 179 | cur_data[t] = t.upper() in line[2] |
1293 | 180 | cur_data['mac_address'] = next_tok | 180 | ifaces[cur_iface] = cur_data |
1294 | 181 | elif ":" in cur_tok: | 181 | |
1295 | 182 | key, _colon, val = cur_tok.partition(":") | 182 | for i in range(0, len(toks)): |
1296 | 183 | if key in combined_fields: | 183 | cur_tok = toks[i] |
1297 | 184 | cur_data[combined_fields[key]] = val | 184 | try: |
1298 | 185 | elif cur_tok in boolmap: | 185 | next_tok = toks[i+1] |
1299 | 186 | cur_data[boolmap[cur_tok]] = True | 186 | except IndexError: |
1300 | 187 | 187 | next_tok = None | |
1301 | 188 | if 'mtu' in cur_data: | 188 | |
1302 | 189 | cur_data['mtu'] = int(cur_data['mtu']) | 189 | # parse link/ether, brd |
1303 | 190 | 190 | if cur_tok in combined_fields.keys(): | |
1304 | 191 | return ifaces | 191 | cur_data[combined_fields[cur_tok]] = next_tok |
1305 | 192 | 192 | # mtu an other interface line key/value pairs | |
1306 | 193 | 193 | elif cur_tok in interface_fields: | |
1307 | 194 | def _parse_ifconfig_yakkety(ifconfig_out): | 194 | cur_data[cur_tok] = next_tok |
1308 | 195 | """Parse ifconfig output from yakkety or later(?) and return a dictionary. | 195 | elif cur_tok.startswith("inet"): |
1309 | 196 | 196 | cidr = toks[1] | |
1310 | 197 | given ifconfig output like below, return: | 197 | address, prefixlen = cidr.split("/") |
1311 | 198 | {'ens2': {'address': '10.5.0.78', | 198 | cur_ip = { |
1312 | 199 | 'broadcast': '10.5.255.255', | 199 | 'address': address, |
1313 | 200 | 'broadcast_flag': True, | 200 | 'prefixlen': prefixlen, |
1314 | 201 | 'inet6': [{'address': 'fe80::f816:3eff:fe05:9673', | 201 | } |
1315 | 202 | 'prefixlen': '64', 'scopeid': '0x20<link>'}, | 202 | if ":" in address: |
1316 | 203 | {'address': 'fe80::f816:3eff:fe05:9673', | 203 | cur_ipv6 = cur_ip.copy() |
1317 | 204 | 'prefixlen': '64', 'scopeid': '0x20<link>'}], | 204 | cur_ipv6.update({'scope': toks[3]}) |
1318 | 205 | 'interface': 'ens2', 'link_encap': 'Ethernet', | 205 | cur_data['inet6'].append(cur_ipv6) |
1319 | 206 | 'mac_address': 'fa:16:3e:05:96:73', 'mtu': 1500, | 206 | else: |
1320 | 207 | 'multicast': True, 'netmask': '255.255.0.0', | 207 | cur_ipv4 = cur_ip.copy() |
1321 | 208 | 'running': True, 'up': True}} | 208 | if len(toks) > 5: |
1322 | 209 | 209 | cur_ipv4.update({'scope': toks[5]}) | |
1323 | 210 | ens2: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 | 210 | else: |
1324 | 211 | inet 10.5.0.78 netmask 255.255.0.0 broadcast 10.5.255.255 | 211 | cur_ipv4.update({'scope': toks[3]}) |
1325 | 212 | inet6 fe80::f816:3eff:fe05:9673 prefixlen 64 scopeid 0x20<link> | 212 | cur_data['inet4'].append(cur_ipv4) |
1326 | 213 | inet6 fe80::f816:3eff:fe05:9673 prefixlen 64 scopeid 0x20<link> | 213 | |
1327 | 214 | ether fa:16:3e:05:96:73 txqueuelen 1000 (Ethernet) | 214 | continue |
1328 | 215 | RX packets 33196 bytes 48916947 (48.9 MB) | 215 | elif cur_tok in inet_fields: |
1329 | 216 | RX errors 0 dropped 0 overruns 0 frame 0 | 216 | if ":" in address: |
1330 | 217 | TX packets 5458 bytes 411486 (411.4 KB) | 217 | cur_ipv6[cur_tok] = next_tok |
1331 | 218 | TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 | 218 | else: |
1332 | 219 | """ | 219 | cur_ipv4[cur_tok] = next_tok |
1333 | 220 | fmap = {'mtu': 'mtu', 'inet': 'address', | 220 | continue |
1334 | 221 | 'netmask': 'netmask', 'broadcast': 'broadcast', | 221 | |
1335 | 222 | 'ether': 'mac_address'} | 222 | return ifaces |
1336 | 223 | boolmap = {'RUNNING': 'running', 'UP': 'up', 'MULTICAST': 'multicast', | 223 | |
1337 | 224 | 'BROADCAST': 'broadcast_flag'} | 224 | |
1338 | 225 | 225 | def ip_a_to_dict(ip_a): | |
1188 | 226 | ifaces = {} | ||
1189 | 227 | for line in ifconfig_out.splitlines(): | ||
1190 | 228 | if not line: | ||
1191 | 229 | continue | ||
1192 | 230 | if not line.startswith(" "): | ||
1193 | 231 | cur_iface = line.split()[0].rstrip(":") | ||
1194 | 232 | cur_data = {'inet6': [], 'interface': cur_iface} | ||
1195 | 233 | for t in boolmap.values(): | ||
1196 | 234 | cur_data[t] = False | ||
1197 | 235 | ifaces[cur_iface] = cur_data | ||
1198 | 236 | |||
1199 | 237 | toks = line.split() | ||
1200 | 238 | if toks[0] == "inet6": | ||
1201 | 239 | cur_ipv6 = {'address': toks[1]} | ||
1202 | 240 | cur_data['inet6'].append(cur_ipv6) | ||
1203 | 241 | |||
1204 | 242 | for i in range(0, len(toks)): | ||
1205 | 243 | cur_tok = toks[i] | ||
1206 | 244 | try: | ||
1207 | 245 | next_tok = toks[i+1] | ||
1208 | 246 | except IndexError: | ||
1209 | 247 | next_tok = None | ||
1210 | 248 | if cur_tok in fmap: | ||
1211 | 249 | cur_data[fmap[cur_tok]] = next_tok | ||
1212 | 250 | elif cur_tok in ('prefixlen', 'scopeid'): | ||
1213 | 251 | cur_ipv6[cur_tok] = next_tok | ||
1214 | 252 | cur_data['inet6'].append | ||
1215 | 253 | elif cur_tok.startswith("flags="): | ||
1216 | 254 | # flags=4163<UP,BROADCAST,RUNNING,MULTICAST> | ||
1217 | 255 | flags = cur_tok[cur_tok.find("<") + 1: | ||
1218 | 256 | cur_tok.rfind(">")].split(",") | ||
1219 | 257 | for flag in flags: | ||
1220 | 258 | if flag in boolmap: | ||
1221 | 259 | cur_data[boolmap[flag]] = True | ||
1222 | 260 | elif cur_tok == "(Ethernet)": | ||
1223 | 261 | cur_data['link_encap'] = 'Ethernet' | ||
1224 | 262 | |||
1225 | 263 | if 'mtu' in cur_data: | ||
1226 | 264 | cur_data['mtu'] = int(cur_data['mtu']) | ||
1227 | 265 | |||
1228 | 266 | return ifaces | ||
1229 | 267 | |||
1230 | 268 | |||
1231 | 269 | def ifconfig_to_dict(ifconfig_a): | ||
1232 | 270 | # if the first token of the first line ends in a ':' then assume yakkety | ||
1233 | 271 | # parse ifconfig output and return a dictionary. | ||
1234 | 272 | # | ||
1339 | 273 | # return a dictionary of network information like: | 226 | # return a dictionary of network information like: |
1355 | 274 | # {'ens2': {'address': '10.5.0.78', 'broadcast': '10.5.255.255', | 227 | # {'interface0': {'broadcast': '10.0.2.255', |
1356 | 275 | # 'broadcast_flag': True, | 228 | # 'group': 'default', |
1357 | 276 | # 'inet6': [{'address': 'fe80::f816:3eff:fe05:9673', | 229 | # 'inet4': [{'address': '10.0.2.15', |
1358 | 277 | # 'prefixlen': '64', 'scopeid': '0x20<link>'}, | 230 | # 'preferred_lft': 'forever', |
1359 | 278 | # {'address': 'fe80::f816:3eff:fe05:9673', | 231 | # 'prefixlen': '24', |
1360 | 279 | # 'prefixlen': '64', 'scopeid': '0x20<link>'}], | 232 | # 'scope': 'global', |
1361 | 280 | # 'interface': 'ens2', 'link_encap': 'Ethernet', | 233 | # 'valid_lft': 'forever'}], |
1362 | 281 | # 'mac_address': 'fa:16:3e:05:96:73', 'mtu': 1500, | 234 | # 'inet6': [{'address': 'fe80::5054:ff:fe12:3400', |
1363 | 282 | # 'multicast': True, 'netmask': '255.255.0.0', | 235 | # 'preferred_lft': 'forever', |
1364 | 283 | # 'running': True, 'up': True}} | 236 | # 'prefixlen': '64', |
1365 | 284 | line = ifconfig_a.lstrip().splitlines()[0] | 237 | # 'scope': 'link', |
1366 | 285 | if line.split()[0].endswith(":"): | 238 | # 'valid_lft': 'forever'}], |
1367 | 286 | return _parse_ifconfig_yakkety(ifconfig_a) | 239 | # 'interface': 'interface0', |
1368 | 287 | else: | 240 | # 'loopback': False, |
1369 | 288 | return _parse_ifconfig_xenial(ifconfig_a) | 241 | # 'lower_up': False, |
1370 | 242 | # 'mac_address': '52:54:00:12:34:00', | ||
1371 | 243 | # 'mtu': '1500', | ||
1372 | 244 | # 'multicast': False, | ||
1373 | 245 | # 'qdisc': 'pfifo_fast', | ||
1374 | 246 | # 'qlen': '1000', | ||
1375 | 247 | # 'running': False, | ||
1376 | 248 | # 'state': 'UP', | ||
1377 | 249 | # 'up': False}, | ||
1378 | 250 | # from iproute2 `ip a` command output | ||
1379 | 251 | return _parse_ip_a(ip_a) | ||
1380 | 289 | 252 | ||
1381 | === modified file 'tests/vmtests/test_basic.py' | |||
1382 | --- tests/vmtests/test_basic.py 2016-08-09 15:01:07 +0000 | |||
1383 | +++ tests/vmtests/test_basic.py 2016-08-29 20:01:45 +0000 | |||
1384 | @@ -3,7 +3,6 @@ | |||
1385 | 3 | get_apt_proxy) | 3 | get_apt_proxy) |
1386 | 4 | from .releases import base_vm_classes as relbase | 4 | from .releases import base_vm_classes as relbase |
1387 | 5 | 5 | ||
1388 | 6 | import os | ||
1389 | 7 | import re | 6 | import re |
1390 | 8 | import textwrap | 7 | import textwrap |
1391 | 9 | 8 | ||
1392 | @@ -47,21 +46,17 @@ | |||
1393 | 47 | def test_partition_numbers(self): | 46 | def test_partition_numbers(self): |
1394 | 48 | # vde should have partitions 1 and 10 | 47 | # vde should have partitions 1 and 10 |
1395 | 49 | disk = "vde" | 48 | disk = "vde" |
1396 | 50 | proc_partitions_path = os.path.join(self.td.collect, | ||
1397 | 51 | 'proc_partitions') | ||
1398 | 52 | self.assertTrue(os.path.exists(proc_partitions_path)) | ||
1399 | 53 | found = [] | 49 | found = [] |
1404 | 54 | with open(proc_partitions_path, 'r') as fp: | 50 | proc_partitions = self.load_collect_file('proc_partitions') |
1405 | 55 | for line in fp.readlines(): | 51 | for line in proc_partitions.splitlines(): |
1406 | 56 | if disk in line: | 52 | if disk in line: |
1407 | 57 | found.append(line.split()[3]) | 53 | found.append(line.split()[3]) |
1408 | 58 | # /proc/partitions should have 3 lines with 'vde' in them. | 54 | # /proc/partitions should have 3 lines with 'vde' in them. |
1409 | 59 | expected = [disk + s for s in ["", "1", "10"]] | 55 | expected = [disk + s for s in ["", "1", "10"]] |
1410 | 60 | self.assertEqual(found, expected) | 56 | self.assertEqual(found, expected) |
1411 | 61 | 57 | ||
1412 | 62 | def test_partitions(self): | 58 | def test_partitions(self): |
1415 | 63 | with open(os.path.join(self.td.collect, "fstab")) as fp: | 59 | fstab_lines = self.load_collect_file('fstab').splitlines() |
1414 | 64 | fstab_lines = fp.readlines() | ||
1416 | 65 | print("\n".join(fstab_lines)) | 60 | print("\n".join(fstab_lines)) |
1417 | 66 | # Test that vda1 is on / | 61 | # Test that vda1 is on / |
1418 | 67 | blkid_info = self.get_blkid_data("blkid_output_vda1") | 62 | blkid_info = self.get_blkid_data("blkid_output_vda1") |
1419 | @@ -94,12 +89,8 @@ | |||
1420 | 94 | 89 | ||
1421 | 95 | def test_whole_disk_format(self): | 90 | def test_whole_disk_format(self): |
1422 | 96 | # confirm the whole disk format is the expected device | 91 | # confirm the whole disk format is the expected device |
1429 | 97 | with open(os.path.join(self.td.collect, | 92 | btrfs_show_super = self.load_collect_file('btrfs_show_super_vdd') |
1430 | 98 | "btrfs_show_super_vdd"), "r") as fp: | 93 | ls_uuid = self.load_collect_file("ls_uuid") |
1425 | 99 | btrfs_show_super = fp.read() | ||
1426 | 100 | |||
1427 | 101 | with open(os.path.join(self.td.collect, "ls_uuid"), "r") as fp: | ||
1428 | 102 | ls_uuid = fp.read() | ||
1431 | 103 | 94 | ||
1432 | 104 | # extract uuid from btrfs superblock | 95 | # extract uuid from btrfs superblock |
1433 | 105 | btrfs_fsid = [line for line in btrfs_show_super.split('\n') | 96 | btrfs_fsid = [line for line in btrfs_show_super.split('\n') |
1434 | @@ -123,8 +114,7 @@ | |||
1435 | 123 | 114 | ||
1436 | 124 | def test_proxy_set(self): | 115 | def test_proxy_set(self): |
1437 | 125 | expected = get_apt_proxy() | 116 | expected = get_apt_proxy() |
1440 | 126 | with open(os.path.join(self.td.collect, "apt-proxy")) as fp: | 117 | apt_proxy_found = self.load_collect_file("apt-proxy").rstrip() |
1439 | 127 | apt_proxy_found = fp.read().rstrip() | ||
1441 | 128 | if expected: | 118 | if expected: |
1442 | 129 | # the proxy should have gotten set through | 119 | # the proxy should have gotten set through |
1443 | 130 | self.assertIn(expected, apt_proxy_found) | 120 | self.assertIn(expected, apt_proxy_found) |
1444 | @@ -157,12 +147,8 @@ | |||
1445 | 157 | 147 | ||
1446 | 158 | def test_whole_disk_format(self): | 148 | def test_whole_disk_format(self): |
1447 | 159 | # confirm the whole disk format is the expected device | 149 | # confirm the whole disk format is the expected device |
1454 | 160 | with open(os.path.join(self.td.collect, | 150 | btrfs_show_super = self.load_collect_file("btrfs_show_super_vdd") |
1455 | 161 | "btrfs_show_super_vdd"), "r") as fp: | 151 | ls_uuid = self.load_collect_file("ls_uuid") |
1450 | 162 | btrfs_show_super = fp.read() | ||
1451 | 163 | |||
1452 | 164 | with open(os.path.join(self.td.collect, "ls_uuid"), "r") as fp: | ||
1453 | 165 | ls_uuid = fp.read() | ||
1456 | 166 | 152 | ||
1457 | 167 | # extract uuid from btrfs superblock | 153 | # extract uuid from btrfs superblock |
1458 | 168 | btrfs_fsid = re.findall('.*uuid:\ (.*)\n', btrfs_show_super) | 154 | btrfs_fsid = re.findall('.*uuid:\ (.*)\n', btrfs_show_super) |
1459 | @@ -275,21 +261,17 @@ | |||
1460 | 275 | def test_partition_numbers(self): | 261 | def test_partition_numbers(self): |
1461 | 276 | # vde should have partitions 1 and 10 | 262 | # vde should have partitions 1 and 10 |
1462 | 277 | disk = "sdd" | 263 | disk = "sdd" |
1463 | 278 | proc_partitions_path = os.path.join(self.td.collect, | ||
1464 | 279 | 'proc_partitions') | ||
1465 | 280 | self.assertTrue(os.path.exists(proc_partitions_path)) | ||
1466 | 281 | found = [] | 264 | found = [] |
1471 | 282 | with open(proc_partitions_path, 'r') as fp: | 265 | proc_partitions = self.load_collect_file('proc_partitions') |
1472 | 283 | for line in fp.readlines(): | 266 | for line in proc_partitions.splitlines(): |
1473 | 284 | if disk in line: | 267 | if disk in line: |
1474 | 285 | found.append(line.split()[3]) | 268 | found.append(line.split()[3]) |
1475 | 286 | # /proc/partitions should have 3 lines with 'vde' in them. | 269 | # /proc/partitions should have 3 lines with 'vde' in them. |
1476 | 287 | expected = [disk + s for s in ["", "1", "10"]] | 270 | expected = [disk + s for s in ["", "1", "10"]] |
1477 | 288 | self.assertEqual(found, expected) | 271 | self.assertEqual(found, expected) |
1478 | 289 | 272 | ||
1479 | 290 | def test_partitions(self): | 273 | def test_partitions(self): |
1482 | 291 | with open(os.path.join(self.td.collect, "fstab")) as fp: | 274 | fstab_lines = self.load_collect_file('fstab').splitlines() |
1481 | 292 | fstab_lines = fp.readlines() | ||
1483 | 293 | print("\n".join(fstab_lines)) | 275 | print("\n".join(fstab_lines)) |
1484 | 294 | # Test that vda1 is on / | 276 | # Test that vda1 is on / |
1485 | 295 | blkid_info = self.get_blkid_data("blkid_output_sda1") | 277 | blkid_info = self.get_blkid_data("blkid_output_sda1") |
1486 | @@ -322,12 +304,8 @@ | |||
1487 | 322 | 304 | ||
1488 | 323 | def test_whole_disk_format(self): | 305 | def test_whole_disk_format(self): |
1489 | 324 | # confirm the whole disk format is the expected device | 306 | # confirm the whole disk format is the expected device |
1496 | 325 | with open(os.path.join(self.td.collect, | 307 | btrfs_show_super = self.load_collect_file("btrfs_show_super_sdc") |
1497 | 326 | "btrfs_show_super_sdc"), "r") as fp: | 308 | ls_uuid = self.load_collect_file("ls_uuid") |
1492 | 327 | btrfs_show_super = fp.read() | ||
1493 | 328 | |||
1494 | 329 | with open(os.path.join(self.td.collect, "ls_uuid"), "r") as fp: | ||
1495 | 330 | ls_uuid = fp.read() | ||
1498 | 331 | 309 | ||
1499 | 332 | # extract uuid from btrfs superblock | 310 | # extract uuid from btrfs superblock |
1500 | 333 | btrfs_fsid = [line for line in btrfs_show_super.split('\n') | 311 | btrfs_fsid = [line for line in btrfs_show_super.split('\n') |
1501 | 334 | 312 | ||
1502 | === modified file 'tests/vmtests/test_bcache_basic.py' | |||
1503 | --- tests/vmtests/test_bcache_basic.py 2016-06-13 20:49:15 +0000 | |||
1504 | +++ tests/vmtests/test_bcache_basic.py 2016-08-29 20:01:45 +0000 | |||
1505 | @@ -2,7 +2,6 @@ | |||
1506 | 2 | from .releases import base_vm_classes as relbase | 2 | from .releases import base_vm_classes as relbase |
1507 | 3 | 3 | ||
1508 | 4 | import textwrap | 4 | import textwrap |
1509 | 5 | import os | ||
1510 | 6 | 5 | ||
1511 | 7 | 6 | ||
1512 | 8 | class TestBcacheBasic(VMBaseClass): | 7 | class TestBcacheBasic(VMBaseClass): |
1513 | @@ -27,14 +26,12 @@ | |||
1514 | 27 | 26 | ||
1515 | 28 | def test_bcache_status(self): | 27 | def test_bcache_status(self): |
1516 | 29 | bcache_cset_uuid = None | 28 | bcache_cset_uuid = None |
1522 | 30 | fname = os.path.join(self.td.collect, "bcache_super_vda2") | 29 | for line in self.load_collect_file("bcache_super_vda2").splitlines(): |
1523 | 31 | with open(fname, "r") as fp: | 30 | if line != "" and line.split()[0] == "cset.uuid": |
1524 | 32 | for line in fp.read().splitlines(): | 31 | bcache_cset_uuid = line.split()[-1].rstrip() |
1520 | 33 | if line != "" and line.split()[0] == "cset.uuid": | ||
1521 | 34 | bcache_cset_uuid = line.split()[-1].rstrip() | ||
1525 | 35 | self.assertIsNotNone(bcache_cset_uuid) | 32 | self.assertIsNotNone(bcache_cset_uuid) |
1528 | 36 | with open(os.path.join(self.td.collect, "bcache_ls"), "r") as fp: | 33 | self.assertTrue(bcache_cset_uuid in |
1529 | 37 | self.assertTrue(bcache_cset_uuid in fp.read().splitlines()) | 34 | self.load_collect_file("bcache_ls").splitlines()) |
1530 | 38 | 35 | ||
1531 | 39 | def test_bcache_cachemode(self): | 36 | def test_bcache_cachemode(self): |
1532 | 40 | self.check_file_regex("bcache_cache_mode", r"\[writeback\]") | 37 | self.check_file_regex("bcache_cache_mode", r"\[writeback\]") |
1533 | 41 | 38 | ||
1534 | === removed file 'tests/vmtests/test_bonding.py' | |||
1535 | --- tests/vmtests/test_bonding.py 2016-08-09 15:01:07 +0000 | |||
1536 | +++ tests/vmtests/test_bonding.py 1970-01-01 00:00:00 +0000 | |||
1537 | @@ -1,205 +0,0 @@ | |||
1538 | 1 | from . import VMBaseClass, logger, helpers | ||
1539 | 2 | from .releases import base_vm_classes as relbase | ||
1540 | 3 | |||
1541 | 4 | import ipaddress | ||
1542 | 5 | import os | ||
1543 | 6 | import re | ||
1544 | 7 | import textwrap | ||
1545 | 8 | import yaml | ||
1546 | 9 | |||
1547 | 10 | |||
1548 | 11 | class TestNetworkAbs(VMBaseClass): | ||
1549 | 12 | interactive = False | ||
1550 | 13 | conf_file = "examples/tests/bonding_network.yaml" | ||
1551 | 14 | extra_disks = [] | ||
1552 | 15 | extra_nics = [] | ||
1553 | 16 | collect_scripts = [textwrap.dedent(""" | ||
1554 | 17 | cd OUTPUT_COLLECT_D | ||
1555 | 18 | ifconfig -a > ifconfig_a | ||
1556 | 19 | cp -av /etc/network/interfaces . | ||
1557 | 20 | cp -av /etc/udev/rules.d/70-persistent-net.rules . | ||
1558 | 21 | ip -o route show > ip_route_show | ||
1559 | 22 | route -n > route_n | ||
1560 | 23 | dpkg-query -W -f '${Status}' ifenslave > ifenslave_installed | ||
1561 | 24 | find /etc/network/interfaces.d > find_interfacesd | ||
1562 | 25 | """)] | ||
1563 | 26 | |||
1564 | 27 | def test_output_files_exist(self): | ||
1565 | 28 | self.output_files_exist(["ifconfig_a", | ||
1566 | 29 | "interfaces", | ||
1567 | 30 | "70-persistent-net.rules", | ||
1568 | 31 | "ip_route_show", | ||
1569 | 32 | "ifenslave_installed", | ||
1570 | 33 | "route_n"]) | ||
1571 | 34 | |||
1572 | 35 | def test_ifenslave_installed(self): | ||
1573 | 36 | with open(os.path.join(self.td.collect, "ifenslave_installed")) as fp: | ||
1574 | 37 | status = fp.read().strip() | ||
1575 | 38 | logger.debug('ifenslave installed: {}'.format(status)) | ||
1576 | 39 | self.assertEqual('install ok installed', status) | ||
1577 | 40 | |||
1578 | 41 | def test_etc_network_interfaces(self): | ||
1579 | 42 | with open(os.path.join(self.td.collect, "interfaces")) as fp: | ||
1580 | 43 | eni = fp.read() | ||
1581 | 44 | logger.debug('etc/network/interfaces:\n{}'.format(eni)) | ||
1582 | 45 | |||
1583 | 46 | expected_eni = self.get_expected_etc_network_interfaces() | ||
1584 | 47 | eni_lines = eni.split('\n') | ||
1585 | 48 | for line in expected_eni.split('\n'): | ||
1586 | 49 | self.assertTrue(line in eni_lines) | ||
1587 | 50 | |||
1588 | 51 | def test_ifconfig_output(self): | ||
1589 | 52 | '''check ifconfig output with test input''' | ||
1590 | 53 | network_state = self.get_network_state() | ||
1591 | 54 | logger.debug('expected_network_state:\n{}'.format( | ||
1592 | 55 | yaml.dump(network_state, default_flow_style=False, indent=4))) | ||
1593 | 56 | |||
1594 | 57 | with open(os.path.join(self.td.collect, "ifconfig_a")) as fp: | ||
1595 | 58 | ifconfig_a = fp.read() | ||
1596 | 59 | logger.debug('ifconfig -a:\n{}'.format(ifconfig_a)) | ||
1597 | 60 | |||
1598 | 61 | ifconfig_dict = helpers.ifconfig_to_dict(ifconfig_a) | ||
1599 | 62 | logger.debug('parsed ifcfg dict:\n{}'.format( | ||
1600 | 63 | yaml.dump(ifconfig_dict, default_flow_style=False, indent=4))) | ||
1601 | 64 | |||
1602 | 65 | with open(os.path.join(self.td.collect, "ip_route_show")) as fp: | ||
1603 | 66 | ip_route_show = fp.read() | ||
1604 | 67 | logger.debug("ip route show:\n{}".format(ip_route_show)) | ||
1605 | 68 | for line in [line for line in ip_route_show.split('\n') | ||
1606 | 69 | if 'src' in line]: | ||
1607 | 70 | m = re.search(r'^(?P<network>\S+)\sdev\s' + | ||
1608 | 71 | r'(?P<devname>\S+)\s+' + | ||
1609 | 72 | r'proto kernel\s+scope link' + | ||
1610 | 73 | r'\s+src\s(?P<src_ip>\S+)', | ||
1611 | 74 | line) | ||
1612 | 75 | route_info = m.groupdict('') | ||
1613 | 76 | logger.debug(route_info) | ||
1614 | 77 | |||
1615 | 78 | with open(os.path.join(self.td.collect, "route_n")) as fp: | ||
1616 | 79 | route_n = fp.read() | ||
1617 | 80 | logger.debug("route -n:\n{}".format(route_n)) | ||
1618 | 81 | |||
1619 | 82 | interfaces = network_state.get('interfaces') | ||
1620 | 83 | for iface in interfaces.values(): | ||
1621 | 84 | subnets = iface.get('subnets', {}) | ||
1622 | 85 | if subnets: | ||
1623 | 86 | for index, subnet in zip(range(0, len(subnets)), subnets): | ||
1624 | 87 | iface['index'] = index | ||
1625 | 88 | if index == 0: | ||
1626 | 89 | ifname = "{name}".format(**iface) | ||
1627 | 90 | else: | ||
1628 | 91 | ifname = "{name}:{index}".format(**iface) | ||
1629 | 92 | |||
1630 | 93 | self.check_interface(iface, | ||
1631 | 94 | ifconfig_dict.get(ifname), | ||
1632 | 95 | route_n) | ||
1633 | 96 | else: | ||
1634 | 97 | iface['index'] = 0 | ||
1635 | 98 | self.check_interface(iface, | ||
1636 | 99 | ifconfig_dict.get(iface['name']), | ||
1637 | 100 | route_n) | ||
1638 | 101 | |||
1639 | 102 | def check_interface(self, iface, ifconfig, route_n): | ||
1640 | 103 | logger.debug( | ||
1641 | 104 | 'testing iface:\n{}\n\nifconfig:\n{}'.format(iface, ifconfig)) | ||
1642 | 105 | subnets = iface.get('subnets', {}) | ||
1643 | 106 | if subnets and iface['index'] != 0: | ||
1644 | 107 | ifname = "{name}:{index}".format(**iface) | ||
1645 | 108 | else: | ||
1646 | 109 | ifname = "{name}".format(**iface) | ||
1647 | 110 | |||
1648 | 111 | # initial check, do we have the correct iface ? | ||
1649 | 112 | logger.debug('ifname={}'.format(ifname)) | ||
1650 | 113 | logger.debug("ifconfig['interface']={}".format(ifconfig['interface'])) | ||
1651 | 114 | self.assertEqual(ifname, ifconfig['interface']) | ||
1652 | 115 | |||
1653 | 116 | # check physical interface attributes | ||
1654 | 117 | # FIXME: can't check mac_addr under bonding since | ||
1655 | 118 | # the bond might change slave mac addrs | ||
1656 | 119 | for key in ['mtu']: | ||
1657 | 120 | if key in iface and iface[key]: | ||
1658 | 121 | self.assertEqual(iface[key], | ||
1659 | 122 | ifconfig[key]) | ||
1660 | 123 | |||
1661 | 124 | def __get_subnet(subnets, subidx): | ||
1662 | 125 | for index, subnet in zip(range(0, len(subnets)), subnets): | ||
1663 | 126 | if index == subidx: | ||
1664 | 127 | break | ||
1665 | 128 | return subnet | ||
1666 | 129 | |||
1667 | 130 | # check subnet related attributes, and specifically only | ||
1668 | 131 | # the subnet specified by iface['index'] | ||
1669 | 132 | subnets = iface.get('subnets', {}) | ||
1670 | 133 | if subnets: | ||
1671 | 134 | subnet = __get_subnet(subnets, iface['index']) | ||
1672 | 135 | if 'address' in subnet and subnet['address']: | ||
1673 | 136 | if ':' in subnet['address']: | ||
1674 | 137 | inet_iface = ipaddress.IPv6Interface( | ||
1675 | 138 | subnet['address']) | ||
1676 | 139 | else: | ||
1677 | 140 | inet_iface = ipaddress.IPv4Interface( | ||
1678 | 141 | subnet['address']) | ||
1679 | 142 | |||
1680 | 143 | # check ip addr | ||
1681 | 144 | self.assertEqual(str(inet_iface.ip), | ||
1682 | 145 | ifconfig['address']) | ||
1683 | 146 | |||
1684 | 147 | self.assertEqual(str(inet_iface.netmask), | ||
1685 | 148 | ifconfig['netmask']) | ||
1686 | 149 | |||
1687 | 150 | self.assertEqual( | ||
1688 | 151 | str(inet_iface.network.broadcast_address), | ||
1689 | 152 | ifconfig['broadcast']) | ||
1690 | 153 | |||
1691 | 154 | # handle gateway by looking at routing table | ||
1692 | 155 | if 'gateway' in subnet and subnet['gateway']: | ||
1693 | 156 | gw_ip = subnet['gateway'] | ||
1694 | 157 | gateways = [line for line in route_n.split('\n') | ||
1695 | 158 | if 'UG' in line and gw_ip in line] | ||
1696 | 159 | logger.debug('matching gateways:\n{}'.format(gateways)) | ||
1697 | 160 | self.assertEqual(len(gateways), 1) | ||
1698 | 161 | [gateways] = gateways | ||
1699 | 162 | (dest, gw, genmask, flags, metric, ref, use, iface) = \ | ||
1700 | 163 | gateways.split() | ||
1701 | 164 | logger.debug('expected gw:{} found gw:{}'.format(gw_ip, gw)) | ||
1702 | 165 | self.assertEqual(gw_ip, gw) | ||
1703 | 166 | |||
1704 | 167 | |||
1705 | 168 | class PreciseHWETTestBonding(relbase.precise_hwe_t, TestNetworkAbs): | ||
1706 | 169 | __test__ = True | ||
1707 | 170 | # package names on precise are different, need to check on ifenslave-2.6 | ||
1708 | 171 | collect_scripts = TestNetworkAbs.collect_scripts + [textwrap.dedent(""" | ||
1709 | 172 | cd OUTPUT_COLLECT_D | ||
1710 | 173 | dpkg-query -W -f '${Status}' ifenslave-2.6 > ifenslave_installed | ||
1711 | 174 | """)] | ||
1712 | 175 | |||
1713 | 176 | |||
1714 | 177 | class TrustyTestBonding(relbase.trusty, TestNetworkAbs): | ||
1715 | 178 | __test__ = False | ||
1716 | 179 | |||
1717 | 180 | |||
1718 | 181 | class TrustyHWEUTestBonding(relbase.trusty_hwe_u, TrustyTestBonding): | ||
1719 | 182 | __test__ = True | ||
1720 | 183 | |||
1721 | 184 | |||
1722 | 185 | class TrustyHWEVTestBonding(relbase.trusty_hwe_v, TrustyTestBonding): | ||
1723 | 186 | # Working, but off by default to safe test suite runtime | ||
1724 | 187 | # oldest/newest HWE-* covered above/below | ||
1725 | 188 | __test__ = False | ||
1726 | 189 | |||
1727 | 190 | |||
1728 | 191 | class TrustyHWEWTestBonding(relbase.trusty_hwe_w, TrustyTestBonding): | ||
1729 | 192 | __test__ = True | ||
1730 | 193 | |||
1731 | 194 | |||
1732 | 195 | class WilyTestBonding(relbase.wily, TestNetworkAbs): | ||
1733 | 196 | # EOL - 2016-07-28 | ||
1734 | 197 | __test__ = False | ||
1735 | 198 | |||
1736 | 199 | |||
1737 | 200 | class XenialTestBonding(relbase.xenial, TestNetworkAbs): | ||
1738 | 201 | __test__ = True | ||
1739 | 202 | |||
1740 | 203 | |||
1741 | 204 | class YakketyTestBonding(relbase.yakkety, TestNetworkAbs): | ||
1742 | 205 | __test__ = True | ||
1743 | 206 | 0 | ||
1744 | === modified file 'tests/vmtests/test_mdadm_bcache.py' | |||
1745 | --- tests/vmtests/test_mdadm_bcache.py 2016-08-09 15:01:07 +0000 | |||
1746 | +++ tests/vmtests/test_mdadm_bcache.py 2016-08-29 20:01:45 +0000 | |||
1747 | @@ -2,7 +2,6 @@ | |||
1748 | 2 | from .releases import base_vm_classes as relbase | 2 | from .releases import base_vm_classes as relbase |
1749 | 3 | 3 | ||
1750 | 4 | import textwrap | 4 | import textwrap |
1751 | 5 | import os | ||
1752 | 6 | 5 | ||
1753 | 7 | 6 | ||
1754 | 8 | class TestMdadmAbs(VMBaseClass): | 7 | class TestMdadmAbs(VMBaseClass): |
1755 | @@ -82,17 +81,16 @@ | |||
1756 | 82 | bcache_cset_uuid = None | 81 | bcache_cset_uuid = None |
1757 | 83 | found = {} | 82 | found = {} |
1758 | 84 | for bcache_super in bcache_supers: | 83 | for bcache_super in bcache_supers: |
1767 | 85 | with open(os.path.join(self.td.collect, bcache_super), "r") as fp: | 84 | for line in self.load_collect_file(bcache_super).splitlines(): |
1768 | 86 | for line in fp.read().splitlines(): | 85 | if line != "" and line.split()[0] == "cset.uuid": |
1769 | 87 | if line != "" and line.split()[0] == "cset.uuid": | 86 | bcache_cset_uuid = line.split()[-1].rstrip() |
1770 | 88 | bcache_cset_uuid = line.split()[-1].rstrip() | 87 | if bcache_cset_uuid in found: |
1771 | 89 | if bcache_cset_uuid in found: | 88 | found[bcache_cset_uuid].append(bcache_super) |
1772 | 90 | found[bcache_cset_uuid].append(bcache_super) | 89 | else: |
1773 | 91 | else: | 90 | found[bcache_cset_uuid] = [bcache_super] |
1766 | 92 | found[bcache_cset_uuid] = [bcache_super] | ||
1774 | 93 | self.assertIsNotNone(bcache_cset_uuid) | 91 | self.assertIsNotNone(bcache_cset_uuid) |
1777 | 94 | with open(os.path.join(self.td.collect, "bcache_ls"), "r") as fp: | 92 | self.assertTrue(bcache_cset_uuid in |
1778 | 95 | self.assertTrue(bcache_cset_uuid in fp.read().splitlines()) | 93 | self.load_collect_file("bcache_ls").splitlines()) |
1779 | 96 | 94 | ||
1780 | 97 | # one cset.uuid for all devices | 95 | # one cset.uuid for all devices |
1781 | 98 | self.assertEqual(len(found), 1) | 96 | self.assertEqual(len(found), 1) |
1782 | 99 | 97 | ||
1783 | === modified file 'tests/vmtests/test_multipath.py' | |||
1784 | --- tests/vmtests/test_multipath.py 2016-06-23 14:26:39 +0000 | |||
1785 | +++ tests/vmtests/test_multipath.py 2016-08-29 20:01:45 +0000 | |||
1786 | @@ -1,7 +1,6 @@ | |||
1787 | 1 | from . import VMBaseClass | 1 | from . import VMBaseClass |
1788 | 2 | from .releases import base_vm_classes as relbase | 2 | from .releases import base_vm_classes as relbase |
1789 | 3 | 3 | ||
1790 | 4 | import os | ||
1791 | 5 | import textwrap | 4 | import textwrap |
1792 | 6 | 5 | ||
1793 | 7 | 6 | ||
1794 | @@ -29,7 +28,7 @@ | |||
1795 | 29 | ls -al /dev/disk/by-uuid/ > ls_uuid | 28 | ls -al /dev/disk/by-uuid/ > ls_uuid |
1796 | 30 | ls -al /dev/disk/by-id/ > ls_disk_id | 29 | ls -al /dev/disk/by-id/ > ls_disk_id |
1797 | 31 | readlink -f /sys/class/block/sda/holders/dm-0 > holders_sda | 30 | readlink -f /sys/class/block/sda/holders/dm-0 > holders_sda |
1799 | 32 | readlink /sys/class/block/sdb/holders/dm-0 > holders_sdb | 31 | readlink -f /sys/class/block/sdb/holders/dm-0 > holders_sdb |
1800 | 33 | cat /etc/fstab > fstab | 32 | cat /etc/fstab > fstab |
1801 | 34 | mkdir -p /dev/disk/by-dname | 33 | mkdir -p /dev/disk/by-dname |
1802 | 35 | ls /dev/disk/by-dname/ > ls_dname | 34 | ls /dev/disk/by-dname/ > ls_dname |
1803 | @@ -37,17 +36,10 @@ | |||
1804 | 37 | """)] | 36 | """)] |
1805 | 38 | 37 | ||
1806 | 39 | def test_multipath_disks_match(self): | 38 | def test_multipath_disks_match(self): |
1818 | 40 | sda = os.path.join(self.td.collect, 'holders_sda') | 39 | sda_data = self.load_collect_file("holders_sda") |
1819 | 41 | sdb = os.path.join(self.td.collect, 'holders_sdb') | 40 | print('sda holders:\n%s' % sda_data) |
1820 | 42 | self.assertTrue(os.path.exists(sda)) | 41 | sdb_data = self.load_collect_file("holders_sdb") |
1821 | 43 | self.assertTrue(os.path.exists(sdb)) | 42 | print('sdb holders:\n%s' % sdb_data) |
1811 | 44 | with open(sda, 'r') as fp: | ||
1812 | 45 | sda_data = fp.read() | ||
1813 | 46 | print('sda holders:\n%s' % sda_data) | ||
1814 | 47 | with open(sda, 'r') as fp: | ||
1815 | 48 | sdb_data = fp.read() | ||
1816 | 49 | print('sdb holders:\n%s' % sda_data) | ||
1817 | 50 | |||
1822 | 51 | self.assertEqual(sda_data, sdb_data) | 43 | self.assertEqual(sda_data, sdb_data) |
1823 | 52 | 44 | ||
1824 | 53 | 45 | ||
1825 | 54 | 46 | ||
1826 | === modified file 'tests/vmtests/test_network.py' | |||
1827 | --- tests/vmtests/test_network.py 2016-08-09 15:01:07 +0000 | |||
1828 | +++ tests/vmtests/test_network.py 2016-08-29 20:01:45 +0000 | |||
1829 | @@ -4,36 +4,46 @@ | |||
1830 | 4 | import ipaddress | 4 | import ipaddress |
1831 | 5 | import os | 5 | import os |
1832 | 6 | import re | 6 | import re |
1833 | 7 | import subprocess | ||
1834 | 8 | import textwrap | 7 | import textwrap |
1835 | 9 | import yaml | 8 | import yaml |
1836 | 10 | 9 | ||
1837 | 11 | 10 | ||
1839 | 12 | class TestNetworkAbs(VMBaseClass): | 11 | class TestNetworkBaseTestsAbs(VMBaseClass): |
1840 | 13 | interactive = False | 12 | interactive = False |
1841 | 14 | conf_file = "examples/tests/basic_network.yaml" | ||
1842 | 15 | extra_disks = [] | 13 | extra_disks = [] |
1843 | 16 | extra_nics = [] | 14 | extra_nics = [] |
1844 | 17 | collect_scripts = [textwrap.dedent(""" | 15 | collect_scripts = [textwrap.dedent(""" |
1845 | 18 | cd OUTPUT_COLLECT_D | 16 | cd OUTPUT_COLLECT_D |
1846 | 17 | echo "waiting for ipv6 to settle" && sleep 5 | ||
1847 | 19 | ifconfig -a > ifconfig_a | 18 | ifconfig -a > ifconfig_a |
1848 | 19 | ip link show > ip_link_show | ||
1849 | 20 | ip a > ip_a | ||
1850 | 21 | find /etc/network/interfaces.d > find_interfacesd | ||
1851 | 20 | cp -av /etc/network/interfaces . | 22 | cp -av /etc/network/interfaces . |
1852 | 21 | cp -av /etc/network/interfaces.d . | 23 | cp -av /etc/network/interfaces.d . |
1853 | 22 | find /etc/network/interfaces.d > find_interfacesd | ||
1854 | 23 | cp /etc/resolv.conf . | 24 | cp /etc/resolv.conf . |
1855 | 24 | cp -av /etc/udev/rules.d/70-persistent-net.rules . | 25 | cp -av /etc/udev/rules.d/70-persistent-net.rules . |
1856 | 25 | ip -o route show > ip_route_show | 26 | ip -o route show > ip_route_show |
1857 | 27 | ip -6 -o route show > ip_6_route_show | ||
1858 | 26 | route -n > route_n | 28 | route -n > route_n |
1859 | 29 | route -6 -n > route_6_n | ||
1860 | 27 | cp -av /run/network ./run_network | 30 | cp -av /run/network ./run_network |
1861 | 31 | cp -av /var/log/upstart ./upstart ||: | ||
1862 | 32 | sleep 10 && ip a > ip_a | ||
1863 | 28 | """)] | 33 | """)] |
1864 | 29 | 34 | ||
1865 | 30 | def test_output_files_exist(self): | 35 | def test_output_files_exist(self): |
1872 | 31 | self.output_files_exist(["ifconfig_a", | 36 | self.output_files_exist([ |
1873 | 32 | "interfaces", | 37 | "70-persistent-net.rules", |
1874 | 33 | "resolv.conf", | 38 | "find_interfacesd", |
1875 | 34 | "70-persistent-net.rules", | 39 | "ifconfig_a", |
1876 | 35 | "ip_route_show", | 40 | "interfaces", |
1877 | 36 | "route_n"]) | 41 | "ip_a", |
1878 | 42 | "ip_route_show", | ||
1879 | 43 | "resolv.conf", | ||
1880 | 44 | "route_6_n", | ||
1881 | 45 | "route_n", | ||
1882 | 46 | ]) | ||
1883 | 37 | 47 | ||
1884 | 38 | def test_etc_network_interfaces(self): | 48 | def test_etc_network_interfaces(self): |
1885 | 39 | with open(os.path.join(self.td.collect, "interfaces")) as fp: | 49 | with open(os.path.join(self.td.collect, "interfaces")) as fp: |
1886 | @@ -76,27 +86,44 @@ | |||
1887 | 76 | ''' | 86 | ''' |
1888 | 77 | expected_ifaces = self.get_expected_etc_resolvconf() | 87 | expected_ifaces = self.get_expected_etc_resolvconf() |
1889 | 78 | logger.debug('parsed eni ifaces:\n{}'.format(expected_ifaces)) | 88 | logger.debug('parsed eni ifaces:\n{}'.format(expected_ifaces)) |
1890 | 89 | |||
1891 | 90 | def _mk_dns_lines(dns_type, config): | ||
1892 | 91 | """ nameservers get a line per ns | ||
1893 | 92 | search is a space-separated list """ | ||
1894 | 93 | lines = [] | ||
1895 | 94 | if dns_type == 'nameservers': | ||
1896 | 95 | if ' ' in config: | ||
1897 | 96 | config = config.split() | ||
1898 | 97 | for ns in config: | ||
1899 | 98 | lines.append("nameserver %s" % ns) | ||
1900 | 99 | elif dns_type == 'search': | ||
1901 | 100 | if isinstance(config, list): | ||
1902 | 101 | config = " ".join(config) | ||
1903 | 102 | lines.append("search %s" % config) | ||
1904 | 103 | |||
1905 | 104 | return lines | ||
1906 | 105 | |||
1907 | 79 | for ifname in expected_ifaces.keys(): | 106 | for ifname in expected_ifaces.keys(): |
1908 | 80 | iface = expected_ifaces.get(ifname) | 107 | iface = expected_ifaces.get(ifname) |
1909 | 81 | for k, v in iface.get('dns', {}).items(): | 108 | for k, v in iface.get('dns', {}).items(): |
1914 | 82 | dns_line = '{} {}'.format( | 109 | print('k=%s v=%s' % (k, v)) |
1915 | 83 | k.replace('nameservers', 'nameserver'), " ".join(v)) | 110 | for dns_line in _mk_dns_lines(k, v): |
1916 | 84 | logger.debug('dns_line:{}'.format(dns_line)) | 111 | logger.debug('dns_line:%s', dns_line) |
1917 | 85 | self.assertTrue(dns_line in resolv_lines) | 112 | self.assertTrue(dns_line in resolv_lines) |
1918 | 86 | 113 | ||
1921 | 87 | def test_ifconfig_output(self): | 114 | def test_ip_output(self): |
1922 | 88 | '''check ifconfig output with test input''' | 115 | '''check iproute2 'ip a' output with test input''' |
1923 | 89 | network_state = self.get_network_state() | 116 | network_state = self.get_network_state() |
1924 | 90 | logger.debug('expected_network_state:\n{}'.format( | 117 | logger.debug('expected_network_state:\n{}'.format( |
1925 | 91 | yaml.dump(network_state, default_flow_style=False, indent=4))) | 118 | yaml.dump(network_state, default_flow_style=False, indent=4))) |
1926 | 92 | 119 | ||
1930 | 93 | with open(os.path.join(self.td.collect, "ifconfig_a")) as fp: | 120 | with open(os.path.join(self.td.collect, "ip_a")) as fp: |
1931 | 94 | ifconfig_a = fp.read() | 121 | ip_a = fp.read() |
1932 | 95 | logger.debug('ifconfig -a:\n{}'.format(ifconfig_a)) | 122 | logger.debug('ip a:\n{}'.format(ip_a)) |
1933 | 96 | 123 | ||
1937 | 97 | ifconfig_dict = helpers.ifconfig_to_dict(ifconfig_a) | 124 | ip_dict = helpers.ip_a_to_dict(ip_a) |
1938 | 98 | logger.debug('parsed ifcfg dict:\n{}'.format( | 125 | print('parsed ip_a dict:\n{}'.format( |
1939 | 99 | yaml.dump(ifconfig_dict, default_flow_style=False, indent=4))) | 126 | yaml.dump(ip_dict, default_flow_style=False, indent=4))) |
1940 | 100 | 127 | ||
1941 | 101 | with open(os.path.join(self.td.collect, "ip_route_show")) as fp: | 128 | with open(os.path.join(self.td.collect, "ip_route_show")) as fp: |
1942 | 102 | ip_route_show = fp.read() | 129 | ip_route_show = fp.read() |
1943 | @@ -115,341 +142,167 @@ | |||
1944 | 115 | route_n = fp.read() | 142 | route_n = fp.read() |
1945 | 116 | logger.debug("route -n:\n{}".format(route_n)) | 143 | logger.debug("route -n:\n{}".format(route_n)) |
1946 | 117 | 144 | ||
1947 | 145 | with open(os.path.join(self.td.collect, "route_6_n")) as fp: | ||
1948 | 146 | route_6_n = fp.read() | ||
1949 | 147 | logger.debug("route -6 -n:\n{}".format(route_6_n)) | ||
1950 | 148 | |||
1951 | 149 | routes = { | ||
1952 | 150 | '4': route_n, | ||
1953 | 151 | '6': route_6_n, | ||
1954 | 152 | } | ||
1955 | 118 | interfaces = network_state.get('interfaces') | 153 | interfaces = network_state.get('interfaces') |
1956 | 119 | for iface in interfaces.values(): | 154 | for iface in interfaces.values(): |
1984 | 120 | subnets = iface.get('subnets', {}) | 155 | print("\nnetwork_state iface: %s" % ( |
1985 | 121 | if subnets: | 156 | yaml.dump(iface, default_flow_style=False, indent=4))) |
1986 | 122 | for index, subnet in zip(range(0, len(subnets)), subnets): | 157 | self.check_interface(iface['name'], |
1987 | 123 | iface['index'] = index | 158 | iface, |
1988 | 124 | if index == 0: | 159 | ip_dict.get(iface['name']), |
1989 | 125 | ifname = "{name}".format(**iface) | 160 | routes) |
1990 | 126 | else: | 161 | |
1991 | 127 | ifname = "{name}:{index}".format(**iface) | 162 | def check_interface(self, ifname, iface, ipcfg, routes): |
1992 | 128 | 163 | print('check_interface: testing ' | |
1993 | 129 | self.check_interface(iface, | 164 | 'ifname:{}\niface:\n{}\n\nipcfg:\n{}'.format(ifname, iface, |
1994 | 130 | ifconfig_dict.get(ifname), | 165 | ipcfg)) |
1995 | 131 | route_n) | 166 | # FIXME: remove check? |
1969 | 132 | else: | ||
1970 | 133 | iface['index'] = 0 | ||
1971 | 134 | self.check_interface(iface, | ||
1972 | 135 | ifconfig_dict.get(iface['name']), | ||
1973 | 136 | route_n) | ||
1974 | 137 | |||
1975 | 138 | def check_interface(self, iface, ifconfig, route_n): | ||
1976 | 139 | logger.debug( | ||
1977 | 140 | 'testing iface:\n{}\n\nifconfig:\n{}'.format(iface, ifconfig)) | ||
1978 | 141 | subnets = iface.get('subnets', {}) | ||
1979 | 142 | if subnets and iface['index'] != 0: | ||
1980 | 143 | ifname = "{name}:{index}".format(**iface) | ||
1981 | 144 | else: | ||
1982 | 145 | ifname = "{name}".format(**iface) | ||
1983 | 146 | |||
1996 | 147 | # initial check, do we have the correct iface ? | 167 | # initial check, do we have the correct iface ? |
2003 | 148 | logger.debug('ifname={}'.format(ifname)) | 168 | print('ifname={}'.format(ifname)) |
2004 | 149 | logger.debug("ifconfig['interface']={}".format(ifconfig['interface'])) | 169 | self.assertEqual(ifname, ipcfg['interface']) |
2005 | 150 | self.assertEqual(ifname, ifconfig['interface']) | 170 | print("ipcfg['interface']={}".format(ipcfg['interface'])) |
2006 | 151 | 171 | ||
2007 | 152 | # check physical interface attributes | 172 | # check physical interface attributes (skip bond members, macs change) |
2008 | 153 | for key in ['mac_address', 'mtu']: | 173 | if iface['type'] in ['physical'] and 'bond-master' not in iface: |
2009 | 174 | for key in ['mac_address']: | ||
2010 | 175 | print("checking mac on iface: %s" % iface['name']) | ||
2011 | 176 | if key in iface and iface[key]: | ||
2012 | 177 | self.assertEqual(iface[key].lower(), | ||
2013 | 178 | ipcfg[key].lower()) | ||
2014 | 179 | |||
2015 | 180 | # we can check mtu on all interfaces | ||
2016 | 181 | for key in ['mtu']: | ||
2017 | 154 | if key in iface and iface[key]: | 182 | if key in iface and iface[key]: |
2032 | 155 | self.assertEqual(iface[key], | 183 | print("checking mtu on iface: %s" % iface['name']) |
2033 | 156 | ifconfig[key]) | 184 | self.assertEqual(int(iface[key]), |
2034 | 157 | 185 | int(ipcfg[key])) | |
2035 | 158 | def __get_subnet(subnets, subidx): | 186 | |
2036 | 159 | for index, subnet in zip(range(0, len(subnets)), subnets): | 187 | # check subnet related attributes |
2037 | 160 | if index == subidx: | 188 | subnets = iface.get('subnets') |
2038 | 161 | break | 189 | if subnets is None: |
2039 | 162 | return subnet | 190 | subnets = [] |
2040 | 163 | 191 | for subnet in subnets: | |
2041 | 164 | # check subnet related attributes, and specifically only | 192 | config_inet_iface = None |
2042 | 165 | # the subnet specified by iface['index'] | 193 | found_inet_iface = None |
2043 | 166 | subnets = iface.get('subnets', {}) | 194 | print('validating subnet:\n%s' % subnet) |
2030 | 167 | if subnets: | ||
2031 | 168 | subnet = __get_subnet(subnets, iface['index']) | ||
2044 | 169 | if 'address' in subnet and subnet['address']: | 195 | if 'address' in subnet and subnet['address']: |
2045 | 196 | # we will create to ipaddress.IPvXInterface objects | ||
2046 | 197 | # one based on config, and other from collected data | ||
2047 | 198 | # and compare. | ||
2048 | 199 | config_ipstr = subnet['address'] | ||
2049 | 200 | if 'netmask' in subnet: | ||
2050 | 201 | config_ipstr += "/%s" % subnet['netmask'] | ||
2051 | 202 | |||
2052 | 203 | # One more bit is how to construct the | ||
2053 | 204 | # right Version interface, detecting on ":" in address | ||
2054 | 205 | # detect ipv6 or v4 | ||
2055 | 170 | if ':' in subnet['address']: | 206 | if ':' in subnet['address']: |
2152 | 171 | inet_iface = ipaddress.IPv6Interface( | 207 | # v6 |
2153 | 172 | subnet['address']) | 208 | config_inet_iface = ipaddress.IPv6Interface(config_ipstr) |
2154 | 173 | else: | 209 | ip_func = ipaddress.IPv6Interface |
2155 | 174 | inet_iface = ipaddress.IPv4Interface( | 210 | addresses = ipcfg.get('inet6', []) |
2156 | 175 | subnet['address']) | 211 | else: |
2157 | 176 | 212 | # v4 | |
2158 | 177 | # check ip addr | 213 | config_inet_iface = ipaddress.IPv4Interface(config_ipstr) |
2159 | 178 | self.assertEqual(str(inet_iface.ip), | 214 | ip_func = ipaddress.IPv4Interface |
2160 | 179 | ifconfig['address']) | 215 | addresses = ipcfg.get('inet4', []) |
2161 | 180 | 216 | ||
2162 | 181 | self.assertEqual(str(inet_iface.netmask), | 217 | # find a matching |
2163 | 182 | ifconfig['netmask']) | 218 | print('found addresses: %s' % addresses) |
2164 | 183 | 219 | for ip in addresses: | |
2165 | 184 | self.assertEqual( | 220 | print('cur ip=%s\nsubnet=%s' % (ip, subnet)) |
2166 | 185 | str(inet_iface.network.broadcast_address), | 221 | # drop /CIDR if present for matching |
2167 | 186 | ifconfig['broadcast']) | 222 | if (ip['address'].split("/")[0] == |
2168 | 187 | 223 | subnet['address'].split("/")[0]): | |
2169 | 188 | # handle gateway by looking at routing table | 224 | print('found a match!') |
2170 | 189 | if 'gateway' in subnet and subnet['gateway']: | 225 | found_ipstr = ip['address'] |
2171 | 190 | gw_ip = subnet['gateway'] | 226 | if ('netmask' in subnet or '/' in subnet['address']): |
2172 | 191 | gateways = [line for line in route_n.split('\n') | 227 | found_ipstr += "/%s" % ip.get('prefixlen') |
2173 | 192 | if 'UG' in line and gw_ip in line] | 228 | found_inet_iface = ip_func(found_ipstr) |
2174 | 193 | logger.debug('matching gateways:\n{}'.format(gateways)) | 229 | print('returning inet iface') |
2175 | 194 | self.assertEqual(len(gateways), 1) | 230 | break |
2176 | 195 | [gateways] = gateways | 231 | |
2177 | 196 | (dest, gw, genmask, flags, metric, ref, use, iface) = \ | 232 | # check ipaddress interface matches (config vs. found) |
2178 | 197 | gateways.split() | 233 | self.assertIsNotNone(config_inet_iface) |
2179 | 198 | logger.debug('expected gw:{} found gw:{}'.format(gw_ip, gw)) | 234 | self.assertIsNotNone(found_inet_iface) |
2180 | 199 | self.assertEqual(gw_ip, gw) | 235 | self.assertEqual(config_inet_iface, found_inet_iface) |
2181 | 200 | 236 | ||
2182 | 201 | 237 | def __find_gw_config(subnet): | |
2183 | 202 | class TestNetworkStaticAbs(TestNetworkAbs): | 238 | gateways = [] |
2184 | 203 | conf_file = "examples/tests/basic_network_static.yaml" | 239 | if 'gateway' in subnet: |
2185 | 204 | 240 | gateways.append(subnet.get('gateway')) | |
2186 | 205 | 241 | for route in subnet.get('routes', []): | |
2187 | 206 | class TestNetworkVlanAbs(TestNetworkAbs): | 242 | gateways += __find_gw_config(route) |
2188 | 207 | conf_file = "examples/tests/vlan_network.yaml" | 243 | return gateways |
2189 | 208 | collect_scripts = TestNetworkAbs.collect_scripts + [textwrap.dedent(""" | 244 | |
2190 | 209 | cd OUTPUT_COLLECT_D | 245 | # handle gateways by looking at routing table |
2191 | 210 | dpkg-query -W -f '${Status}' vlan > vlan_installed | 246 | configured_gws = __find_gw_config(subnet) |
2192 | 211 | ip -d link show interface1.2667 > ip_link_show_interface1.2667 | 247 | print('iface:%s configured_gws: %s' % (ifname, configured_gws)) |
2193 | 212 | ip -d link show interface1.2668 > ip_link_show_interface1.2668 | 248 | for gw_ip in configured_gws: |
2194 | 213 | ip -d link show interface1.2669 > ip_link_show_interface1.2669 | 249 | logger.debug('found a gateway in subnet config: %s', gw_ip) |
2195 | 214 | ip -d link show interface1.2670 > ip_link_show_interface1.2670 | 250 | if ":" in gw_ip: |
2196 | 215 | """)] | 251 | route_d = routes['6'] |
2197 | 216 | 252 | else: | |
2198 | 217 | def get_vlans(self): | 253 | route_d = routes['4'] |
2199 | 218 | network_state = self.get_network_state() | 254 | |
2200 | 219 | logger.debug('get_vlans ns:\n{}'.format( | 255 | found_gws = [line for line in route_d.split('\n') |
2201 | 220 | yaml.dump(network_state, default_flow_style=False, indent=4))) | 256 | if 'UG' in line and gw_ip in line] |
2202 | 221 | interfaces = network_state.get('interfaces') | 257 | logger.debug('found gateways in guest output:\n%s', found_gws) |
2203 | 222 | return [iface for iface in interfaces.values() | 258 | |
2204 | 223 | if iface['type'] == 'vlan'] | 259 | print('found_gws: %s\nexpected: %s' % (found_gws, |
2205 | 224 | 260 | configured_gws)) | |
2206 | 225 | def test_output_files_exist_vlan(self): | 261 | self.assertEqual(len(found_gws), len(configured_gws)) |
2207 | 226 | link_files = ["ip_link_show_{}".format(vlan['name']) | 262 | for fgw in found_gws: |
2208 | 227 | for vlan in self.get_vlans()] | 263 | if ":" in gw_ip: |
2209 | 228 | self.output_files_exist(["vlan_installed"] + link_files) | 264 | (dest, gw, flags, metric, ref, use, iface) = \ |
2210 | 229 | 265 | fgw.split() | |
2211 | 230 | def test_vlan_installed(self): | 266 | else: |
2212 | 231 | with open(os.path.join(self.td.collect, "vlan_installed")) as fp: | 267 | (dest, gw, genmask, flags, metric, ref, use, iface) = \ |
2213 | 232 | status = fp.read().strip() | 268 | fgw.split() |
2214 | 233 | logger.debug('vlan installed?: {}'.format(status)) | 269 | logger.debug('configured gw:%s found gw:%s', gw_ip, gw) |
2215 | 234 | self.assertEqual('install ok installed', status) | 270 | self.assertEqual(gw_ip, gw) |
2216 | 235 | 271 | ||
2217 | 236 | def test_vlan_enabled(self): | 272 | |
2218 | 237 | 273 | class TestNetworkBasicAbs(TestNetworkBaseTestsAbs): | |
2219 | 238 | # we must have at least one | 274 | """ Basic network testing with ipv4 |
2124 | 239 | self.assertGreaterEqual(len(self.get_vlans()), 1) | ||
2125 | 240 | |||
2126 | 241 | # did they get configured? | ||
2127 | 242 | for vlan in self.get_vlans(): | ||
2128 | 243 | link_file = "ip_link_show_" + vlan['name'] | ||
2129 | 244 | vlan_msg = "vlan protocol 802.1Q id " + str(vlan['vlan_id']) | ||
2130 | 245 | self.check_file_regex(link_file, vlan_msg) | ||
2131 | 246 | |||
2132 | 247 | |||
2133 | 248 | class TestNetworkENISource(TestNetworkAbs): | ||
2134 | 249 | """ Curtin now emits a source /etc/network/interfaces.d/*.cfg | ||
2135 | 250 | line. This test exercises this feature by emitting additional | ||
2136 | 251 | network configuration in /etc/network/interfaces.d/eth2.cfg | ||
2137 | 252 | |||
2138 | 253 | This relies on the network_config.yaml of the TestClass to | ||
2139 | 254 | define a spare nic with no configuration. This ensures that | ||
2140 | 255 | a udev rule for eth2 is emitted so we can reference the interface | ||
2141 | 256 | in our injected configuration. | ||
2142 | 257 | |||
2143 | 258 | Note, ifupdown allows multiple stanzas with the same iface name | ||
2144 | 259 | and combines the options together during ifup. We rely on this | ||
2145 | 260 | feature allowing etc/network/interfaces to have an unconfigured | ||
2146 | 261 | iface eth2 inet manual line, and then defer the configuration | ||
2147 | 262 | to /etc/network/interfaces.d/eth2.cfg | ||
2148 | 263 | |||
2149 | 264 | This testcase then uses curtin.net.deb_parse_config method to | ||
2150 | 265 | extract information about what curtin wrote and compare that | ||
2151 | 266 | with what was actually configured (which we capture via ifconfig) | ||
2220 | 267 | """ | 275 | """ |
2408 | 268 | 276 | conf_file = "examples/tests/basic_network.yaml" | |
2409 | 269 | conf_file = "examples/tests/network_source.yaml" | 277 | |
2410 | 270 | collect_scripts = [textwrap.dedent(""" | 278 | |
2411 | 271 | cd OUTPUT_COLLECT_D | 279 | class PreciseHWETTestNetworkBasic(relbase.precise_hwe_t, TestNetworkBasicAbs): |
2412 | 272 | ifconfig -a > ifconfig_a | 280 | # FIXME: off due to hang at test: Starting execute cloud user/final scripts |
2413 | 273 | cp -av /etc/network/interfaces . | 281 | __test__ = False |
2414 | 274 | cp -a /etc/network/interfaces.d . | 282 | |
2415 | 275 | find /etc/network/interfaces.d > find_interfacesd | 283 | |
2416 | 276 | cp /etc/resolv.conf . | 284 | class TrustyTestNetworkBasic(relbase.trusty, TestNetworkBasicAbs): |
2417 | 277 | cp -av /etc/udev/rules.d/70-persistent-net.rules . | 285 | __test__ = True |
2418 | 278 | ip -o route show > ip_route_show | 286 | |
2419 | 279 | route -n > route_n | 287 | |
2420 | 280 | """)] | 288 | class TrustyHWEUTestNetworkBasic(relbase.trusty_hwe_u, TrustyTestNetworkBasic): |
2421 | 281 | 289 | # Working, off by default to safe test suite runtime, covered by bonding | |
2422 | 282 | def test_source_cfg_exists(self): | 290 | __test__ = False |
2423 | 283 | """Test that our curthooks wrote our injected config.""" | 291 | |
2424 | 284 | self.output_files_exist(["interfaces.d/interface2.cfg"]) | 292 | |
2425 | 285 | 293 | class TrustyHWEVTestNetworkBasic(relbase.trusty_hwe_v, TrustyTestNetworkBasic): | |
2426 | 286 | def test_etc_network_interfaces_source_cfg(self): | 294 | # Working, off by default to safe test suite runtime, covered by bonding |
2427 | 287 | """ Compare injected configuration as parsed by curtin matches | 295 | __test__ = False |
2428 | 288 | how ifup configured the interface.""" | 296 | |
2429 | 289 | # interfaces uses absolute paths, fix for test-case | 297 | |
2430 | 290 | interfaces = os.path.join(self.td.collect, "interfaces") | 298 | class TrustyHWEWTestNetworkBasic(relbase.trusty_hwe_w, TrustyTestNetworkBasic): |
2431 | 291 | cmd = ['sed', '-i.orig', '-e', 's,/etc/network/,,g', | 299 | # Working, off by default to safe test suite runtime, covered by bonding |
2432 | 292 | '{}'.format(interfaces)] | 300 | __test__ = False |
2433 | 293 | subprocess.check_call(cmd, stderr=subprocess.STDOUT) | 301 | |
2434 | 294 | 302 | ||
2435 | 295 | curtin_ifaces = self.parse_deb_config(interfaces) | 303 | class XenialTestNetworkBasic(relbase.xenial, TestNetworkBasicAbs): |
2436 | 296 | logger.debug('parsed eni dict:\n{}'.format( | 304 | __test__ = True |
2437 | 297 | yaml.dump(curtin_ifaces, default_flow_style=False, indent=4))) | 305 | |
2438 | 298 | print('parsed eni dict:\n{}'.format( | 306 | |
2439 | 299 | yaml.dump(curtin_ifaces, default_flow_style=False, indent=4))) | 307 | class YakketyTestNetworkBasic(relbase.yakkety, TestNetworkBasicAbs): |
2253 | 300 | |||
2254 | 301 | with open(os.path.join(self.td.collect, "ifconfig_a")) as fp: | ||
2255 | 302 | ifconfig_a = fp.read() | ||
2256 | 303 | logger.debug('ifconfig -a:\n{}'.format(ifconfig_a)) | ||
2257 | 304 | |||
2258 | 305 | ifconfig_dict = helpers.ifconfig_to_dict(ifconfig_a) | ||
2259 | 306 | logger.debug('parsed ifconfig dict:\n{}'.format( | ||
2260 | 307 | yaml.dump(ifconfig_dict, default_flow_style=False, indent=4))) | ||
2261 | 308 | print('parsed ifconfig dict:\n{}'.format( | ||
2262 | 309 | yaml.dump(ifconfig_dict, default_flow_style=False, indent=4))) | ||
2263 | 310 | |||
2264 | 311 | iface = 'interface2' | ||
2265 | 312 | self.assertTrue(iface in curtin_ifaces) | ||
2266 | 313 | |||
2267 | 314 | expected_address = curtin_ifaces[iface].get('address', None) | ||
2268 | 315 | self.assertIsNotNone(expected_address) | ||
2269 | 316 | |||
2270 | 317 | # handle CIDR notation | ||
2271 | 318 | def _nocidr(addr): | ||
2272 | 319 | return addr.split("/")[0] | ||
2273 | 320 | actual_address = ifconfig_dict[iface].get('address', "") | ||
2274 | 321 | self.assertEqual(_nocidr(expected_address), _nocidr(actual_address)) | ||
2275 | 322 | |||
2276 | 323 | |||
2277 | 324 | class PreciseHWETTestNetwork(relbase.precise_hwe_t, TestNetworkAbs): | ||
2278 | 325 | # FIXME: off due to hang at test: Starting execute cloud user/final scripts | ||
2279 | 326 | __test__ = False | ||
2280 | 327 | |||
2281 | 328 | |||
2282 | 329 | class PreciseHWETTestNetworkStatic(relbase.precise_hwe_t, | ||
2283 | 330 | TestNetworkStaticAbs): | ||
2284 | 331 | # FIXME: off due to hang at test: Starting execute cloud user/final scripts | ||
2285 | 332 | __test__ = False | ||
2286 | 333 | |||
2287 | 334 | |||
2288 | 335 | class TrustyTestNetwork(relbase.trusty, TestNetworkAbs): | ||
2289 | 336 | __test__ = True | ||
2290 | 337 | |||
2291 | 338 | |||
2292 | 339 | class TrustyTestNetworkStatic(relbase.trusty, TestNetworkStaticAbs): | ||
2293 | 340 | __test__ = True | ||
2294 | 341 | |||
2295 | 342 | |||
2296 | 343 | class TrustyHWEUTestNetwork(relbase.trusty_hwe_u, TrustyTestNetwork): | ||
2297 | 344 | # Working, off by default to safe test suite runtime, covered by bonding | ||
2298 | 345 | __test__ = False | ||
2299 | 346 | |||
2300 | 347 | |||
2301 | 348 | class TrustyHWEUTestNetworkStatic(relbase.trusty_hwe_u, | ||
2302 | 349 | TestNetworkStaticAbs): | ||
2303 | 350 | # Working, off by default to safe test suite runtime, covered by bonding | ||
2304 | 351 | __test__ = False | ||
2305 | 352 | |||
2306 | 353 | |||
2307 | 354 | class TrustyHWEVTestNetwork(relbase.trusty_hwe_v, TrustyTestNetwork): | ||
2308 | 355 | # Working, off by default to safe test suite runtime, covered by bonding | ||
2309 | 356 | __test__ = False | ||
2310 | 357 | |||
2311 | 358 | |||
2312 | 359 | class TrustyHWEVTestNetworkStatic(relbase.trusty_hwe_v, | ||
2313 | 360 | TestNetworkStaticAbs): | ||
2314 | 361 | # Working, off by default to safe test suite runtime, covered by bonding | ||
2315 | 362 | __test__ = False | ||
2316 | 363 | |||
2317 | 364 | |||
2318 | 365 | class TrustyHWEWTestNetwork(relbase.trusty_hwe_w, TrustyTestNetwork): | ||
2319 | 366 | # Working, off by default to safe test suite runtime, covered by bonding | ||
2320 | 367 | __test__ = False | ||
2321 | 368 | |||
2322 | 369 | |||
2323 | 370 | class TrustyHWEWTestNetworkStatic(relbase.trusty_hwe_w, | ||
2324 | 371 | TestNetworkStaticAbs): | ||
2325 | 372 | # Working, off by default to safe test suite runtime, covered by bonding | ||
2326 | 373 | __test__ = False | ||
2327 | 374 | |||
2328 | 375 | |||
2329 | 376 | class WilyTestNetwork(relbase.wily, TestNetworkAbs): | ||
2330 | 377 | # EOL - 2016-07-28 | ||
2331 | 378 | __test__ = False | ||
2332 | 379 | |||
2333 | 380 | |||
2334 | 381 | class WilyTestNetworkStatic(relbase.wily, TestNetworkStaticAbs): | ||
2335 | 382 | # EOL - 2016-07-28 | ||
2336 | 383 | __test__ = False | ||
2337 | 384 | |||
2338 | 385 | |||
2339 | 386 | class XenialTestNetwork(relbase.xenial, TestNetworkAbs): | ||
2340 | 387 | __test__ = True | ||
2341 | 388 | |||
2342 | 389 | |||
2343 | 390 | class XenialTestNetworkStatic(relbase.xenial, TestNetworkStaticAbs): | ||
2344 | 391 | __test__ = True | ||
2345 | 392 | |||
2346 | 393 | |||
2347 | 394 | class YakketyTestNetwork(relbase.yakkety, TestNetworkAbs): | ||
2348 | 395 | __test__ = True | ||
2349 | 396 | |||
2350 | 397 | |||
2351 | 398 | class YakketyTestNetworkStatic(relbase.yakkety, TestNetworkStaticAbs): | ||
2352 | 399 | __test__ = True | ||
2353 | 400 | |||
2354 | 401 | |||
2355 | 402 | class PreciseTestNetworkVlan(relbase.precise, TestNetworkVlanAbs): | ||
2356 | 403 | __test__ = True | ||
2357 | 404 | |||
2358 | 405 | # precise ip -d link show output is different (of course) | ||
2359 | 406 | def test_vlan_enabled(self): | ||
2360 | 407 | |||
2361 | 408 | # we must have at least one | ||
2362 | 409 | self.assertGreaterEqual(len(self.get_vlans()), 1) | ||
2363 | 410 | |||
2364 | 411 | # did they get configured? | ||
2365 | 412 | for vlan in self.get_vlans(): | ||
2366 | 413 | link_file = "ip_link_show_" + vlan['name'] | ||
2367 | 414 | vlan_msg = "vlan id " + str(vlan['vlan_id']) | ||
2368 | 415 | self.check_file_regex(link_file, vlan_msg) | ||
2369 | 416 | |||
2370 | 417 | |||
2371 | 418 | class TrustyTestNetworkVlan(relbase.trusty, TestNetworkVlanAbs): | ||
2372 | 419 | __test__ = True | ||
2373 | 420 | |||
2374 | 421 | |||
2375 | 422 | class WilyTestNetworkVlan(relbase.wily, TestNetworkVlanAbs): | ||
2376 | 423 | # EOL - 2016-07-28 | ||
2377 | 424 | __test__ = False | ||
2378 | 425 | |||
2379 | 426 | |||
2380 | 427 | class XenialTestNetworkVlan(relbase.xenial, TestNetworkVlanAbs): | ||
2381 | 428 | __test__ = True | ||
2382 | 429 | |||
2383 | 430 | |||
2384 | 431 | class YakketyTestNetworkVlan(relbase.yakkety, TestNetworkVlanAbs): | ||
2385 | 432 | __test__ = True | ||
2386 | 433 | |||
2387 | 434 | |||
2388 | 435 | class PreciseTestNetworkENISource(relbase.precise, TestNetworkENISource): | ||
2389 | 436 | __test__ = False | ||
2390 | 437 | # not working, still debugging though; possible older ifupdown doesn't | ||
2391 | 438 | # like the multiple iface method. | ||
2392 | 439 | |||
2393 | 440 | |||
2394 | 441 | class TrustyTestNetworkENISource(relbase.trusty, TestNetworkENISource): | ||
2395 | 442 | __test__ = True | ||
2396 | 443 | |||
2397 | 444 | |||
2398 | 445 | class WilyTestNetworkENISource(relbase.wily, TestNetworkENISource): | ||
2399 | 446 | # EOL - 2016-07-28 | ||
2400 | 447 | __test__ = False | ||
2401 | 448 | |||
2402 | 449 | |||
2403 | 450 | class XenialTestNetworkENISource(relbase.xenial, TestNetworkENISource): | ||
2404 | 451 | __test__ = True | ||
2405 | 452 | |||
2406 | 453 | |||
2407 | 454 | class YakketyTestNetworkENISource(relbase.yakkety, TestNetworkENISource): | ||
2440 | 455 | __test__ = True | 308 | __test__ = True |
2441 | 456 | 309 | ||
2442 | === added file 'tests/vmtests/test_network_alias.py' | |||
2443 | --- tests/vmtests/test_network_alias.py 1970-01-01 00:00:00 +0000 | |||
2444 | +++ tests/vmtests/test_network_alias.py 2016-08-29 20:01:45 +0000 | |||
2445 | @@ -0,0 +1,40 @@ | |||
2446 | 1 | from .releases import base_vm_classes as relbase | ||
2447 | 2 | from .test_network import TestNetworkBaseTestsAbs | ||
2448 | 3 | |||
2449 | 4 | |||
2450 | 5 | class TestNetworkAliasAbs(TestNetworkBaseTestsAbs): | ||
2451 | 6 | """ Multi-ip address network testing | ||
2452 | 7 | """ | ||
2453 | 8 | conf_file = "examples/tests/network_alias.yaml" | ||
2454 | 9 | |||
2455 | 10 | |||
2456 | 11 | class PreciseHWETTestNetworkAlias(relbase.precise_hwe_t, TestNetworkAliasAbs): | ||
2457 | 12 | # FIXME: off due to hang at test: Starting execute cloud user/final scripts | ||
2458 | 13 | __test__ = True | ||
2459 | 14 | |||
2460 | 15 | |||
2461 | 16 | class TrustyTestNetworkAlias(relbase.trusty, TestNetworkAliasAbs): | ||
2462 | 17 | __test__ = True | ||
2463 | 18 | |||
2464 | 19 | |||
2465 | 20 | class TrustyHWEUTestNetworkAlias(relbase.trusty_hwe_u, TrustyTestNetworkAlias): | ||
2466 | 21 | # Working, off by default to safe test suite runtime, covered by bonding | ||
2467 | 22 | __test__ = False | ||
2468 | 23 | |||
2469 | 24 | |||
2470 | 25 | class TrustyHWEVTestNetworkAlias(relbase.trusty_hwe_v, TrustyTestNetworkAlias): | ||
2471 | 26 | # Working, off by default to safe test suite runtime, covered by bonding | ||
2472 | 27 | __test__ = False | ||
2473 | 28 | |||
2474 | 29 | |||
2475 | 30 | class TrustyHWEWTestNetworkAlias(relbase.trusty_hwe_w, TrustyTestNetworkAlias): | ||
2476 | 31 | # Working, off by default to safe test suite runtime, covered by bonding | ||
2477 | 32 | __test__ = False | ||
2478 | 33 | |||
2479 | 34 | |||
2480 | 35 | class XenialTestNetworkAlias(relbase.xenial, TestNetworkAliasAbs): | ||
2481 | 36 | __test__ = True | ||
2482 | 37 | |||
2483 | 38 | |||
2484 | 39 | class YakketyTestNetworkAlias(relbase.yakkety, TestNetworkAliasAbs): | ||
2485 | 40 | __test__ = True | ||
2486 | 0 | 41 | ||
2487 | === added file 'tests/vmtests/test_network_bonding.py' | |||
2488 | --- tests/vmtests/test_network_bonding.py 1970-01-01 00:00:00 +0000 | |||
2489 | +++ tests/vmtests/test_network_bonding.py 2016-08-29 20:01:45 +0000 | |||
2490 | @@ -0,0 +1,63 @@ | |||
2491 | 1 | from . import logger | ||
2492 | 2 | from .releases import base_vm_classes as relbase | ||
2493 | 3 | from .test_network import TestNetworkBaseTestsAbs | ||
2494 | 4 | |||
2495 | 5 | import textwrap | ||
2496 | 6 | |||
2497 | 7 | |||
2498 | 8 | class TestNetworkBondingAbs(TestNetworkBaseTestsAbs): | ||
2499 | 9 | conf_file = "examples/tests/bonding_network.yaml" | ||
2500 | 10 | collect_scripts = TestNetworkBaseTestsAbs.collect_scripts + [ | ||
2501 | 11 | textwrap.dedent(""" | ||
2502 | 12 | cd OUTPUT_COLLECT_D | ||
2503 | 13 | dpkg-query -W -f '${Status}' ifenslave > ifenslave_installed | ||
2504 | 14 | """)] | ||
2505 | 15 | |||
2506 | 16 | def test_output_files_exist_ifenslave(self): | ||
2507 | 17 | self.output_files_exist(["ifenslave_installed"]) | ||
2508 | 18 | |||
2509 | 19 | def test_ifenslave_installed(self): | ||
2510 | 20 | status = self.load_collect_file("ifenslave_installed") | ||
2511 | 21 | logger.debug('ifenslave installed: {}'.format(status)) | ||
2512 | 22 | self.assertEqual('install ok installed', status) | ||
2513 | 23 | |||
2514 | 24 | |||
2515 | 25 | class PreciseHWETTestBonding(relbase.precise_hwe_t, TestNetworkBondingAbs): | ||
2516 | 26 | __test__ = True | ||
2517 | 27 | # package names on precise are different, need to check on ifenslave-2.6 | ||
2518 | 28 | collect_scripts = TestNetworkBondingAbs.collect_scripts + [ | ||
2519 | 29 | textwrap.dedent(""" | ||
2520 | 30 | cd OUTPUT_COLLECT_D | ||
2521 | 31 | dpkg-query -W -f '${Status}' ifenslave-2.6 > ifenslave_installed | ||
2522 | 32 | """)] | ||
2523 | 33 | |||
2524 | 34 | |||
2525 | 35 | class TrustyTestBonding(relbase.trusty, TestNetworkBondingAbs): | ||
2526 | 36 | __test__ = False | ||
2527 | 37 | |||
2528 | 38 | |||
2529 | 39 | class TrustyHWEUTestBonding(relbase.trusty_hwe_u, TrustyTestBonding): | ||
2530 | 40 | __test__ = True | ||
2531 | 41 | |||
2532 | 42 | |||
2533 | 43 | class TrustyHWEVTestBonding(relbase.trusty_hwe_v, TrustyTestBonding): | ||
2534 | 44 | # Working, but off by default to safe test suite runtime | ||
2535 | 45 | # oldest/newest HWE-* covered above/below | ||
2536 | 46 | __test__ = False | ||
2537 | 47 | |||
2538 | 48 | |||
2539 | 49 | class TrustyHWEWTestBonding(relbase.trusty_hwe_w, TrustyTestBonding): | ||
2540 | 50 | __test__ = True | ||
2541 | 51 | |||
2542 | 52 | |||
2543 | 53 | class WilyTestBonding(relbase.wily, TestNetworkBondingAbs): | ||
2544 | 54 | # EOL - 2016-07-28 | ||
2545 | 55 | __test__ = False | ||
2546 | 56 | |||
2547 | 57 | |||
2548 | 58 | class XenialTestBonding(relbase.xenial, TestNetworkBondingAbs): | ||
2549 | 59 | __test__ = True | ||
2550 | 60 | |||
2551 | 61 | |||
2552 | 62 | class YakketyTestBonding(relbase.yakkety, TestNetworkBondingAbs): | ||
2553 | 63 | __test__ = True | ||
2554 | 0 | 64 | ||
2555 | === added file 'tests/vmtests/test_network_enisource.py' | |||
2556 | --- tests/vmtests/test_network_enisource.py 1970-01-01 00:00:00 +0000 | |||
2557 | +++ tests/vmtests/test_network_enisource.py 2016-08-29 20:01:45 +0000 | |||
2558 | @@ -0,0 +1,91 @@ | |||
2559 | 1 | from . import logger, helpers | ||
2560 | 2 | from .releases import base_vm_classes as relbase | ||
2561 | 3 | from .test_network import TestNetworkBaseTestsAbs | ||
2562 | 4 | |||
2563 | 5 | import os | ||
2564 | 6 | import subprocess | ||
2565 | 7 | import yaml | ||
2566 | 8 | |||
2567 | 9 | |||
2568 | 10 | class TestNetworkENISource(TestNetworkBaseTestsAbs): | ||
2569 | 11 | """ Curtin now emits a source /etc/network/interfaces.d/*.cfg | ||
2570 | 12 | line. This test exercises this feature by emitting additional | ||
2571 | 13 | network configuration in /etc/network/interfaces.d/interface2.cfg | ||
2572 | 14 | |||
2573 | 15 | This relies on the network_config.yaml of the TestClass to | ||
2574 | 16 | define a spare nic with no configuration. This ensures that | ||
2575 | 17 | a udev rule for interface2 is emitted so we can reference the interface | ||
2576 | 18 | in our injected configuration. | ||
2577 | 19 | |||
2578 | 20 | Note, ifupdown allows multiple stanzas with the same iface name | ||
2579 | 21 | and combines the options together during ifup. We rely on this | ||
2580 | 22 | feature allowing etc/network/interfaces to have an unconfigured | ||
2581 | 23 | iface interface2 inet manual line, and then defer the configuration | ||
2582 | 24 | to /etc/network/interfaces.d/interface2.cfg | ||
2583 | 25 | |||
2584 | 26 | This testcase then uses curtin.net.deb_parse_config method to | ||
2585 | 27 | extract information about what curtin wrote and compare that | ||
2586 | 28 | with what was actually configured (which we capture via ifconfig) | ||
2587 | 29 | """ | ||
2588 | 30 | |||
2589 | 31 | conf_file = "examples/tests/network_source.yaml" | ||
2590 | 32 | |||
2591 | 33 | def test_source_cfg_exists(self): | ||
2592 | 34 | """Test that our curthooks wrote our injected config.""" | ||
2593 | 35 | self.output_files_exist(["interfaces.d/interface2.cfg"]) | ||
2594 | 36 | |||
2595 | 37 | def test_etc_network_interfaces_source_cfg(self): | ||
2596 | 38 | """ Compare injected configuration as parsed by curtin matches | ||
2597 | 39 | how ifup configured the interface.""" | ||
2598 | 40 | # interfaces uses absolute paths, fix for test-case | ||
2599 | 41 | interfaces = os.path.join(self.td.collect, "interfaces") | ||
2600 | 42 | cmd = ['sed', '-i.orig', '-e', 's,/etc/network/,,g', | ||
2601 | 43 | '{}'.format(interfaces)] | ||
2602 | 44 | subprocess.check_call(cmd, stderr=subprocess.STDOUT) | ||
2603 | 45 | |||
2604 | 46 | curtin_ifaces = self.parse_deb_config(interfaces) | ||
2605 | 47 | logger.debug('parsed eni dict:\n{}'.format( | ||
2606 | 48 | yaml.dump(curtin_ifaces, default_flow_style=False, indent=4))) | ||
2607 | 49 | print('parsed eni dict:\n{}'.format( | ||
2608 | 50 | yaml.dump(curtin_ifaces, default_flow_style=False, indent=4))) | ||
2609 | 51 | |||
2610 | 52 | ip_a = self.load_collect_file("ip_a") | ||
2611 | 53 | logger.debug('ip a:\n{}'.format(ip_a)) | ||
2612 | 54 | |||
2613 | 55 | ip_a_dict = helpers.ip_a_to_dict(ip_a) | ||
2614 | 56 | logger.debug('parsed ip_a dict:\n{}'.format( | ||
2615 | 57 | yaml.dump(ip_a_dict, default_flow_style=False, indent=4))) | ||
2616 | 58 | print('parsed ip_a dict:\n{}'.format( | ||
2617 | 59 | yaml.dump(ip_a_dict, default_flow_style=False, indent=4))) | ||
2618 | 60 | |||
2619 | 61 | iface = 'interface2' | ||
2620 | 62 | self.assertTrue(iface in curtin_ifaces) | ||
2621 | 63 | |||
2622 | 64 | expected_address = curtin_ifaces[iface].get('address', None) | ||
2623 | 65 | self.assertIsNotNone(expected_address) | ||
2624 | 66 | |||
2625 | 67 | # handle CIDR notation | ||
2626 | 68 | def _nocidr(addr): | ||
2627 | 69 | return addr.split("/")[0] | ||
2628 | 70 | |||
2629 | 71 | [actual_address] = [ip.get('address') for ip in | ||
2630 | 72 | ip_a_dict[iface].get('inet4', [])] | ||
2631 | 73 | self.assertEqual(_nocidr(expected_address), _nocidr(actual_address)) | ||
2632 | 74 | |||
2633 | 75 | |||
2634 | 76 | class PreciseTestNetworkENISource(relbase.precise, TestNetworkENISource): | ||
2635 | 77 | __test__ = False | ||
2636 | 78 | # not working, still debugging though; possible older ifupdown doesn't | ||
2637 | 79 | # like the multiple iface method. | ||
2638 | 80 | |||
2639 | 81 | |||
2640 | 82 | class TrustyTestNetworkENISource(relbase.trusty, TestNetworkENISource): | ||
2641 | 83 | __test__ = True | ||
2642 | 84 | |||
2643 | 85 | |||
2644 | 86 | class XenialTestNetworkENISource(relbase.xenial, TestNetworkENISource): | ||
2645 | 87 | __test__ = True | ||
2646 | 88 | |||
2647 | 89 | |||
2648 | 90 | class YakketyTestNetworkENISource(relbase.yakkety, TestNetworkENISource): | ||
2649 | 91 | __test__ = True | ||
2650 | 0 | 92 | ||
2651 | === added file 'tests/vmtests/test_network_ipv6.py' | |||
2652 | --- tests/vmtests/test_network_ipv6.py 1970-01-01 00:00:00 +0000 | |||
2653 | +++ tests/vmtests/test_network_ipv6.py 2016-08-29 20:01:45 +0000 | |||
2654 | @@ -0,0 +1,53 @@ | |||
2655 | 1 | from .releases import base_vm_classes as relbase | ||
2656 | 2 | from .test_network import TestNetworkBaseTestsAbs | ||
2657 | 3 | |||
2658 | 4 | import textwrap | ||
2659 | 5 | |||
2660 | 6 | |||
2661 | 7 | class TestNetworkIPV6Abs(TestNetworkBaseTestsAbs): | ||
2662 | 8 | """ IPV6 complex testing. The configuration exercises | ||
2663 | 9 | - ipv4 and ipv6 address on same interface | ||
2664 | 10 | - bonding in LACP mode | ||
2665 | 11 | - unconfigured subnets on bond | ||
2666 | 12 | - vlans over bonds | ||
2667 | 13 | - all IP is static | ||
2668 | 14 | """ | ||
2669 | 15 | conf_file = "examples/network-ipv6-bond-vlan.yaml" | ||
2670 | 16 | collect_scripts = TestNetworkBaseTestsAbs.collect_scripts + [ | ||
2671 | 17 | textwrap.dedent(""" | ||
2672 | 18 | grep . -r /sys/class/net/bond0/ > sysfs_bond0 || : | ||
2673 | 19 | grep . -r /sys/class/net/bond0.108/ > sysfs_bond0.108 || : | ||
2674 | 20 | grep . -r /sys/class/net/bond0.208/ > sysfs_bond0.208 || : | ||
2675 | 21 | """)] | ||
2676 | 22 | |||
2677 | 23 | |||
2678 | 24 | class PreciseHWETTestNetwork(relbase.precise_hwe_t, TestNetworkIPV6Abs): | ||
2679 | 25 | # FIXME: off due to hang at test: Starting execute cloud user/final scripts | ||
2680 | 26 | __test__ = False | ||
2681 | 27 | |||
2682 | 28 | |||
2683 | 29 | class TrustyTestNetworkIPV6(relbase.trusty, TestNetworkIPV6Abs): | ||
2684 | 30 | __test__ = True | ||
2685 | 31 | |||
2686 | 32 | |||
2687 | 33 | class TrustyHWEUTestNetworkIPV6(relbase.trusty_hwe_u, TrustyTestNetworkIPV6): | ||
2688 | 34 | # Working, off by default to safe test suite runtime, covered by bonding | ||
2689 | 35 | __test__ = False | ||
2690 | 36 | |||
2691 | 37 | |||
2692 | 38 | class TrustyHWEVTestNetworkIPV6(relbase.trusty_hwe_v, TrustyTestNetworkIPV6): | ||
2693 | 39 | # Working, off by default to safe test suite runtime, covered by bonding | ||
2694 | 40 | __test__ = False | ||
2695 | 41 | |||
2696 | 42 | |||
2697 | 43 | class TrustyHWEWTestNetworkIPV6(relbase.trusty_hwe_w, TrustyTestNetworkIPV6): | ||
2698 | 44 | # Working, off by default to safe test suite runtime, covered by bonding | ||
2699 | 45 | __test__ = False | ||
2700 | 46 | |||
2701 | 47 | |||
2702 | 48 | class XenialTestNetworkIPV6(relbase.xenial, TestNetworkIPV6Abs): | ||
2703 | 49 | __test__ = True | ||
2704 | 50 | |||
2705 | 51 | |||
2706 | 52 | class YakketyTestNetworkIPV6(relbase.yakkety, TestNetworkIPV6Abs): | ||
2707 | 53 | __test__ = True | ||
2708 | 0 | 54 | ||
2709 | === added file 'tests/vmtests/test_network_ipv6_enisource.py' | |||
2710 | --- tests/vmtests/test_network_ipv6_enisource.py 1970-01-01 00:00:00 +0000 | |||
2711 | +++ tests/vmtests/test_network_ipv6_enisource.py 2016-08-29 20:01:45 +0000 | |||
2712 | @@ -0,0 +1,26 @@ | |||
2713 | 1 | from .releases import base_vm_classes as relbase | ||
2714 | 2 | from .test_network_enisource import TestNetworkENISource | ||
2715 | 3 | |||
2716 | 4 | |||
2717 | 5 | class TestNetworkIPV6ENISource(TestNetworkENISource): | ||
2718 | 6 | conf_file = "examples/tests/network_source_ipv6.yaml" | ||
2719 | 7 | |||
2720 | 8 | |||
2721 | 9 | class PreciseTestNetworkIPV6ENISource(relbase.precise, | ||
2722 | 10 | TestNetworkIPV6ENISource): | ||
2723 | 11 | __test__ = False | ||
2724 | 12 | # not working, still debugging though; possible older ifupdown doesn't | ||
2725 | 13 | # like the multiple iface method. | ||
2726 | 14 | |||
2727 | 15 | |||
2728 | 16 | class TrustyTestNetworkIPV6ENISource(relbase.trusty, TestNetworkIPV6ENISource): | ||
2729 | 17 | __test__ = True | ||
2730 | 18 | |||
2731 | 19 | |||
2732 | 20 | class XenialTestNetworkIPV6ENISource(relbase.xenial, TestNetworkIPV6ENISource): | ||
2733 | 21 | __test__ = True | ||
2734 | 22 | |||
2735 | 23 | |||
2736 | 24 | class YakketyTestNetworkIPV6ENISource(relbase.yakkety, | ||
2737 | 25 | TestNetworkIPV6ENISource): | ||
2738 | 26 | __test__ = True | ||
2739 | 0 | 27 | ||
2740 | === added file 'tests/vmtests/test_network_ipv6_static.py' | |||
2741 | --- tests/vmtests/test_network_ipv6_static.py 1970-01-01 00:00:00 +0000 | |||
2742 | +++ tests/vmtests/test_network_ipv6_static.py 2016-08-29 20:01:45 +0000 | |||
2743 | @@ -0,0 +1,42 @@ | |||
2744 | 1 | from .releases import base_vm_classes as relbase | ||
2745 | 2 | from .test_network_static import TestNetworkStaticAbs | ||
2746 | 3 | |||
2747 | 4 | |||
2748 | 5 | # reuse basic network tests but with different config (static, no dhcp) | ||
2749 | 6 | class TestNetworkIPV6StaticAbs(TestNetworkStaticAbs): | ||
2750 | 7 | conf_file = "examples/tests/basic_network_static_ipv6.yaml" | ||
2751 | 8 | |||
2752 | 9 | |||
2753 | 10 | class PreciseHWETTestNetworkIPV6Static(relbase.precise_hwe_t, | ||
2754 | 11 | TestNetworkIPV6StaticAbs): | ||
2755 | 12 | __test__ = True | ||
2756 | 13 | |||
2757 | 14 | |||
2758 | 15 | class TrustyTestNetworkIPV6Static(relbase.trusty, TestNetworkIPV6StaticAbs): | ||
2759 | 16 | __test__ = True | ||
2760 | 17 | |||
2761 | 18 | |||
2762 | 19 | class TrustyHWEUTestNetworkIPV6Static(relbase.trusty_hwe_u, | ||
2763 | 20 | TestNetworkIPV6StaticAbs): | ||
2764 | 21 | # unsupported kernel, 2016-08 | ||
2765 | 22 | __test__ = False | ||
2766 | 23 | |||
2767 | 24 | |||
2768 | 25 | class TrustyHWEVTestNetworkIPV6Static(relbase.trusty_hwe_v, | ||
2769 | 26 | TestNetworkIPV6StaticAbs): | ||
2770 | 27 | # unsupported kernel, 2016-08 | ||
2771 | 28 | __test__ = False | ||
2772 | 29 | |||
2773 | 30 | |||
2774 | 31 | class TrustyHWEWTestNetworkIPV6Static(relbase.trusty_hwe_w, | ||
2775 | 32 | TestNetworkIPV6StaticAbs): | ||
2776 | 33 | # unsupported kernel, 2016-08 | ||
2777 | 34 | __test__ = False | ||
2778 | 35 | |||
2779 | 36 | |||
2780 | 37 | class XenialTestNetworkIPV6Static(relbase.xenial, TestNetworkIPV6StaticAbs): | ||
2781 | 38 | __test__ = True | ||
2782 | 39 | |||
2783 | 40 | |||
2784 | 41 | class YakketyTestNetworkIPV6Static(relbase.yakkety, TestNetworkIPV6StaticAbs): | ||
2785 | 42 | __test__ = True | ||
2786 | 0 | 43 | ||
2787 | === added file 'tests/vmtests/test_network_ipv6_vlan.py' | |||
2788 | --- tests/vmtests/test_network_ipv6_vlan.py 1970-01-01 00:00:00 +0000 | |||
2789 | +++ tests/vmtests/test_network_ipv6_vlan.py 2016-08-29 20:01:45 +0000 | |||
2790 | @@ -0,0 +1,34 @@ | |||
2791 | 1 | from .releases import base_vm_classes as relbase | ||
2792 | 2 | from .test_network_vlan import TestNetworkVlanAbs | ||
2793 | 3 | |||
2794 | 4 | |||
2795 | 5 | class TestNetworkIPV6VlanAbs(TestNetworkVlanAbs): | ||
2796 | 6 | conf_file = "examples/tests/vlan_network_ipv6.yaml" | ||
2797 | 7 | |||
2798 | 8 | |||
2799 | 9 | class PreciseTestNetworkIPV6Vlan(relbase.precise, TestNetworkIPV6VlanAbs): | ||
2800 | 10 | __test__ = True | ||
2801 | 11 | |||
2802 | 12 | # precise ip -d link show output is different (of course) | ||
2803 | 13 | def test_vlan_enabled(self): | ||
2804 | 14 | |||
2805 | 15 | # we must have at least one | ||
2806 | 16 | self.assertGreaterEqual(len(self.get_vlans()), 1) | ||
2807 | 17 | |||
2808 | 18 | # did they get configured? | ||
2809 | 19 | for vlan in self.get_vlans(): | ||
2810 | 20 | link_file = "ip_link_show_" + vlan['name'] | ||
2811 | 21 | vlan_msg = "vlan id " + str(vlan['vlan_id']) | ||
2812 | 22 | self.check_file_regex(link_file, vlan_msg) | ||
2813 | 23 | |||
2814 | 24 | |||
2815 | 25 | class TrustyTestNetworkIPV6Vlan(relbase.trusty, TestNetworkIPV6VlanAbs): | ||
2816 | 26 | __test__ = True | ||
2817 | 27 | |||
2818 | 28 | |||
2819 | 29 | class XenialTestNetworkIPV6Vlan(relbase.xenial, TestNetworkIPV6VlanAbs): | ||
2820 | 30 | __test__ = True | ||
2821 | 31 | |||
2822 | 32 | |||
2823 | 33 | class YakketyTestNetworkIPV6Vlan(relbase.yakkety, TestNetworkIPV6VlanAbs): | ||
2824 | 34 | __test__ = True | ||
2825 | 0 | 35 | ||
2826 | === added file 'tests/vmtests/test_network_mtu.py' | |||
2827 | --- tests/vmtests/test_network_mtu.py 1970-01-01 00:00:00 +0000 | |||
2828 | +++ tests/vmtests/test_network_mtu.py 2016-08-29 20:01:45 +0000 | |||
2829 | @@ -0,0 +1,155 @@ | |||
2830 | 1 | from .releases import base_vm_classes as relbase | ||
2831 | 2 | from .test_network_ipv6 import TestNetworkIPV6Abs | ||
2832 | 3 | from curtin import util | ||
2833 | 4 | |||
2834 | 5 | import os | ||
2835 | 6 | import textwrap | ||
2836 | 7 | |||
2837 | 8 | |||
2838 | 9 | class TestNetworkMtuAbs(TestNetworkIPV6Abs): | ||
2839 | 10 | """ Test that the mtu of the ipv6 address is properly | ||
2840 | 11 | |||
2841 | 12 | 1. devices default MTU to 1500, test if mtu under | ||
2842 | 13 | inet6 stanza can be set separately from device | ||
2843 | 14 | mtu (works on Xenial and newer ifupdown), check | ||
2844 | 15 | via sysctl. | ||
2845 | 16 | |||
2846 | 17 | 2. if ipv6 mtu is > than underlying device, this fails | ||
2847 | 18 | and is unnoticed, ifupdown/hook should fix by changing | ||
2848 | 19 | mtu of underlying device to the same size as the ipv6 | ||
2849 | 20 | mtu | ||
2850 | 21 | |||
2851 | 22 | 3. order of the v4 vs. v6 stanzas could affect final mtu | ||
2852 | 23 | ipv6 first, then ipv4 with mtu. | ||
2853 | 24 | """ | ||
2854 | 25 | conf_file = "examples/tests/network_mtu.yaml" | ||
2855 | 26 | collect_scripts = TestNetworkIPV6Abs.collect_scripts + [textwrap.dedent(""" | ||
2856 | 27 | cd OUTPUT_COLLECT_D | ||
2857 | 28 | proc_v6="/proc/sys/net/ipv6/conf" | ||
2858 | 29 | for f in `seq 0 7`; do | ||
2859 | 30 | cat /sys/class/net/interface${f}/mtu > interface${f}_dev_mtu; | ||
2860 | 31 | cat $proc_v6/interface${f}/mtu > interface${f}_ipv6_mtu; | ||
2861 | 32 | done | ||
2862 | 33 | if [ -e /var/log/upstart ]; then | ||
2863 | 34 | cp -a /var/log/upstart ./var_log_upstart | ||
2864 | 35 | fi | ||
2865 | 36 | """)] | ||
2866 | 37 | |||
2867 | 38 | def _load_mtu_data(self, ifname): | ||
2868 | 39 | """ load mtu related files by interface name. | ||
2869 | 40 | returns a dictionary with the follwing | ||
2870 | 41 | keys: 'device', and 'ipv6'. """ | ||
2871 | 42 | |||
2872 | 43 | mtu_fn = { | ||
2873 | 44 | 'device': "%s_dev_mtu" % ifname, | ||
2874 | 45 | 'ipv6': "%s_ipv6_mtu" % ifname, | ||
2875 | 46 | } | ||
2876 | 47 | mtu_val = {} | ||
2877 | 48 | for fnk in mtu_fn.keys(): | ||
2878 | 49 | fn = os.path.join(self.td.collect, mtu_fn[fnk]) | ||
2879 | 50 | mtu_val.update({fnk: int(util.load_file(fn))}) | ||
2880 | 51 | |||
2881 | 52 | return mtu_val | ||
2882 | 53 | |||
2883 | 54 | def _check_subnet_mtu(self, subnet, iface): | ||
2884 | 55 | mtu_data = self._load_mtu_data(iface['name']) | ||
2885 | 56 | print('subnet:%s' % subnet) | ||
2886 | 57 | print('mtu_data:%s' % mtu_data) | ||
2887 | 58 | # ipv4 address mtu changes *device* mtu | ||
2888 | 59 | if '.' in subnet['address']: | ||
2889 | 60 | print('subnet_mtu=%s device_mtu=%s' % (int(subnet['mtu']), | ||
2890 | 61 | int(mtu_data['device']))) | ||
2891 | 62 | self.assertEqual(int(subnet['mtu']), | ||
2892 | 63 | int(mtu_data['device'])) | ||
2893 | 64 | # ipv6 address mtu changes *protocol* mtu | ||
2894 | 65 | elif ':' in subnet['address']: | ||
2895 | 66 | print('subnet_mtu=%s ipv6_mtu=%s' % (int(subnet['mtu']), | ||
2896 | 67 | int(mtu_data['device']))) | ||
2897 | 68 | self.assertEqual(int(subnet['mtu']), | ||
2898 | 69 | int(mtu_data['ipv6'])) | ||
2899 | 70 | |||
2900 | 71 | def _check_iface_subnets(self, ifname): | ||
2901 | 72 | network_state = self.get_network_state() | ||
2902 | 73 | interfaces = network_state.get('interfaces') | ||
2903 | 74 | |||
2904 | 75 | iface = interfaces.get(ifname) | ||
2905 | 76 | subnets = iface.get('subnets') | ||
2906 | 77 | print('iface=%s subnets=%s' % (iface['name'], subnets)) | ||
2907 | 78 | for subnet in subnets: | ||
2908 | 79 | if 'mtu' in subnet: | ||
2909 | 80 | self._check_subnet_mtu(subnet, iface) | ||
2910 | 81 | |||
2911 | 82 | def _disabled_ipv4_and_ipv6_mtu_all(self): | ||
2912 | 83 | """ we don't pass all tests, skip for now """ | ||
2913 | 84 | network_state = self.get_network_state() | ||
2914 | 85 | interfaces = network_state.get('interfaces') | ||
2915 | 86 | |||
2916 | 87 | for iface in interfaces.values(): | ||
2917 | 88 | subnets = iface.get('subnets', {}) | ||
2918 | 89 | if subnets: | ||
2919 | 90 | for index, subnet in zip(range(0, len(subnets)), subnets): | ||
2920 | 91 | print("iface=%s subnet=%s" % (iface['name'], subnet)) | ||
2921 | 92 | if 'mtu' in subnet: | ||
2922 | 93 | self._check_subnet_mtu(subnet, iface) | ||
2923 | 94 | |||
2924 | 95 | def test_ipv6_mtu_smaller_than_ipv4_non_default(self): | ||
2925 | 96 | self._check_iface_subnets('interface0') | ||
2926 | 97 | |||
2927 | 98 | def test_ipv6_mtu_equal_ipv4_non_default(self): | ||
2928 | 99 | self._check_iface_subnets('interface1') | ||
2929 | 100 | |||
2930 | 101 | def test_ipv6_mtu_higher_than_default_no_ipv4_mtu(self): | ||
2931 | 102 | self._check_iface_subnets('interface2') | ||
2932 | 103 | |||
2933 | 104 | def test_ipv6_mtu_higher_than_default_no_ipv4_iface_up(self): | ||
2934 | 105 | self._check_iface_subnets('interface3') | ||
2935 | 106 | |||
2936 | 107 | def test_ipv6_mtu_smaller_than_ipv4_v6_iface_first(self): | ||
2937 | 108 | self._check_iface_subnets('interface4') | ||
2938 | 109 | |||
2939 | 110 | def test_ipv6_mtu_equal_ipv4_non_default_v6_iface_first(self): | ||
2940 | 111 | self._check_iface_subnets('interface5') | ||
2941 | 112 | |||
2942 | 113 | def test_ipv6_mtu_higher_than_default_no_ipv4_mtu_v6_iface_first(self): | ||
2943 | 114 | self._check_iface_subnets('interface6') | ||
2944 | 115 | |||
2945 | 116 | def test_ipv6_mtu_higher_than_default_no_ipv4_iface_v6_iface_first(self): | ||
2946 | 117 | self._check_iface_subnets('interface7') | ||
2947 | 118 | |||
2948 | 119 | |||
2949 | 120 | class PreciseHWETTestNetworkMtu(relbase.precise_hwe_t, TestNetworkMtuAbs): | ||
2950 | 121 | # FIXME: Precise mtu / ipv6 is buggy | ||
2951 | 122 | __test__ = False | ||
2952 | 123 | |||
2953 | 124 | |||
2954 | 125 | class TrustyTestNetworkMtu(relbase.trusty, TestNetworkMtuAbs): | ||
2955 | 126 | __test__ = True | ||
2956 | 127 | |||
2957 | 128 | # FIXME: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=809714 | ||
2958 | 129 | # fixed in newer ifupdown than is in trusty | ||
2959 | 130 | def test_ipv6_mtu_smaller_than_ipv4_non_default(self): | ||
2960 | 131 | # trusty ifupdown uses device mtu to change v6 mtu | ||
2961 | 132 | pass | ||
2962 | 133 | |||
2963 | 134 | |||
2964 | 135 | class TrustyHWEUTestNetworkMtu(relbase.trusty_hwe_u, TrustyTestNetworkMtu): | ||
2965 | 136 | # unsupported kernel, 2016-08 | ||
2966 | 137 | __test__ = False | ||
2967 | 138 | |||
2968 | 139 | |||
2969 | 140 | class TrustyHWEVTestNetworkMtu(relbase.trusty_hwe_v, TrustyTestNetworkMtu): | ||
2970 | 141 | # unsupported kernel, 2016-08 | ||
2971 | 142 | __test__ = False | ||
2972 | 143 | |||
2973 | 144 | |||
2974 | 145 | class TrustyHWEWTestNetworkMtu(relbase.trusty_hwe_w, TrustyTestNetworkMtu): | ||
2975 | 146 | # unsupported kernel, 2016-08 | ||
2976 | 147 | __test__ = False | ||
2977 | 148 | |||
2978 | 149 | |||
2979 | 150 | class XenialTestNetworkMtu(relbase.xenial, TestNetworkMtuAbs): | ||
2980 | 151 | __test__ = True | ||
2981 | 152 | |||
2982 | 153 | |||
2983 | 154 | class YakketyTestNetworkMtu(relbase.yakkety, TestNetworkMtuAbs): | ||
2984 | 155 | __test__ = True | ||
2985 | 0 | 156 | ||
2986 | === added file 'tests/vmtests/test_network_static.py' | |||
2987 | --- tests/vmtests/test_network_static.py 1970-01-01 00:00:00 +0000 | |||
2988 | +++ tests/vmtests/test_network_static.py 2016-08-29 20:01:45 +0000 | |||
2989 | @@ -0,0 +1,44 @@ | |||
2990 | 1 | from .releases import base_vm_classes as relbase | ||
2991 | 2 | from .test_network import TestNetworkBaseTestsAbs | ||
2992 | 3 | |||
2993 | 4 | |||
2994 | 5 | class TestNetworkStaticAbs(TestNetworkBaseTestsAbs): | ||
2995 | 6 | """ Static network testing with ipv4 | ||
2996 | 7 | """ | ||
2997 | 8 | conf_file = "examples/tests/basic_network_static.yaml" | ||
2998 | 9 | |||
2999 | 10 | |||
3000 | 11 | class PreciseHWETTestNetworkStatic(relbase.precise_hwe_t, | ||
3001 | 12 | TestNetworkStaticAbs): | ||
3002 | 13 | # FIXME: off due to hang at test: Starting execute cloud user/final scripts | ||
3003 | 14 | __test__ = False | ||
3004 | 15 | |||
3005 | 16 | |||
3006 | 17 | class TrustyTestNetworkStatic(relbase.trusty, TestNetworkStaticAbs): | ||
3007 | 18 | __test__ = True | ||
3008 | 19 | |||
3009 | 20 | |||
3010 | 21 | class TrustyHWEUTestNetworkStatic(relbase.trusty_hwe_u, | ||
3011 | 22 | TrustyTestNetworkStatic): | ||
3012 | 23 | # Working, off by default to safe test suite runtime, covered by bonding | ||
3013 | 24 | __test__ = False | ||
3014 | 25 | |||
3015 | 26 | |||
3016 | 27 | class TrustyHWEVTestNetworkStatic(relbase.trusty_hwe_v, | ||
3017 | 28 | TrustyTestNetworkStatic): | ||
3018 | 29 | # Working, off by default to safe test suite runtime, covered by bonding | ||
3019 | 30 | __test__ = False | ||
3020 | 31 | |||
3021 | 32 | |||
3022 | 33 | class TrustyHWEWTestNetworkStatic(relbase.trusty_hwe_w, | ||
3023 | 34 | TrustyTestNetworkStatic): | ||
3024 | 35 | # Working, off by default to safe test suite runtime, covered by bonding | ||
3025 | 36 | __test__ = False | ||
3026 | 37 | |||
3027 | 38 | |||
3028 | 39 | class XenialTestNetworkStatic(relbase.xenial, TestNetworkStaticAbs): | ||
3029 | 40 | __test__ = True | ||
3030 | 41 | |||
3031 | 42 | |||
3032 | 43 | class YakketyTestNetworkStatic(relbase.yakkety, TestNetworkStaticAbs): | ||
3033 | 44 | __test__ = True | ||
3034 | 0 | 45 | ||
3035 | === added file 'tests/vmtests/test_network_vlan.py' | |||
3036 | --- tests/vmtests/test_network_vlan.py 1970-01-01 00:00:00 +0000 | |||
3037 | +++ tests/vmtests/test_network_vlan.py 2016-08-29 20:01:45 +0000 | |||
3038 | @@ -0,0 +1,77 @@ | |||
3039 | 1 | from . import logger | ||
3040 | 2 | from .releases import base_vm_classes as relbase | ||
3041 | 3 | from .test_network import TestNetworkBaseTestsAbs | ||
3042 | 4 | |||
3043 | 5 | import textwrap | ||
3044 | 6 | import yaml | ||
3045 | 7 | |||
3046 | 8 | |||
3047 | 9 | class TestNetworkVlanAbs(TestNetworkBaseTestsAbs): | ||
3048 | 10 | conf_file = "examples/tests/vlan_network.yaml" | ||
3049 | 11 | collect_scripts = TestNetworkBaseTestsAbs.collect_scripts + [ | ||
3050 | 12 | textwrap.dedent(""" | ||
3051 | 13 | cd OUTPUT_COLLECT_D | ||
3052 | 14 | dpkg-query -W -f '${Status}' vlan > vlan_installed | ||
3053 | 15 | ip -d link show interface1.2667 > ip_link_show_interface1.2667 | ||
3054 | 16 | ip -d link show interface1.2668 > ip_link_show_interface1.2668 | ||
3055 | 17 | ip -d link show interface1.2669 > ip_link_show_interface1.2669 | ||
3056 | 18 | ip -d link show interface1.2670 > ip_link_show_interface1.2670 | ||
3057 | 19 | """)] | ||
3058 | 20 | |||
3059 | 21 | def get_vlans(self): | ||
3060 | 22 | network_state = self.get_network_state() | ||
3061 | 23 | logger.debug('get_vlans ns:\n%s', yaml.dump(network_state, | ||
3062 | 24 | default_flow_style=False, | ||
3063 | 25 | indent=4)) | ||
3064 | 26 | interfaces = network_state.get('interfaces') | ||
3065 | 27 | return [iface for iface in interfaces.values() | ||
3066 | 28 | if iface['type'] == 'vlan'] | ||
3067 | 29 | |||
3068 | 30 | def test_output_files_exist_vlan(self): | ||
3069 | 31 | link_files = ["ip_link_show_%s" % vlan['name'] | ||
3070 | 32 | for vlan in self.get_vlans()] | ||
3071 | 33 | self.output_files_exist(["vlan_installed"] + link_files) | ||
3072 | 34 | |||
3073 | 35 | def test_vlan_installed(self): | ||
3074 | 36 | status = self.load_collect_file("vlan_installed").strip() | ||
3075 | 37 | logger.debug('vlan installed?: %s', status) | ||
3076 | 38 | self.assertEqual('install ok installed', status) | ||
3077 | 39 | |||
3078 | 40 | def test_vlan_enabled(self): | ||
3079 | 41 | |||
3080 | 42 | # we must have at least one | ||
3081 | 43 | self.assertGreaterEqual(len(self.get_vlans()), 1) | ||
3082 | 44 | |||
3083 | 45 | # did they get configured? | ||
3084 | 46 | for vlan in self.get_vlans(): | ||
3085 | 47 | link_file = "ip_link_show_" + vlan['name'] | ||
3086 | 48 | vlan_msg = "vlan protocol 802.1Q id " + str(vlan['vlan_id']) | ||
3087 | 49 | self.check_file_regex(link_file, vlan_msg) | ||
3088 | 50 | |||
3089 | 51 | |||
3090 | 52 | class PreciseTestNetworkVlan(relbase.precise, TestNetworkVlanAbs): | ||
3091 | 53 | __test__ = True | ||
3092 | 54 | |||
3093 | 55 | # precise ip -d link show output is different (of course) | ||
3094 | 56 | def test_vlan_enabled(self): | ||
3095 | 57 | |||
3096 | 58 | # we must have at least one | ||
3097 | 59 | self.assertGreaterEqual(len(self.get_vlans()), 1) | ||
3098 | 60 | |||
3099 | 61 | # did they get configured? | ||
3100 | 62 | for vlan in self.get_vlans(): | ||
3101 | 63 | link_file = "ip_link_show_" + vlan['name'] | ||
3102 | 64 | vlan_msg = "vlan id " + str(vlan['vlan_id']) | ||
3103 | 65 | self.check_file_regex(link_file, vlan_msg) | ||
3104 | 66 | |||
3105 | 67 | |||
3106 | 68 | class TrustyTestNetworkVlan(relbase.trusty, TestNetworkVlanAbs): | ||
3107 | 69 | __test__ = True | ||
3108 | 70 | |||
3109 | 71 | |||
3110 | 72 | class XenialTestNetworkVlan(relbase.xenial, TestNetworkVlanAbs): | ||
3111 | 73 | __test__ = True | ||
3112 | 74 | |||
3113 | 75 | |||
3114 | 76 | class YakketyTestNetworkVlan(relbase.yakkety, TestNetworkVlanAbs): | ||
3115 | 77 | __test__ = True | ||
3116 | 0 | 78 | ||
3117 | === modified file 'tests/vmtests/test_raid5_bcache.py' | |||
3118 | --- tests/vmtests/test_raid5_bcache.py 2016-06-13 20:49:15 +0000 | |||
3119 | +++ tests/vmtests/test_raid5_bcache.py 2016-08-29 20:01:45 +0000 | |||
3120 | @@ -2,7 +2,6 @@ | |||
3121 | 2 | from .releases import base_vm_classes as relbase | 2 | from .releases import base_vm_classes as relbase |
3122 | 3 | 3 | ||
3123 | 4 | import textwrap | 4 | import textwrap |
3124 | 5 | import os | ||
3125 | 6 | 5 | ||
3126 | 7 | 6 | ||
3127 | 8 | class TestMdadmAbs(VMBaseClass): | 7 | class TestMdadmAbs(VMBaseClass): |
3128 | @@ -55,14 +54,12 @@ | |||
3129 | 55 | 54 | ||
3130 | 56 | def test_bcache_status(self): | 55 | def test_bcache_status(self): |
3131 | 57 | bcache_cset_uuid = None | 56 | bcache_cset_uuid = None |
3137 | 58 | fname = os.path.join(self.td.collect, "bcache_super_vda2") | 57 | for line in self.load_collect_file("bcache_super_vda2").splitlines(): |
3138 | 59 | with open(fname, "r") as fp: | 58 | if line != "" and line.split()[0] == "cset.uuid": |
3139 | 60 | for line in fp.read().splitlines(): | 59 | bcache_cset_uuid = line.split()[-1].rstrip() |
3135 | 61 | if line != "" and line.split()[0] == "cset.uuid": | ||
3136 | 62 | bcache_cset_uuid = line.split()[-1].rstrip() | ||
3140 | 63 | self.assertIsNotNone(bcache_cset_uuid) | 60 | self.assertIsNotNone(bcache_cset_uuid) |
3143 | 64 | with open(os.path.join(self.td.collect, "bcache_ls"), "r") as fp: | 61 | self.assertTrue(bcache_cset_uuid in |
3144 | 65 | self.assertTrue(bcache_cset_uuid in fp.read().splitlines()) | 62 | self.load_collect_file("bcache_ls").splitlines()) |
3145 | 66 | 63 | ||
3146 | 67 | def test_bcache_cachemode(self): | 64 | def test_bcache_cachemode(self): |
3147 | 68 | self.check_file_regex("bcache_cache_mode", r"\[writeback\]") | 65 | self.check_file_regex("bcache_cache_mode", r"\[writeback\]") |
3148 | 69 | 66 | ||
3149 | === modified file 'tests/vmtests/test_uefi_basic.py' | |||
3150 | --- tests/vmtests/test_uefi_basic.py 2016-08-09 15:01:07 +0000 | |||
3151 | +++ tests/vmtests/test_uefi_basic.py 2016-08-29 20:01:45 +0000 | |||
3152 | @@ -2,7 +2,6 @@ | |||
3153 | 2 | 2 | ||
3154 | 3 | from .releases import base_vm_classes as relbase | 3 | from .releases import base_vm_classes as relbase |
3155 | 4 | 4 | ||
3156 | 5 | import os | ||
3157 | 6 | import textwrap | 5 | import textwrap |
3158 | 7 | 6 | ||
3159 | 8 | 7 | ||
3160 | @@ -40,7 +39,7 @@ | |||
3161 | 40 | "proc_partitions"]) | 39 | "proc_partitions"]) |
3162 | 41 | 40 | ||
3163 | 42 | def test_sys_firmware_efi(self): | 41 | def test_sys_firmware_efi(self): |
3165 | 43 | sys_efi_expected = [ | 42 | sys_efi_possible = [ |
3166 | 44 | 'config_table', | 43 | 'config_table', |
3167 | 45 | 'efivars', | 44 | 'efivars', |
3168 | 46 | 'fw_platform_size', | 45 | 'fw_platform_size', |
3169 | @@ -50,22 +49,20 @@ | |||
3170 | 50 | 'systab', | 49 | 'systab', |
3171 | 51 | 'vars', | 50 | 'vars', |
3172 | 52 | ] | 51 | ] |
3179 | 53 | sys_efi = self.td.collect + "ls_sys_firmware_efi" | 52 | efi_lines = self.load_collect_file( |
3180 | 54 | if (os.path.exists(sys_efi)): | 53 | "ls_sys_firmware_efi").strip().split('\n') |
3181 | 55 | with open(sys_efi) as fp: | 54 | |
3182 | 56 | efi_lines = fp.read().strip().split('\n') | 55 | # sys/firmware/efi contents differ based on kernel and configuration |
3183 | 57 | self.assertEqual(sorted(sys_efi_expected), | 56 | for efi_line in efi_lines: |
3184 | 58 | sorted(efi_lines)) | 57 | self.assertIn(efi_line, sys_efi_possible) |
3185 | 59 | 58 | ||
3186 | 60 | def test_disk_block_sizes(self): | 59 | def test_disk_block_sizes(self): |
3187 | 61 | """ Test disk logical and physical block size are match | 60 | """ Test disk logical and physical block size are match |
3188 | 62 | the class block size. | 61 | the class block size. |
3189 | 63 | """ | 62 | """ |
3190 | 64 | for bs in ['lbs', 'pbs']: | 63 | for bs in ['lbs', 'pbs']: |
3195 | 65 | with open(os.path.join(self.td.collect, | 64 | size = int(self.load_collect_file('vda_' + bs)) |
3196 | 66 | 'vda_' + bs), 'r') as fp: | 65 | self.assertEqual(self.disk_block_size, size) |
3193 | 67 | size = int(fp.read()) | ||
3194 | 68 | self.assertEqual(self.disk_block_size, size) | ||
3197 | 69 | 66 | ||
3198 | 70 | def test_disk_block_size_with_blockdev(self): | 67 | def test_disk_block_size_with_blockdev(self): |
3199 | 71 | """ validate maas setting | 68 | """ validate maas setting |
3200 | @@ -75,10 +72,8 @@ | |||
3201 | 75 | --getbsz get blocksize | 72 | --getbsz get blocksize |
3202 | 76 | """ | 73 | """ |
3203 | 77 | for syscall in ['getss', 'getpbsz']: | 74 | for syscall in ['getss', 'getpbsz']: |
3208 | 78 | with open(os.path.join(self.td.collect, | 75 | size = int(self.load_collect_file('vda_blockdev_' + syscall)) |
3209 | 79 | 'vda_blockdev_' + syscall), 'r') as fp: | 76 | self.assertEqual(self.disk_block_size, size) |
3206 | 80 | size = int(fp.read()) | ||
3207 | 81 | self.assertEqual(self.disk_block_size, size) | ||
3210 | 82 | 77 | ||
3211 | 83 | 78 | ||
3212 | 84 | class PreciseUefiTestBasic(relbase.precise, TestBasicAbs): | 79 | class PreciseUefiTestBasic(relbase.precise, TestBasicAbs): |
PASSED: Continuous integration, rev:436 /jenkins. ubuntu. com/server/ job/curtin- ci/250/ /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= amd64/250 /jenkins. ubuntu. com/server/ job/curtin- ci/nodes= ppc64el/ 250
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild: /jenkins. ubuntu. com/server/ job/curtin- ci/250/ rebuild
https:/