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