Merge lp:~raharper/curtin/trunk.more-ipv6 into lp:~curtin-dev/curtin/trunk

Proposed by Ryan Harper
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
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/interfaces for different releases (precise -> yakkety)
ifupdown has subtle issues with various networking features and curtin needs to ensure
consistent behavior.

- Propery handle emitting the 'auto' control tag for stacked interfaces, like vlans over bonds
- Workaround LP:1609367 by rendering ifupdown hooks to handle the various cases. This works generically in all ubuntu releases
- 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 TestNetworkTestBaseAbs, all varients testing network inherit from this class and override only the config file and any special case test-cases and file collection
- Global replace of testcase use of 'with open' and instead use load_collect_file()
- 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/interfaces for
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
  TestNetworkTestBaseAbs, all variants testing network inherit from this
  class and override only the config file and any special case test-cases
  and file collection
- Global replace of testcase use of 'with open' and instead use
  load_collect_file()
- Add ip_a_to_dict parser for `/sbin/ip a` output

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
lp:~raharper/curtin/trunk.more-ipv6 updated
437. By Ryan Harper

merge from trunk

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Christian Ehrhardt  (paelzer) wrote :

Hi Ryan,
great work - a few minor comments and discussion points added inline.

Revision history for this message
Ryan Harper (raharper) wrote :
Download full text (12.9 KiB)

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/commands/apply_net.py'
> > --- curtin/commands/apply_net.py 2016-07-13 07:50:49 +0000
> > +++ curtin/commands/apply_net.py 2016-08-25 23:32:25 +0000
> > @@ -26,6 +26,54 @@
> >
> > LOG = log.LOG
> >
> > +IFUPDOWN_IPV6_MTU_PRE_HOOK = """#!/bin/sh -x
>
> 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_MTU=$(/bin/cat /sys/class/net/$IFACE/mtu ||:)
> > +CUR_IPV6_MTU=$(sysctl -n net.ipv6.conf.$IFACE.mtu ||:)
> > +[ -n "$CUR_DEV_MTU" ] && echo $CUR_DEV_MTU > /run/network/$IFACE_dev.mtu
> > +[ -n "$CUR_IPV6_MTU" ] && echo $CUR_IPV6_MTU >
> /run/network/$IFACE_ipv6.mtu
> > +exit 0
> > +"""
> > +
> > +IFUPDOWN_IPV6_MTU_POST_HOOK = """#!/bin/sh -x
> > +# injected by curtin installer
> > +
> > +[ "$IFACE" != "lo" ] || exit 0
> > +
> > +# Trigger only if MTU configured
> > +[ -n "$IF_MTU" ] || exit 0
> > +
> > +PRE_DEV_MTU=$(cat /run/network/$IFACE_dev.mtu)
> > +CUR_DEV_MTU=$(/bin/cat /sys/class/net/$IFACE/mtu)
> > +PRE_IPV6_MTU=$(cat /run/network/$IFACE_ipv6.mtu)
>
> 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_MTU=$(sysctl -n net.ipv6.conf.$IFACE.mtu)
> > +
> > +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.conf.$IFACE.mtu=$IF_MTU
>
> 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.conf.$IFACE.mtu=$PRE_IPV6_MTU
> > + fi
> > +fi
> > +exit 0
> > +"""
> > +
> >
> > ...

Revision history for this message
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.

Revision history for this message
Ryan Harper (raharper) wrote :
Download full text (15.1 KiB)

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/commands/apply_net.py'
> > --- curtin/commands/apply_net.py 2016-07-13 07:50:49 +0000
> > +++ curtin/commands/apply_net.py 2016-08-25 23:32:25 +0000
> > @@ -26,6 +26,54 @@
> >
> > LOG = log.LOG
> >
> > +IFUPDOWN_IPV6_MTU_PRE_HOOK = """#!/bin/sh -x
>
> 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_MTU=$(/bin/cat /sys/class/net/$IFACE/mtu ||:)
>
> [ ! -e /sys/class/net/$IFACE/mtu ] || read CUR_DEV_MTU
> /sys/class/net/$IFACE/mtu
>

Nice, will change.

>
> > +CUR_IPV6_MTU=$(sysctl -n net.ipv6.conf.$IFACE.mtu ||:)
> > +[ -n "$CUR_DEV_MTU" ] && echo $CUR_DEV_MTU > /run/network/$IFACE_dev.mtu
> > +[ -n "$CUR_IPV6_MTU" ] && echo $CUR_IPV6_MTU >
> /run/network/$IFACE_ipv6.mtu
> > +exit 0
> > +"""
> > +
> > +IFUPDOWN_IPV6_MTU_POST_HOOK = """#!/bin/sh -x
> > +# injected by curtin installer
> > +
> > +[ "$IFACE" != "lo" ] || exit 0
> > +
> > +# Trigger only if MTU configured
> > +[ -n "$IF_MTU" ] || exit 0
> > +
> > +PRE_DEV_MTU=$(cat /run/network/$IFACE_dev.mtu)
>
> +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_MTU=$(/bin/cat /sys/class/net/$IFACE/mtu)
>
> read CUR_DEV_MTU < /sys/class/net/$IFACE/mtu
> 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/$IFACE_ipv6.mtu)
> > +CUR_IPV6_MTU=$(sysctl -n net.ipv6.conf.$IFACE.mtu)
>
> 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.conf.$IFACE.mtu=$IF_MTU
>
> 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 /...

lp:~raharper/curtin/trunk.more-ipv6 updated
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.debug

curtin.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't

tests.vmtests.helpers
- drop unused ifconfig-a parsing
- update ip_a_to_dict comment to display parsed output

tests.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

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
lp:~raharper/curtin/trunk.more-ipv6 updated
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

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
lp:~raharper/curtin/trunk.more-ipv6 updated
444. By Ryan Harper

merge from trunk

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
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.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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):

Subscribers

People subscribed via source and target branches