Merge ~chad.smith/cloud-init:ubuntu/devel into cloud-init:ubuntu/devel
- Git
- lp:~chad.smith/cloud-init
- ubuntu/devel
- Merge into 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) |
||||||||||||||||
| Related bugs: |
|
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Scott Moser | Approve | ||
| Server Team CI bot | continuous-integration | Approve | |
|
Review via email:
|
|||
Commit message
new-upstream-
Description of the change
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)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
| 1 | diff --git a/bash_completion/cloud-init b/bash_completion/cloud-init |
| 2 | index 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 |
| 30 | diff --git a/tools/net-convert.py b/cloudinit/cmd/devel/net_convert.py |
| 31 | index 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 |
| 102 | diff --git a/cloudinit/cmd/devel/parser.py b/cloudinit/cmd/devel/parser.py |
| 103 | index 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 |
| 139 | diff --git a/cloudinit/net/eni.py b/cloudinit/net/eni.py |
| 140 | index 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'] = {} |
| 161 | diff --git a/cloudinit/net/netplan.py b/cloudinit/net/netplan.py |
| 162 | index 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 | |
| 183 | diff --git a/cloudinit/sources/DataSourceOpenNebula.py b/cloudinit/sources/DataSourceOpenNebula.py |
| 184 | index 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 |
| 196 | diff --git a/config/cloud.cfg.tmpl b/config/cloud.cfg.tmpl |
| 197 | index 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 %} |
| 209 | diff --git a/debian/changelog b/debian/changelog |
| 210 | index 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. |
| 231 | diff --git a/doc/rtd/topics/debugging.rst b/doc/rtd/topics/debugging.rst |
| 232 | index 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", |
| 244 | diff --git a/tests/unittests/test_cli.py b/tests/unittests/test_cli.py |
| 245 | index 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) |
| 258 | diff --git a/tests/unittests/test_datasource/test_opennebula.py b/tests/unittests/test_datasource/test_opennebula.py |
| 259 | index 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': [ |
| 691 | diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py |
| 692 | index 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 |
| 743 | diff --git a/tools/Z99-cloud-locale-test.sh b/tools/Z99-cloud-locale-test.sh |
| 744 | index 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 |
| 781 | diff --git a/tools/Z99-cloudinit-warnings.sh b/tools/Z99-cloudinit-warnings.sh |
| 782 | index 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 |

PASSED: Continuous integration, rev:baebe101f4d 5df42a9d82e810d d6423fe7709219 /jenkins. ubuntu. com/server/ job/cloud- init-ci/ 213/
https:/
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: /jenkins. ubuntu. com/server/ job/cloud- init-ci/ 213/rebuild
https:/