Merge ~chad.smith/cloud-init:ubuntu/devel into cloud-init:ubuntu/devel

Proposed by Chad Smith
Status: Merged
Merged at revision: baebe101f4d5df42a9d82e810dd6423fe7709219
Proposed branch: ~chad.smith/cloud-init:ubuntu/devel
Merge into: cloud-init:ubuntu/devel
Diff against target: 799 lines (+496/-39)
14 files modified
bash_completion/cloud-init (+5/-2)
cloudinit/cmd/devel/net_convert.py (+23/-12)
cloudinit/cmd/devel/parser.py (+13/-7)
cloudinit/net/eni.py (+9/-2)
cloudinit/net/netplan.py (+4/-0)
cloudinit/sources/DataSourceOpenNebula.py (+1/-1)
config/cloud.cfg.tmpl (+0/-2)
debian/changelog (+14/-0)
doc/rtd/topics/debugging.rst (+1/-1)
tests/unittests/test_cli.py (+1/-2)
tests/unittests/test_datasource/test_opennebula.py (+406/-2)
tests/unittests/test_net.py (+6/-0)
tools/Z99-cloud-locale-test.sh (+8/-5)
tools/Z99-cloudinit-warnings.sh (+5/-3)
Reviewer Review Type Date Requested Status
Scott Moser Approve
Server Team CI bot continuous-integration Approve
Review via email: mp+352825@code.launchpad.net

Commit message

new-upstream-snapshot of cloud-init tip for release into cosmic

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

PASSED: Continuous integration, rev:baebe101f4d5df42a9d82e810dd6423fe7709219
https://jenkins.ubuntu.com/server/job/cloud-init-ci/213/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: MAAS Compatability Testing
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/213/rebuild

review: Approve (continuous-integration)
Revision history for this message
Scott Moser (smoser) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/bash_completion/cloud-init b/bash_completion/cloud-init
2index 581432c..f38164b 100644
3--- a/bash_completion/cloud-init
4+++ b/bash_completion/cloud-init
5@@ -28,7 +28,7 @@ _cloudinit_complete()
6 COMPREPLY=($(compgen -W "--help --tarfile --include-userdata" -- $cur_word))
7 ;;
8 devel)
9- COMPREPLY=($(compgen -W "--help schema" -- $cur_word))
10+ COMPREPLY=($(compgen -W "--help schema net-convert" -- $cur_word))
11 ;;
12 dhclient-hook|features)
13 COMPREPLY=($(compgen -W "--help" -- $cur_word))
14@@ -59,6 +59,9 @@ _cloudinit_complete()
15 --frequency)
16 COMPREPLY=($(compgen -W "--help instance always once" -- $cur_word))
17 ;;
18+ net-convert)
19+ COMPREPLY=($(compgen -W "--help --network-data --kind --directory --output-kind" -- $cur_word))
20+ ;;
21 schema)
22 COMPREPLY=($(compgen -W "--help --config-file --doc --annotate" -- $cur_word))
23 ;;
24@@ -74,4 +77,4 @@ _cloudinit_complete()
25 }
26 complete -F _cloudinit_complete cloud-init
27
28-# vi: syntax=bash expandtab
29+# vi: syntax=sh expandtab
30diff --git a/tools/net-convert.py b/cloudinit/cmd/devel/net_convert.py
31index d1a4a64..1ec08a3 100755
32--- a/tools/net-convert.py
33+++ b/cloudinit/cmd/devel/net_convert.py
34@@ -1,6 +1,6 @@
35-#!/usr/bin/python3
36 # This file is part of cloud-init. See LICENSE file for license information.
37
38+"""Debug network config format conversions."""
39 import argparse
40 import json
41 import os
42@@ -9,18 +9,25 @@ import yaml
43
44 from cloudinit.sources.helpers import openstack
45
46-from cloudinit.net import eni
47+from cloudinit.net import eni, netplan, network_state, sysconfig
48 from cloudinit import log
49-from cloudinit.net import netplan
50-from cloudinit.net import network_state
51-from cloudinit.net import sysconfig
52
53+NAME = 'net-convert'
54
55-def main():
56- parser = argparse.ArgumentParser()
57- parser.add_argument("--network-data", "-p", type=open,
58+
59+def get_parser(parser=None):
60+ """Build or extend and arg parser for net-convert utility.
61+
62+ @param parser: Optional existing ArgumentParser instance representing the
63+ subcommand which will be extended to support the args of this utility.
64+
65+ @returns: ArgumentParser with proper argument configuration.
66+ """
67+ if not parser:
68+ parser = argparse.ArgumentParser(prog=NAME, description=__doc__)
69+ parser.add_argument("-p", "--network-data", type=open,
70 metavar="PATH", required=True)
71- parser.add_argument("--kind", "-k",
72+ parser.add_argument("-k", "--kind",
73 choices=['eni', 'network_data.json', 'yaml'],
74 required=True)
75 parser.add_argument("-d", "--directory",
76@@ -33,11 +40,13 @@ def main():
77 help="interface name to mac mapping")
78 parser.add_argument("--debug", action='store_true',
79 help='enable debug logging to stderr.')
80- parser.add_argument("--output-kind", "-ok",
81+ parser.add_argument("-O", "--output-kind",
82 choices=['eni', 'netplan', 'sysconfig'],
83 required=True)
84- args = parser.parse_args()
85+ return parser
86
87+
88+def handle_args(name, args):
89 if not args.directory.endswith("/"):
90 args.directory += "/"
91
92@@ -99,6 +108,8 @@ def main():
93
94
95 if __name__ == '__main__':
96- main()
97+ args = get_parser().parse_args()
98+ handle_args(NAME, args)
99+
100
101 # vi: ts=4 expandtab
102diff --git a/cloudinit/cmd/devel/parser.py b/cloudinit/cmd/devel/parser.py
103index acacc4e..40a4b01 100644
104--- a/cloudinit/cmd/devel/parser.py
105+++ b/cloudinit/cmd/devel/parser.py
106@@ -5,8 +5,9 @@
107 """Define 'devel' subcommand argument parsers to include in cloud-init cmd."""
108
109 import argparse
110-from cloudinit.config.schema import (
111- get_parser as schema_parser, handle_schema_args)
112+from cloudinit.config import schema
113+
114+from . import net_convert
115
116
117 def get_parser(parser=None):
118@@ -17,10 +18,15 @@ def get_parser(parser=None):
119 subparsers = parser.add_subparsers(title='Subcommands', dest='subcommand')
120 subparsers.required = True
121
122- parser_schema = subparsers.add_parser(
123- 'schema', help='Validate cloud-config files or document schema')
124- # Construct schema subcommand parser
125- schema_parser(parser_schema)
126- parser_schema.set_defaults(action=('schema', handle_schema_args))
127+ subcmds = [
128+ ('schema', 'Validate cloud-config files for document schema',
129+ schema.get_parser, schema.handle_schema_args),
130+ (net_convert.NAME, net_convert.__doc__,
131+ net_convert.get_parser, net_convert.handle_args)
132+ ]
133+ for (subcmd, helpmsg, get_parser, handler) in subcmds:
134+ parser = subparsers.add_parser(subcmd, help=helpmsg)
135+ get_parser(parser)
136+ parser.set_defaults(action=(subcmd, handler))
137
138 return parser
139diff --git a/cloudinit/net/eni.py b/cloudinit/net/eni.py
140index bd20a36..80be242 100644
141--- a/cloudinit/net/eni.py
142+++ b/cloudinit/net/eni.py
143@@ -247,8 +247,15 @@ def _parse_deb_config_data(ifaces, contents, src_dir, src_path):
144 ifaces[currif]['bridge']['ports'] = []
145 for iface in split[1:]:
146 ifaces[currif]['bridge']['ports'].append(iface)
147- elif option == "bridge_hw" and split[1].lower() == "mac":
148- ifaces[currif]['bridge']['mac'] = split[2]
149+ elif option == "bridge_hw":
150+ # doc is confusing and thus some may put literal 'MAC'
151+ # bridge_hw MAC <address>
152+ # but correct is:
153+ # bridge_hw <address>
154+ if split[1].lower() == "mac":
155+ ifaces[currif]['bridge']['mac'] = split[2]
156+ else:
157+ ifaces[currif]['bridge']['mac'] = split[1]
158 elif option == "bridge_pathcost":
159 if 'pathcost' not in ifaces[currif]['bridge']:
160 ifaces[currif]['bridge']['pathcost'] = {}
161diff --git a/cloudinit/net/netplan.py b/cloudinit/net/netplan.py
162index 4014363..6352e78 100644
163--- a/cloudinit/net/netplan.py
164+++ b/cloudinit/net/netplan.py
165@@ -291,6 +291,8 @@ class Renderer(renderer.Renderer):
166
167 if len(bond_config) > 0:
168 bond.update({'parameters': bond_config})
169+ if ifcfg.get('mac_address'):
170+ bond['macaddress'] = ifcfg.get('mac_address').lower()
171 slave_interfaces = ifcfg.get('bond-slaves')
172 if slave_interfaces == 'none':
173 _extract_bond_slaves_by_name(interfaces, bond, ifname)
174@@ -327,6 +329,8 @@ class Renderer(renderer.Renderer):
175
176 if len(br_config) > 0:
177 bridge.update({'parameters': br_config})
178+ if ifcfg.get('mac_address'):
179+ bridge['macaddress'] = ifcfg.get('mac_address').lower()
180 _extract_addresses(ifcfg, bridge, ifname)
181 bridges.update({ifname: bridge})
182
183diff --git a/cloudinit/sources/DataSourceOpenNebula.py b/cloudinit/sources/DataSourceOpenNebula.py
184index 16c1078..77ccd12 100644
185--- a/cloudinit/sources/DataSourceOpenNebula.py
186+++ b/cloudinit/sources/DataSourceOpenNebula.py
187@@ -232,7 +232,7 @@ class OpenNebulaNetwork(object):
188
189 # Set IPv6 default gateway
190 gateway6 = self.get_gateway6(c_dev)
191- if gateway:
192+ if gateway6:
193 devconf['gateway6'] = gateway6
194
195 # Set DNS servers and search domains
196diff --git a/config/cloud.cfg.tmpl b/config/cloud.cfg.tmpl
197index 5619de3..1fef133 100644
198--- a/config/cloud.cfg.tmpl
199+++ b/config/cloud.cfg.tmpl
200@@ -24,8 +24,6 @@ disable_root: true
201 {% if variant in ["centos", "fedora", "rhel"] %}
202 mount_default_fields: [~, ~, 'auto', 'defaults,nofail', '0', '2']
203 resize_rootfs_tmp: /dev
204-ssh_deletekeys: 0
205-ssh_genkeytypes: ~
206 ssh_pwauth: 0
207
208 {% endif %}
209diff --git a/debian/changelog b/debian/changelog
210index 05932be..fa27db3 100644
211--- a/debian/changelog
212+++ b/debian/changelog
213@@ -1,3 +1,17 @@
214+cloud-init (18.3-24-gf6249277-0ubuntu1) cosmic; urgency=medium
215+
216+ * New upstream snapshot.
217+ - docs: Fix example cloud-init analyze command to match output.
218+ [Wesley Gao]
219+ - netplan: Correctly render macaddress on a bonds and bridges when
220+ provided.
221+ - tools: Add 'net-convert' subcommand command to 'cloud-init devel'.
222+ - redhat: remove ssh keys on new instance.
223+ - Use typeset or local in profile.d scripts.
224+ - OpenNebula: Fix null gateway6 [Akihiko Ota]
225+
226+ -- Chad Smith <chad.smith@canonical.com> Thu, 09 Aug 2018 10:27:29 -0600
227+
228 cloud-init (18.3-18-g3cee0bf8-0ubuntu1) cosmic; urgency=medium
229
230 * New upstream snapshot.
231diff --git a/doc/rtd/topics/debugging.rst b/doc/rtd/topics/debugging.rst
232index cacc8a2..51363ea 100644
233--- a/doc/rtd/topics/debugging.rst
234+++ b/doc/rtd/topics/debugging.rst
235@@ -45,7 +45,7 @@ subcommands default to reading /var/log/cloud-init.log.
236
237 .. code-block:: shell-session
238
239- $ cloud-init analyze blame -i my-cloud-init.log
240+ $ cloud-init analyze dump -i my-cloud-init.log
241 [
242 {
243 "description": "running config modules",
244diff --git a/tests/unittests/test_cli.py b/tests/unittests/test_cli.py
245index 0c0f427..199d69b 100644
246--- a/tests/unittests/test_cli.py
247+++ b/tests/unittests/test_cli.py
248@@ -208,8 +208,7 @@ class TestCLI(test_helpers.FilesystemMockingTestCase):
249 for subcommand in expected_subcommands:
250 self.assertIn(subcommand, error)
251
252- @mock.patch('cloudinit.config.schema.handle_schema_args')
253- def test_wb_devel_schema_subcommand_parser(self, m_schema):
254+ def test_wb_devel_schema_subcommand_parser(self):
255 """The subcommand cloud-init schema calls the correct subparser."""
256 exit_code = self._call_main(['cloud-init', 'devel', 'schema'])
257 self.assertEqual(1, exit_code)
258diff --git a/tests/unittests/test_datasource/test_opennebula.py b/tests/unittests/test_datasource/test_opennebula.py
259index ab42f34..36b4d77 100644
260--- a/tests/unittests/test_datasource/test_opennebula.py
261+++ b/tests/unittests/test_datasource/test_opennebula.py
262@@ -354,6 +354,412 @@ class TestOpenNebulaNetwork(unittest.TestCase):
263
264 system_nics = ('eth0', 'ens3')
265
266+ def test_context_devname(self):
267+ """Verify context_devname correctly returns mac and name."""
268+ context = {
269+ 'ETH0_MAC': '02:00:0a:12:01:01',
270+ 'ETH1_MAC': '02:00:0a:12:0f:0f', }
271+ expected = {
272+ '02:00:0a:12:01:01': 'ETH0',
273+ '02:00:0a:12:0f:0f': 'ETH1', }
274+ net = ds.OpenNebulaNetwork(context)
275+ self.assertEqual(expected, net.context_devname)
276+
277+ def test_get_nameservers(self):
278+ """
279+ Verify get_nameservers('device') correctly returns DNS server addresses
280+ and search domains.
281+ """
282+ context = {
283+ 'DNS': '1.2.3.8',
284+ 'ETH0_DNS': '1.2.3.6 1.2.3.7',
285+ 'ETH0_SEARCH_DOMAIN': 'example.com example.org', }
286+ expected = {
287+ 'addresses': ['1.2.3.6', '1.2.3.7', '1.2.3.8'],
288+ 'search': ['example.com', 'example.org']}
289+ net = ds.OpenNebulaNetwork(context)
290+ val = net.get_nameservers('eth0')
291+ self.assertEqual(expected, val)
292+
293+ def test_get_mtu(self):
294+ """Verify get_mtu('device') correctly returns MTU size."""
295+ context = {'ETH0_MTU': '1280'}
296+ net = ds.OpenNebulaNetwork(context)
297+ val = net.get_mtu('eth0')
298+ self.assertEqual('1280', val)
299+
300+ def test_get_ip(self):
301+ """Verify get_ip('device') correctly returns IPv4 address."""
302+ context = {'ETH0_IP': PUBLIC_IP}
303+ net = ds.OpenNebulaNetwork(context)
304+ val = net.get_ip('eth0', MACADDR)
305+ self.assertEqual(PUBLIC_IP, val)
306+
307+ def test_get_ip_emptystring(self):
308+ """
309+ Verify get_ip('device') correctly returns IPv4 address.
310+ It returns IP address created by MAC address if ETH0_IP has empty
311+ string.
312+ """
313+ context = {'ETH0_IP': ''}
314+ net = ds.OpenNebulaNetwork(context)
315+ val = net.get_ip('eth0', MACADDR)
316+ self.assertEqual(IP_BY_MACADDR, val)
317+
318+ def test_get_ip6(self):
319+ """
320+ Verify get_ip6('device') correctly returns IPv6 address.
321+ In this case, IPv6 address is Given by ETH0_IP6.
322+ """
323+ context = {
324+ 'ETH0_IP6': IP6_GLOBAL,
325+ 'ETH0_IP6_ULA': '', }
326+ expected = [IP6_GLOBAL]
327+ net = ds.OpenNebulaNetwork(context)
328+ val = net.get_ip6('eth0')
329+ self.assertEqual(expected, val)
330+
331+ def test_get_ip6_ula(self):
332+ """
333+ Verify get_ip6('device') correctly returns IPv6 address.
334+ In this case, IPv6 address is Given by ETH0_IP6_ULA.
335+ """
336+ context = {
337+ 'ETH0_IP6': '',
338+ 'ETH0_IP6_ULA': IP6_ULA, }
339+ expected = [IP6_ULA]
340+ net = ds.OpenNebulaNetwork(context)
341+ val = net.get_ip6('eth0')
342+ self.assertEqual(expected, val)
343+
344+ def test_get_ip6_dual(self):
345+ """
346+ Verify get_ip6('device') correctly returns IPv6 address.
347+ In this case, IPv6 addresses are Given by ETH0_IP6 and ETH0_IP6_ULA.
348+ """
349+ context = {
350+ 'ETH0_IP6': IP6_GLOBAL,
351+ 'ETH0_IP6_ULA': IP6_ULA, }
352+ expected = [IP6_GLOBAL, IP6_ULA]
353+ net = ds.OpenNebulaNetwork(context)
354+ val = net.get_ip6('eth0')
355+ self.assertEqual(expected, val)
356+
357+ def test_get_ip6_prefix(self):
358+ """
359+ Verify get_ip6_prefix('device') correctly returns IPv6 prefix.
360+ """
361+ context = {'ETH0_IP6_PREFIX_LENGTH': IP6_PREFIX}
362+ net = ds.OpenNebulaNetwork(context)
363+ val = net.get_ip6_prefix('eth0')
364+ self.assertEqual(IP6_PREFIX, val)
365+
366+ def test_get_ip6_prefix_emptystring(self):
367+ """
368+ Verify get_ip6_prefix('device') correctly returns IPv6 prefix.
369+ It returns default value '64' if ETH0_IP6_PREFIX_LENGTH has empty
370+ string.
371+ """
372+ context = {'ETH0_IP6_PREFIX_LENGTH': ''}
373+ net = ds.OpenNebulaNetwork(context)
374+ val = net.get_ip6_prefix('eth0')
375+ self.assertEqual('64', val)
376+
377+ def test_get_gateway(self):
378+ """
379+ Verify get_gateway('device') correctly returns IPv4 default gateway
380+ address.
381+ """
382+ context = {'ETH0_GATEWAY': '1.2.3.5'}
383+ net = ds.OpenNebulaNetwork(context)
384+ val = net.get_gateway('eth0')
385+ self.assertEqual('1.2.3.5', val)
386+
387+ def test_get_gateway6(self):
388+ """
389+ Verify get_gateway6('device') correctly returns IPv6 default gateway
390+ address.
391+ """
392+ context = {'ETH0_GATEWAY6': IP6_GW}
393+ net = ds.OpenNebulaNetwork(context)
394+ val = net.get_gateway6('eth0')
395+ self.assertEqual(IP6_GW, val)
396+
397+ def test_get_mask(self):
398+ """
399+ Verify get_mask('device') correctly returns IPv4 subnet mask.
400+ """
401+ context = {'ETH0_MASK': '255.255.0.0'}
402+ net = ds.OpenNebulaNetwork(context)
403+ val = net.get_mask('eth0')
404+ self.assertEqual('255.255.0.0', val)
405+
406+ def test_get_mask_emptystring(self):
407+ """
408+ Verify get_mask('device') correctly returns IPv4 subnet mask.
409+ It returns default value '255.255.255.0' if ETH0_MASK has empty string.
410+ """
411+ context = {'ETH0_MASK': ''}
412+ net = ds.OpenNebulaNetwork(context)
413+ val = net.get_mask('eth0')
414+ self.assertEqual('255.255.255.0', val)
415+
416+ def test_get_network(self):
417+ """
418+ Verify get_network('device') correctly returns IPv4 network address.
419+ """
420+ context = {'ETH0_NETWORK': '1.2.3.0'}
421+ net = ds.OpenNebulaNetwork(context)
422+ val = net.get_network('eth0', MACADDR)
423+ self.assertEqual('1.2.3.0', val)
424+
425+ def test_get_network_emptystring(self):
426+ """
427+ Verify get_network('device') correctly returns IPv4 network address.
428+ It returns network address created by MAC address if ETH0_NETWORK has
429+ empty string.
430+ """
431+ context = {'ETH0_NETWORK': ''}
432+ net = ds.OpenNebulaNetwork(context)
433+ val = net.get_network('eth0', MACADDR)
434+ self.assertEqual('10.18.1.0', val)
435+
436+ def test_get_field(self):
437+ """
438+ Verify get_field('device', 'name') returns *context* value.
439+ """
440+ context = {'ETH9_DUMMY': 'DUMMY_VALUE'}
441+ net = ds.OpenNebulaNetwork(context)
442+ val = net.get_field('eth9', 'dummy')
443+ self.assertEqual('DUMMY_VALUE', val)
444+
445+ def test_get_field_withdefaultvalue(self):
446+ """
447+ Verify get_field('device', 'name', 'default value') returns *context*
448+ value.
449+ """
450+ context = {'ETH9_DUMMY': 'DUMMY_VALUE'}
451+ net = ds.OpenNebulaNetwork(context)
452+ val = net.get_field('eth9', 'dummy', 'DEFAULT_VALUE')
453+ self.assertEqual('DUMMY_VALUE', val)
454+
455+ def test_get_field_withdefaultvalue_emptycontext(self):
456+ """
457+ Verify get_field('device', 'name', 'default value') returns *default*
458+ value if context value is empty string.
459+ """
460+ context = {'ETH9_DUMMY': ''}
461+ net = ds.OpenNebulaNetwork(context)
462+ val = net.get_field('eth9', 'dummy', 'DEFAULT_VALUE')
463+ self.assertEqual('DEFAULT_VALUE', val)
464+
465+ def test_get_field_emptycontext(self):
466+ """
467+ Verify get_field('device', 'name') returns None if context value is
468+ empty string.
469+ """
470+ context = {'ETH9_DUMMY': ''}
471+ net = ds.OpenNebulaNetwork(context)
472+ val = net.get_field('eth9', 'dummy')
473+ self.assertEqual(None, val)
474+
475+ def test_get_field_nonecontext(self):
476+ """
477+ Verify get_field('device', 'name') returns None if context value is
478+ None.
479+ """
480+ context = {'ETH9_DUMMY': None}
481+ net = ds.OpenNebulaNetwork(context)
482+ val = net.get_field('eth9', 'dummy')
483+ self.assertEqual(None, val)
484+
485+ @mock.patch(DS_PATH + ".get_physical_nics_by_mac")
486+ def test_gen_conf_gateway(self, m_get_phys_by_mac):
487+ """Test rendering with/without IPv4 gateway"""
488+ self.maxDiff = None
489+ # empty ETH0_GATEWAY
490+ context = {
491+ 'ETH0_MAC': '02:00:0a:12:01:01',
492+ 'ETH0_GATEWAY': '', }
493+ for nic in self.system_nics:
494+ expected = {
495+ 'version': 2,
496+ 'ethernets': {
497+ nic: {
498+ 'match': {'macaddress': MACADDR},
499+ 'addresses': [IP_BY_MACADDR + '/' + IP4_PREFIX]}}}
500+ m_get_phys_by_mac.return_value = {MACADDR: nic}
501+ net = ds.OpenNebulaNetwork(context)
502+ self.assertEqual(net.gen_conf(), expected)
503+
504+ # set ETH0_GATEWAY
505+ context = {
506+ 'ETH0_MAC': '02:00:0a:12:01:01',
507+ 'ETH0_GATEWAY': '1.2.3.5', }
508+ for nic in self.system_nics:
509+ expected = {
510+ 'version': 2,
511+ 'ethernets': {
512+ nic: {
513+ 'gateway4': '1.2.3.5',
514+ 'match': {'macaddress': MACADDR},
515+ 'addresses': [IP_BY_MACADDR + '/' + IP4_PREFIX]}}}
516+ m_get_phys_by_mac.return_value = {MACADDR: nic}
517+ net = ds.OpenNebulaNetwork(context)
518+ self.assertEqual(net.gen_conf(), expected)
519+
520+ @mock.patch(DS_PATH + ".get_physical_nics_by_mac")
521+ def test_gen_conf_gateway6(self, m_get_phys_by_mac):
522+ """Test rendering with/without IPv6 gateway"""
523+ self.maxDiff = None
524+ # empty ETH0_GATEWAY6
525+ context = {
526+ 'ETH0_MAC': '02:00:0a:12:01:01',
527+ 'ETH0_GATEWAY6': '', }
528+ for nic in self.system_nics:
529+ expected = {
530+ 'version': 2,
531+ 'ethernets': {
532+ nic: {
533+ 'match': {'macaddress': MACADDR},
534+ 'addresses': [IP_BY_MACADDR + '/' + IP4_PREFIX]}}}
535+ m_get_phys_by_mac.return_value = {MACADDR: nic}
536+ net = ds.OpenNebulaNetwork(context)
537+ self.assertEqual(net.gen_conf(), expected)
538+
539+ # set ETH0_GATEWAY6
540+ context = {
541+ 'ETH0_MAC': '02:00:0a:12:01:01',
542+ 'ETH0_GATEWAY6': IP6_GW, }
543+ for nic in self.system_nics:
544+ expected = {
545+ 'version': 2,
546+ 'ethernets': {
547+ nic: {
548+ 'gateway6': IP6_GW,
549+ 'match': {'macaddress': MACADDR},
550+ 'addresses': [IP_BY_MACADDR + '/' + IP4_PREFIX]}}}
551+ m_get_phys_by_mac.return_value = {MACADDR: nic}
552+ net = ds.OpenNebulaNetwork(context)
553+ self.assertEqual(net.gen_conf(), expected)
554+
555+ @mock.patch(DS_PATH + ".get_physical_nics_by_mac")
556+ def test_gen_conf_ipv6address(self, m_get_phys_by_mac):
557+ """Test rendering with/without IPv6 address"""
558+ self.maxDiff = None
559+ # empty ETH0_IP6, ETH0_IP6_ULA, ETH0_IP6_PREFIX_LENGTH
560+ context = {
561+ 'ETH0_MAC': '02:00:0a:12:01:01',
562+ 'ETH0_IP6': '',
563+ 'ETH0_IP6_ULA': '',
564+ 'ETH0_IP6_PREFIX_LENGTH': '', }
565+ for nic in self.system_nics:
566+ expected = {
567+ 'version': 2,
568+ 'ethernets': {
569+ nic: {
570+ 'match': {'macaddress': MACADDR},
571+ 'addresses': [IP_BY_MACADDR + '/' + IP4_PREFIX]}}}
572+ m_get_phys_by_mac.return_value = {MACADDR: nic}
573+ net = ds.OpenNebulaNetwork(context)
574+ self.assertEqual(net.gen_conf(), expected)
575+
576+ # set ETH0_IP6, ETH0_IP6_ULA, ETH0_IP6_PREFIX_LENGTH
577+ context = {
578+ 'ETH0_MAC': '02:00:0a:12:01:01',
579+ 'ETH0_IP6': IP6_GLOBAL,
580+ 'ETH0_IP6_PREFIX_LENGTH': IP6_PREFIX,
581+ 'ETH0_IP6_ULA': IP6_ULA, }
582+ for nic in self.system_nics:
583+ expected = {
584+ 'version': 2,
585+ 'ethernets': {
586+ nic: {
587+ 'match': {'macaddress': MACADDR},
588+ 'addresses': [
589+ IP_BY_MACADDR + '/' + IP4_PREFIX,
590+ IP6_GLOBAL + '/' + IP6_PREFIX,
591+ IP6_ULA + '/' + IP6_PREFIX]}}}
592+ m_get_phys_by_mac.return_value = {MACADDR: nic}
593+ net = ds.OpenNebulaNetwork(context)
594+ self.assertEqual(net.gen_conf(), expected)
595+
596+ @mock.patch(DS_PATH + ".get_physical_nics_by_mac")
597+ def test_gen_conf_dns(self, m_get_phys_by_mac):
598+ """Test rendering with/without DNS server, search domain"""
599+ self.maxDiff = None
600+ # empty DNS, ETH0_DNS, ETH0_SEARCH_DOMAIN
601+ context = {
602+ 'ETH0_MAC': '02:00:0a:12:01:01',
603+ 'DNS': '',
604+ 'ETH0_DNS': '',
605+ 'ETH0_SEARCH_DOMAIN': '', }
606+ for nic in self.system_nics:
607+ expected = {
608+ 'version': 2,
609+ 'ethernets': {
610+ nic: {
611+ 'match': {'macaddress': MACADDR},
612+ 'addresses': [IP_BY_MACADDR + '/' + IP4_PREFIX]}}}
613+ m_get_phys_by_mac.return_value = {MACADDR: nic}
614+ net = ds.OpenNebulaNetwork(context)
615+ self.assertEqual(net.gen_conf(), expected)
616+
617+ # set DNS, ETH0_DNS, ETH0_SEARCH_DOMAIN
618+ context = {
619+ 'ETH0_MAC': '02:00:0a:12:01:01',
620+ 'DNS': '1.2.3.8',
621+ 'ETH0_DNS': '1.2.3.6 1.2.3.7',
622+ 'ETH0_SEARCH_DOMAIN': 'example.com example.org', }
623+ for nic in self.system_nics:
624+ expected = {
625+ 'version': 2,
626+ 'ethernets': {
627+ nic: {
628+ 'nameservers': {
629+ 'addresses': ['1.2.3.6', '1.2.3.7', '1.2.3.8'],
630+ 'search': ['example.com', 'example.org']},
631+ 'match': {'macaddress': MACADDR},
632+ 'addresses': [IP_BY_MACADDR + '/' + IP4_PREFIX]}}}
633+ m_get_phys_by_mac.return_value = {MACADDR: nic}
634+ net = ds.OpenNebulaNetwork(context)
635+ self.assertEqual(net.gen_conf(), expected)
636+
637+ @mock.patch(DS_PATH + ".get_physical_nics_by_mac")
638+ def test_gen_conf_mtu(self, m_get_phys_by_mac):
639+ """Test rendering with/without MTU"""
640+ self.maxDiff = None
641+ # empty ETH0_MTU
642+ context = {
643+ 'ETH0_MAC': '02:00:0a:12:01:01',
644+ 'ETH0_MTU': '', }
645+ for nic in self.system_nics:
646+ expected = {
647+ 'version': 2,
648+ 'ethernets': {
649+ nic: {
650+ 'match': {'macaddress': MACADDR},
651+ 'addresses': [IP_BY_MACADDR + '/' + IP4_PREFIX]}}}
652+ m_get_phys_by_mac.return_value = {MACADDR: nic}
653+ net = ds.OpenNebulaNetwork(context)
654+ self.assertEqual(net.gen_conf(), expected)
655+
656+ # set ETH0_MTU
657+ context = {
658+ 'ETH0_MAC': '02:00:0a:12:01:01',
659+ 'ETH0_MTU': '1280', }
660+ for nic in self.system_nics:
661+ expected = {
662+ 'version': 2,
663+ 'ethernets': {
664+ nic: {
665+ 'mtu': '1280',
666+ 'match': {'macaddress': MACADDR},
667+ 'addresses': [IP_BY_MACADDR + '/' + IP4_PREFIX]}}}
668+ m_get_phys_by_mac.return_value = {MACADDR: nic}
669+ net = ds.OpenNebulaNetwork(context)
670+ self.assertEqual(net.gen_conf(), expected)
671+
672 @mock.patch(DS_PATH + ".get_physical_nics_by_mac")
673 def test_eth0(self, m_get_phys_by_mac):
674 for nic in self.system_nics:
675@@ -395,7 +801,6 @@ class TestOpenNebulaNetwork(unittest.TestCase):
676 'match': {'macaddress': MACADDR},
677 'addresses': [IP_BY_MACADDR + '/16'],
678 'gateway4': '1.2.3.5',
679- 'gateway6': None,
680 'nameservers': {
681 'addresses': ['1.2.3.6', '1.2.3.7', '1.2.3.8']}}}}
682
683@@ -494,7 +899,6 @@ class TestOpenNebulaNetwork(unittest.TestCase):
684 'match': {'macaddress': MAC_1},
685 'addresses': ['10.3.1.3/16'],
686 'gateway4': '10.3.0.1',
687- 'gateway6': None,
688 'nameservers': {
689 'addresses': ['10.3.1.2', '1.2.3.8'],
690 'search': [
691diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
692index 5ab61cf..58e5ea1 100644
693--- a/tests/unittests/test_net.py
694+++ b/tests/unittests/test_net.py
695@@ -643,6 +643,7 @@ iface br0 inet static
696 bridge_stp off
697 bridge_waitport 1 eth3
698 bridge_waitport 2 eth4
699+ hwaddress bb:bb:bb:bb:bb:aa
700
701 # control-alias br0
702 iface br0 inet6 static
703@@ -708,6 +709,7 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
704 interfaces:
705 - eth1
706 - eth2
707+ macaddress: aa:bb:cc:dd:ee:ff
708 parameters:
709 mii-monitor-interval: 100
710 mode: active-backup
711@@ -720,6 +722,7 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
712 interfaces:
713 - eth3
714 - eth4
715+ macaddress: bb:bb:bb:bb:bb:aa
716 nameservers:
717 addresses:
718 - 8.8.8.8
719@@ -803,6 +806,7 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
720 IPV6ADDR=2001:1::1/64
721 IPV6INIT=yes
722 IPV6_DEFAULTGW=2001:4800:78ff:1b::1
723+ MACADDR=bb:bb:bb:bb:bb:aa
724 NETMASK=255.255.255.0
725 NM_CONTROLLED=no
726 ONBOOT=yes
727@@ -973,6 +977,7 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
728 use_tempaddr: 1
729 forwarding: 1
730 # basically anything in /proc/sys/net/ipv6/conf/.../
731+ mac_address: bb:bb:bb:bb:bb:aa
732 params:
733 bridge_ageing: 250
734 bridge_bridgeprio: 22
735@@ -1075,6 +1080,7 @@ pre-down route del -net 10.0.0.0 netmask 255.0.0.0 gw 11.0.0.1 metric 3 || true
736 interfaces:
737 - bond0s0
738 - bond0s1
739+ macaddress: aa:bb:cc:dd:e8:ff
740 mtu: 9000
741 parameters:
742 mii-monitor-interval: 100
743diff --git a/tools/Z99-cloud-locale-test.sh b/tools/Z99-cloud-locale-test.sh
744index 4978d87..9ee44bd 100644
745--- a/tools/Z99-cloud-locale-test.sh
746+++ b/tools/Z99-cloud-locale-test.sh
747@@ -11,8 +11,11 @@
748 # of how to fix them.
749
750 locale_warn() {
751- local bad_names="" bad_lcs="" key="" val="" var="" vars="" bad_kv=""
752- local w1 w2 w3 w4 remain
753+ command -v local >/dev/null && local _local="local" ||
754+ typeset _local="typeset"
755+
756+ $_local bad_names="" bad_lcs="" key="" val="" var="" vars="" bad_kv=""
757+ $_local w1 w2 w3 w4 remain
758
759 # if shell is zsh, act like sh only for this function (-L).
760 # The behavior change will not permenently affect user's shell.
761@@ -53,8 +56,8 @@ locale_warn() {
762 printf " This can affect your user experience significantly, including the\n"
763 printf " ability to manage packages. You may install the locales by running:\n\n"
764
765- local bad invalid="" to_gen="" sfile="/usr/share/i18n/SUPPORTED"
766- local pkgs=""
767+ $_local bad invalid="" to_gen="" sfile="/usr/share/i18n/SUPPORTED"
768+ $_local local pkgs=""
769 if [ -e "$sfile" ]; then
770 for bad in ${bad_lcs}; do
771 grep -q -i "${bad}" "$sfile" &&
772@@ -67,7 +70,7 @@ locale_warn() {
773 fi
774 to_gen=${to_gen# }
775
776- local pkgs=""
777+ $_local pkgs=""
778 for bad in ${to_gen}; do
779 pkgs="${pkgs} language-pack-${bad%%_*}"
780 done
781diff --git a/tools/Z99-cloudinit-warnings.sh b/tools/Z99-cloudinit-warnings.sh
782index 1d41337..cb8b463 100644
783--- a/tools/Z99-cloudinit-warnings.sh
784+++ b/tools/Z99-cloudinit-warnings.sh
785@@ -4,9 +4,11 @@
786 # Purpose: show user warnings on login.
787
788 cloud_init_warnings() {
789- local warning="" idir="/var/lib/cloud/instance" n=0
790- local warndir="$idir/warnings"
791- local ufile="$HOME/.cloud-warnings.skip" sfile="$warndir/.skip"
792+ command -v local >/dev/null && local _local="local" ||
793+ typeset _local="typeset"
794+ $_local warning="" idir="/var/lib/cloud/instance" n=0
795+ $_local warndir="$idir/warnings"
796+ $_local ufile="$HOME/.cloud-warnings.skip" sfile="$warndir/.skip"
797 [ -d "$warndir" ] || return 0
798 [ ! -f "$ufile" ] || return 0
799 [ ! -f "$sfile" ] || return 0

Subscribers

People subscribed via source and target branches