Merge ~oddbloke/cloud-init/+git/cloud-init:ubuntu/devel into cloud-init:ubuntu/devel
- Git
- lp:~oddbloke/cloud-init/+git/cloud-init
- ubuntu/devel
- Merge into ubuntu/devel
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 1639758c62b57cbe38afbf583472aaf67cbf6616 | ||||||||
Proposed branch: | ~oddbloke/cloud-init/+git/cloud-init:ubuntu/devel | ||||||||
Merge into: | cloud-init:ubuntu/devel | ||||||||
Diff against target: |
2218 lines (+1161/-150) 51 files modified
.gitignore (+1/-0) cloudinit/cmd/clean.py (+14/-13) cloudinit/cmd/tests/test_clean.py (+4/-2) cloudinit/config/cc_apt_pipelining.py (+2/-2) cloudinit/config/cc_chef.py (+3/-0) cloudinit/config/cc_rsyslog.py (+1/-1) cloudinit/config/schema.py (+1/-1) cloudinit/config/tests/test_apt_pipelining.py (+28/-0) cloudinit/distros/__init__.py (+9/-4) cloudinit/handlers/upstart_job.py (+1/-1) cloudinit/net/netplan.py (+2/-1) cloudinit/net/network_state.py (+2/-2) cloudinit/net/tests/test_dhcp.py (+1/-0) cloudinit/netinfo.py (+5/-2) cloudinit/safeyaml.py (+7/-0) cloudinit/sources/DataSourceAzure.py (+9/-4) cloudinit/sources/DataSourceEc2.py (+22/-1) cloudinit/sources/DataSourceOVF.py (+3/-1) cloudinit/sources/helpers/azure.py (+78/-31) cloudinit/sources/helpers/openstack.py (+6/-6) cloudinit/stages.py (+2/-2) cloudinit/tests/helpers.py (+2/-21) cloudinit/tests/test_netinfo.py (+14/-0) cloudinit/url_helper.py (+1/-1) cloudinit/util.py (+11/-17) debian/changelog (+44/-0) debian/cloud-init.lintian-overrides (+4/-0) debian/control (+4/-5) doc/examples/cloud-config-chef.txt (+3/-0) doc/examples/cloud-config-disk-setup.txt (+17/-2) doc/rtd/topics/datasources/ec2.rst (+11/-0) doc/rtd/topics/instancedata.rst (+1/-1) doc/rtd/topics/merging.rst (+84/-6) templates/chef_client.rb.tmpl (+4/-1) tests/cloud_tests/verify.py (+7/-2) tests/data/azure/parse_certificates_fingerprints (+4/-0) tests/data/azure/parse_certificates_pem (+152/-0) tests/data/azure/pubkey_extract_cert (+13/-0) tests/data/azure/pubkey_extract_ssh_key (+1/-0) tests/data/netinfo/freebsd-ifconfig-output (+17/-0) tests/data/netinfo/freebsd-netdev-formatted-output (+11/-0) tests/unittests/test_datasource/test_azure.py (+2/-3) tests/unittests/test_datasource/test_azure_helper.py (+65/-6) tests/unittests/test_datasource/test_configdrive.py (+27/-8) tests/unittests/test_datasource/test_ec2.py (+24/-0) tests/unittests/test_distros/test_create_users.py (+28/-0) tests/unittests/test_distros/test_netconfig.py (+1/-1) tests/unittests/test_ds_identify.py (+1/-1) tests/unittests/test_handler/test_handler_chef.py (+3/-0) tests/unittests/test_net.py (+397/-0) tools/cloud-init-per (+7/-1) |
||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ryan Harper | Approve | ||
Server Team CI bot | continuous-integration | Needs Fixing | |
Review via email: mp+364099@code.launchpad.net |
Commit message
Description of the change
Server Team CI bot (server-team-bot) wrote : | # |
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:d78d4d09dfd
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:8f319b3aeff
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
Server Team CI bot (server-team-bot) wrote : | # |
FAILED: Continuous integration, rev:1639758c62b
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
https:/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild:
https:/
Preview Diff
1 | diff --git a/.gitignore b/.gitignore | |||
2 | index 75565ed..80c509e 100644 | |||
3 | --- a/.gitignore | |||
4 | +++ b/.gitignore | |||
5 | @@ -11,3 +11,4 @@ prime | |||
6 | 11 | stage | 11 | stage |
7 | 12 | *.snap | 12 | *.snap |
8 | 13 | *.cover | 13 | *.cover |
9 | 14 | .idea/ | ||
10 | diff --git a/cloudinit/cmd/clean.py b/cloudinit/cmd/clean.py | |||
11 | index de22f7f..30e49de 100644 | |||
12 | --- a/cloudinit/cmd/clean.py | |||
13 | +++ b/cloudinit/cmd/clean.py | |||
14 | @@ -5,12 +5,13 @@ | |||
15 | 5 | """Define 'clean' utility and handler as part of cloud-init commandline.""" | 5 | """Define 'clean' utility and handler as part of cloud-init commandline.""" |
16 | 6 | 6 | ||
17 | 7 | import argparse | 7 | import argparse |
18 | 8 | import glob | ||
19 | 8 | import os | 9 | import os |
20 | 9 | import sys | 10 | import sys |
21 | 10 | 11 | ||
22 | 11 | from cloudinit.stages import Init | 12 | from cloudinit.stages import Init |
23 | 12 | from cloudinit.util import ( | 13 | from cloudinit.util import ( |
25 | 13 | ProcessExecutionError, chdir, del_dir, del_file, get_config_logfiles, | 14 | ProcessExecutionError, del_dir, del_file, get_config_logfiles, |
26 | 14 | is_link, subp) | 15 | is_link, subp) |
27 | 15 | 16 | ||
28 | 16 | 17 | ||
29 | @@ -61,18 +62,18 @@ def remove_artifacts(remove_logs, remove_seed=False): | |||
30 | 61 | 62 | ||
31 | 62 | if not os.path.isdir(init.paths.cloud_dir): | 63 | if not os.path.isdir(init.paths.cloud_dir): |
32 | 63 | return 0 # Artifacts dir already cleaned | 64 | return 0 # Artifacts dir already cleaned |
45 | 64 | with chdir(init.paths.cloud_dir): | 65 | seed_path = os.path.join(init.paths.cloud_dir, 'seed') |
46 | 65 | for path in os.listdir('.'): | 66 | for path in glob.glob('%s/*' % init.paths.cloud_dir): |
47 | 66 | if path == 'seed' and not remove_seed: | 67 | if path == seed_path and not remove_seed: |
48 | 67 | continue | 68 | continue |
49 | 68 | try: | 69 | try: |
50 | 69 | if os.path.isdir(path) and not is_link(path): | 70 | if os.path.isdir(path) and not is_link(path): |
51 | 70 | del_dir(path) | 71 | del_dir(path) |
52 | 71 | else: | 72 | else: |
53 | 72 | del_file(path) | 73 | del_file(path) |
54 | 73 | except OSError as e: | 74 | except OSError as e: |
55 | 74 | error('Could not remove {0}: {1}'.format(path, str(e))) | 75 | error('Could not remove {0}: {1}'.format(path, str(e))) |
56 | 75 | return 1 | 76 | return 1 |
57 | 76 | return 0 | 77 | return 0 |
58 | 77 | 78 | ||
59 | 78 | 79 | ||
60 | diff --git a/cloudinit/cmd/tests/test_clean.py b/cloudinit/cmd/tests/test_clean.py | |||
61 | index 5a3ec3b..f092ab3 100644 | |||
62 | --- a/cloudinit/cmd/tests/test_clean.py | |||
63 | +++ b/cloudinit/cmd/tests/test_clean.py | |||
64 | @@ -22,7 +22,8 @@ class TestClean(CiTestCase): | |||
65 | 22 | class FakeInit(object): | 22 | class FakeInit(object): |
66 | 23 | cfg = {'def_log_file': self.log1, | 23 | cfg = {'def_log_file': self.log1, |
67 | 24 | 'output': {'all': '|tee -a {0}'.format(self.log2)}} | 24 | 'output': {'all': '|tee -a {0}'.format(self.log2)}} |
69 | 25 | paths = mypaths(cloud_dir=self.artifact_dir) | 25 | # Ensure cloud_dir has a trailing slash, to match real behaviour |
70 | 26 | paths = mypaths(cloud_dir='{}/'.format(self.artifact_dir)) | ||
71 | 26 | 27 | ||
72 | 27 | def __init__(self, ds_deps): | 28 | def __init__(self, ds_deps): |
73 | 28 | pass | 29 | pass |
74 | @@ -136,7 +137,8 @@ class TestClean(CiTestCase): | |||
75 | 136 | clean.remove_artifacts, remove_logs=False) | 137 | clean.remove_artifacts, remove_logs=False) |
76 | 137 | self.assertEqual(1, retcode) | 138 | self.assertEqual(1, retcode) |
77 | 138 | self.assertEqual( | 139 | self.assertEqual( |
79 | 139 | 'ERROR: Could not remove dir1: oops\n', m_stderr.getvalue()) | 140 | 'ERROR: Could not remove %s/dir1: oops\n' % self.artifact_dir, |
80 | 141 | m_stderr.getvalue()) | ||
81 | 140 | 142 | ||
82 | 141 | def test_handle_clean_args_reboots(self): | 143 | def test_handle_clean_args_reboots(self): |
83 | 142 | """handle_clean_args_reboots when reboot arg is provided.""" | 144 | """handle_clean_args_reboots when reboot arg is provided.""" |
84 | diff --git a/cloudinit/config/cc_apt_pipelining.py b/cloudinit/config/cc_apt_pipelining.py | |||
85 | index cdf28cd..459332a 100644 | |||
86 | --- a/cloudinit/config/cc_apt_pipelining.py | |||
87 | +++ b/cloudinit/config/cc_apt_pipelining.py | |||
88 | @@ -49,7 +49,7 @@ APT_PIPE_TPL = ("//Written by cloud-init per 'apt_pipelining'\n" | |||
89 | 49 | 49 | ||
90 | 50 | def handle(_name, cfg, _cloud, log, _args): | 50 | def handle(_name, cfg, _cloud, log, _args): |
91 | 51 | 51 | ||
93 | 52 | apt_pipe_value = util.get_cfg_option_str(cfg, "apt_pipelining", False) | 52 | apt_pipe_value = util.get_cfg_option_str(cfg, "apt_pipelining", 'os') |
94 | 53 | apt_pipe_value_s = str(apt_pipe_value).lower().strip() | 53 | apt_pipe_value_s = str(apt_pipe_value).lower().strip() |
95 | 54 | 54 | ||
96 | 55 | if apt_pipe_value_s == "false": | 55 | if apt_pipe_value_s == "false": |
97 | @@ -59,7 +59,7 @@ def handle(_name, cfg, _cloud, log, _args): | |||
98 | 59 | elif apt_pipe_value_s in [str(b) for b in range(0, 6)]: | 59 | elif apt_pipe_value_s in [str(b) for b in range(0, 6)]: |
99 | 60 | write_apt_snippet(apt_pipe_value_s, log, DEFAULT_FILE) | 60 | write_apt_snippet(apt_pipe_value_s, log, DEFAULT_FILE) |
100 | 61 | else: | 61 | else: |
102 | 62 | log.warn("Invalid option for apt_pipeling: %s", apt_pipe_value) | 62 | log.warn("Invalid option for apt_pipelining: %s", apt_pipe_value) |
103 | 63 | 63 | ||
104 | 64 | 64 | ||
105 | 65 | def write_apt_snippet(setting, log, f_name): | 65 | def write_apt_snippet(setting, log, f_name): |
106 | diff --git a/cloudinit/config/cc_chef.py b/cloudinit/config/cc_chef.py | |||
107 | index 46abedd..a624030 100644 | |||
108 | --- a/cloudinit/config/cc_chef.py | |||
109 | +++ b/cloudinit/config/cc_chef.py | |||
110 | @@ -51,6 +51,7 @@ file). | |||
111 | 51 | 51 | ||
112 | 52 | chef: | 52 | chef: |
113 | 53 | client_key: | 53 | client_key: |
114 | 54 | encrypted_data_bag_secret: | ||
115 | 54 | environment: | 55 | environment: |
116 | 55 | file_backup_path: | 56 | file_backup_path: |
117 | 56 | file_cache_path: | 57 | file_cache_path: |
118 | @@ -114,6 +115,7 @@ CHEF_RB_TPL_DEFAULTS = { | |||
119 | 114 | 'file_backup_path': "/var/backups/chef", | 115 | 'file_backup_path': "/var/backups/chef", |
120 | 115 | 'pid_file': "/var/run/chef/client.pid", | 116 | 'pid_file': "/var/run/chef/client.pid", |
121 | 116 | 'show_time': True, | 117 | 'show_time': True, |
122 | 118 | 'encrypted_data_bag_secret': None, | ||
123 | 117 | } | 119 | } |
124 | 118 | CHEF_RB_TPL_BOOL_KEYS = frozenset(['show_time']) | 120 | CHEF_RB_TPL_BOOL_KEYS = frozenset(['show_time']) |
125 | 119 | CHEF_RB_TPL_PATH_KEYS = frozenset([ | 121 | CHEF_RB_TPL_PATH_KEYS = frozenset([ |
126 | @@ -124,6 +126,7 @@ CHEF_RB_TPL_PATH_KEYS = frozenset([ | |||
127 | 124 | 'json_attribs', | 126 | 'json_attribs', |
128 | 125 | 'file_cache_path', | 127 | 'file_cache_path', |
129 | 126 | 'pid_file', | 128 | 'pid_file', |
130 | 129 | 'encrypted_data_bag_secret', | ||
131 | 127 | ]) | 130 | ]) |
132 | 128 | CHEF_RB_TPL_KEYS = list(CHEF_RB_TPL_DEFAULTS.keys()) | 131 | CHEF_RB_TPL_KEYS = list(CHEF_RB_TPL_DEFAULTS.keys()) |
133 | 129 | CHEF_RB_TPL_KEYS.extend(CHEF_RB_TPL_BOOL_KEYS) | 132 | CHEF_RB_TPL_KEYS.extend(CHEF_RB_TPL_BOOL_KEYS) |
134 | diff --git a/cloudinit/config/cc_rsyslog.py b/cloudinit/config/cc_rsyslog.py | |||
135 | index 27d2366..22b1753 100644 | |||
136 | --- a/cloudinit/config/cc_rsyslog.py | |||
137 | +++ b/cloudinit/config/cc_rsyslog.py | |||
138 | @@ -203,7 +203,7 @@ LOG = logging.getLogger(__name__) | |||
139 | 203 | COMMENT_RE = re.compile(r'[ ]*[#]+[ ]*') | 203 | COMMENT_RE = re.compile(r'[ ]*[#]+[ ]*') |
140 | 204 | HOST_PORT_RE = re.compile( | 204 | HOST_PORT_RE = re.compile( |
141 | 205 | r'^(?P<proto>[@]{0,2})' | 205 | r'^(?P<proto>[@]{0,2})' |
143 | 206 | r'(([[](?P<bracket_addr>[^\]]*)[\]])|(?P<addr>[^:]*))' | 206 | r'(([\[](?P<bracket_addr>[^\]]*)[\]])|(?P<addr>[^:]*))' |
144 | 207 | r'([:](?P<port>[0-9]+))?$') | 207 | r'([:](?P<port>[0-9]+))?$') |
145 | 208 | 208 | ||
146 | 209 | 209 | ||
147 | diff --git a/cloudinit/config/schema.py b/cloudinit/config/schema.py | |||
148 | index 080a6d0..807c3ee 100644 | |||
149 | --- a/cloudinit/config/schema.py | |||
150 | +++ b/cloudinit/config/schema.py | |||
151 | @@ -367,7 +367,7 @@ def handle_schema_args(name, args): | |||
152 | 367 | if not args.annotate: | 367 | if not args.annotate: |
153 | 368 | error(str(e)) | 368 | error(str(e)) |
154 | 369 | except RuntimeError as e: | 369 | except RuntimeError as e: |
156 | 370 | error(str(e)) | 370 | error(str(e)) |
157 | 371 | else: | 371 | else: |
158 | 372 | print("Valid cloud-config file {0}".format(args.config_file)) | 372 | print("Valid cloud-config file {0}".format(args.config_file)) |
159 | 373 | if args.doc: | 373 | if args.doc: |
160 | diff --git a/cloudinit/config/tests/test_apt_pipelining.py b/cloudinit/config/tests/test_apt_pipelining.py | |||
161 | 374 | new file mode 100644 | 374 | new file mode 100644 |
162 | index 0000000..2a6bb10 | |||
163 | --- /dev/null | |||
164 | +++ b/cloudinit/config/tests/test_apt_pipelining.py | |||
165 | @@ -0,0 +1,28 @@ | |||
166 | 1 | # This file is part of cloud-init. See LICENSE file for license information. | ||
167 | 2 | |||
168 | 3 | """Tests cc_apt_pipelining handler""" | ||
169 | 4 | |||
170 | 5 | import cloudinit.config.cc_apt_pipelining as cc_apt_pipelining | ||
171 | 6 | |||
172 | 7 | from cloudinit.tests.helpers import CiTestCase, mock | ||
173 | 8 | |||
174 | 9 | |||
175 | 10 | class TestAptPipelining(CiTestCase): | ||
176 | 11 | |||
177 | 12 | @mock.patch('cloudinit.config.cc_apt_pipelining.util.write_file') | ||
178 | 13 | def test_not_disabled_by_default(self, m_write_file): | ||
179 | 14 | """ensure that default behaviour is to not disable pipelining""" | ||
180 | 15 | cc_apt_pipelining.handle('foo', {}, None, mock.MagicMock(), None) | ||
181 | 16 | self.assertEqual(0, m_write_file.call_count) | ||
182 | 17 | |||
183 | 18 | @mock.patch('cloudinit.config.cc_apt_pipelining.util.write_file') | ||
184 | 19 | def test_false_disables_pipelining(self, m_write_file): | ||
185 | 20 | """ensure that pipelining can be disabled with correct config""" | ||
186 | 21 | cc_apt_pipelining.handle( | ||
187 | 22 | 'foo', {'apt_pipelining': 'false'}, None, mock.MagicMock(), None) | ||
188 | 23 | self.assertEqual(1, m_write_file.call_count) | ||
189 | 24 | args, _ = m_write_file.call_args | ||
190 | 25 | self.assertEqual(cc_apt_pipelining.DEFAULT_FILE, args[0]) | ||
191 | 26 | self.assertIn('Pipeline-Depth "0"', args[1]) | ||
192 | 27 | |||
193 | 28 | # vi: ts=4 expandtab | ||
194 | diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py | |||
195 | index ef618c2..20c994d 100644 | |||
196 | --- a/cloudinit/distros/__init__.py | |||
197 | +++ b/cloudinit/distros/__init__.py | |||
198 | @@ -577,11 +577,16 @@ class Distro(object): | |||
199 | 577 | """ | 577 | """ |
200 | 578 | Lock the password of a user, i.e., disable password logins | 578 | Lock the password of a user, i.e., disable password logins |
201 | 579 | """ | 579 | """ |
202 | 580 | # passwd must use short '-l' due to SLES11 lacking long form '--lock' | ||
203 | 581 | lock_tools = (['passwd', '-l', name], ['usermod', '--lock', name]) | ||
204 | 580 | try: | 582 | try: |
209 | 581 | # Need to use the short option name '-l' instead of '--lock' | 583 | cmd = next(l for l in lock_tools if util.which(l[0])) |
210 | 582 | # (which would be more descriptive) since SLES 11 doesn't know | 584 | except StopIteration: |
211 | 583 | # about long names. | 585 | raise RuntimeError(( |
212 | 584 | util.subp(['passwd', '-l', name]) | 586 | "Unable to lock user account '%s'. No tools available. " |
213 | 587 | " Tried: %s.") % (name, [c[0] for c in lock_tools])) | ||
214 | 588 | try: | ||
215 | 589 | util.subp(cmd) | ||
216 | 585 | except Exception as e: | 590 | except Exception as e: |
217 | 586 | util.logexc(LOG, 'Failed to disable password for user %s', name) | 591 | util.logexc(LOG, 'Failed to disable password for user %s', name) |
218 | 587 | raise e | 592 | raise e |
219 | diff --git a/cloudinit/handlers/upstart_job.py b/cloudinit/handlers/upstart_job.py | |||
220 | index 83fb072..003cad6 100644 | |||
221 | --- a/cloudinit/handlers/upstart_job.py | |||
222 | +++ b/cloudinit/handlers/upstart_job.py | |||
223 | @@ -89,7 +89,7 @@ def _has_suitable_upstart(): | |||
224 | 89 | util.subp(["dpkg", "--compare-versions", dpkg_ver, "ge", good]) | 89 | util.subp(["dpkg", "--compare-versions", dpkg_ver, "ge", good]) |
225 | 90 | return True | 90 | return True |
226 | 91 | except util.ProcessExecutionError as e: | 91 | except util.ProcessExecutionError as e: |
228 | 92 | if e.exit_code is 1: | 92 | if e.exit_code == 1: |
229 | 93 | pass | 93 | pass |
230 | 94 | else: | 94 | else: |
231 | 95 | util.logexc(LOG, "dpkg --compare-versions failed [%s]", | 95 | util.logexc(LOG, "dpkg --compare-versions failed [%s]", |
232 | diff --git a/cloudinit/net/netplan.py b/cloudinit/net/netplan.py | |||
233 | index 21517fd..e54a34e 100644 | |||
234 | --- a/cloudinit/net/netplan.py | |||
235 | +++ b/cloudinit/net/netplan.py | |||
236 | @@ -361,7 +361,8 @@ class Renderer(renderer.Renderer): | |||
237 | 361 | if section: | 361 | if section: |
238 | 362 | dump = util.yaml_dumps({name: section}, | 362 | dump = util.yaml_dumps({name: section}, |
239 | 363 | explicit_start=False, | 363 | explicit_start=False, |
241 | 364 | explicit_end=False) | 364 | explicit_end=False, |
242 | 365 | noalias=True) | ||
243 | 365 | txt = util.indent(dump, ' ' * 4) | 366 | txt = util.indent(dump, ' ' * 4) |
244 | 366 | return [txt] | 367 | return [txt] |
245 | 367 | return [] | 368 | return [] |
246 | diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py | |||
247 | index f76e508..539b76d 100644 | |||
248 | --- a/cloudinit/net/network_state.py | |||
249 | +++ b/cloudinit/net/network_state.py | |||
250 | @@ -706,9 +706,9 @@ class NetworkStateInterpreter(object): | |||
251 | 706 | """Common ipconfig extraction from v2 to v1 subnets array.""" | 706 | """Common ipconfig extraction from v2 to v1 subnets array.""" |
252 | 707 | 707 | ||
253 | 708 | subnets = [] | 708 | subnets = [] |
255 | 709 | if 'dhcp4' in cfg: | 709 | if cfg.get('dhcp4'): |
256 | 710 | subnets.append({'type': 'dhcp4'}) | 710 | subnets.append({'type': 'dhcp4'}) |
258 | 711 | if 'dhcp6' in cfg: | 711 | if cfg.get('dhcp6'): |
259 | 712 | self.use_ipv6 = True | 712 | self.use_ipv6 = True |
260 | 713 | subnets.append({'type': 'dhcp6'}) | 713 | subnets.append({'type': 'dhcp6'}) |
261 | 714 | 714 | ||
262 | diff --git a/cloudinit/net/tests/test_dhcp.py b/cloudinit/net/tests/test_dhcp.py | |||
263 | index 79e8842..5139024 100644 | |||
264 | --- a/cloudinit/net/tests/test_dhcp.py | |||
265 | +++ b/cloudinit/net/tests/test_dhcp.py | |||
266 | @@ -117,6 +117,7 @@ class TestDHCPDiscoveryClean(CiTestCase): | |||
267 | 117 | self.assertEqual('eth9', call[0][1]) | 117 | self.assertEqual('eth9', call[0][1]) |
268 | 118 | self.assertIn('/var/tmp/cloud-init/cloud-init-dhcp-', call[0][2]) | 118 | self.assertIn('/var/tmp/cloud-init/cloud-init-dhcp-', call[0][2]) |
269 | 119 | 119 | ||
270 | 120 | @mock.patch('time.sleep', mock.MagicMock()) | ||
271 | 120 | @mock.patch('cloudinit.net.dhcp.os.kill') | 121 | @mock.patch('cloudinit.net.dhcp.os.kill') |
272 | 121 | @mock.patch('cloudinit.net.dhcp.util.subp') | 122 | @mock.patch('cloudinit.net.dhcp.util.subp') |
273 | 122 | def test_dhcp_discovery_run_in_sandbox_warns_invalid_pid(self, m_subp, | 123 | def test_dhcp_discovery_run_in_sandbox_warns_invalid_pid(self, m_subp, |
274 | diff --git a/cloudinit/netinfo.py b/cloudinit/netinfo.py | |||
275 | index 9ff929c..e91cd26 100644 | |||
276 | --- a/cloudinit/netinfo.py | |||
277 | +++ b/cloudinit/netinfo.py | |||
278 | @@ -141,6 +141,9 @@ def _netdev_info_ifconfig(ifconfig_data): | |||
279 | 141 | res = re.match(r'.*<(\S+)>', toks[i + 1]) | 141 | res = re.match(r'.*<(\S+)>', toks[i + 1]) |
280 | 142 | if res: | 142 | if res: |
281 | 143 | devs[curdev]['ipv6'][-1]['scope6'] = res.group(1) | 143 | devs[curdev]['ipv6'][-1]['scope6'] = res.group(1) |
282 | 144 | else: | ||
283 | 145 | devs[curdev]['ipv6'][-1]['scope6'] = toks[i + 1] | ||
284 | 146 | |||
285 | 144 | return devs | 147 | return devs |
286 | 145 | 148 | ||
287 | 146 | 149 | ||
288 | @@ -389,8 +392,8 @@ def netdev_pformat(): | |||
289 | 389 | addr.get('scope', empty), data["hwaddr"])) | 392 | addr.get('scope', empty), data["hwaddr"])) |
290 | 390 | for addr in data.get('ipv6'): | 393 | for addr in data.get('ipv6'): |
291 | 391 | tbl.add_row( | 394 | tbl.add_row( |
294 | 392 | (dev, data["up"], addr["ip"], empty, addr["scope6"], | 395 | (dev, data["up"], addr["ip"], empty, |
295 | 393 | data["hwaddr"])) | 396 | addr.get("scope6", empty), data["hwaddr"])) |
296 | 394 | if len(data.get('ipv6')) + len(data.get('ipv4')) == 0: | 397 | if len(data.get('ipv6')) + len(data.get('ipv4')) == 0: |
297 | 395 | tbl.add_row((dev, data["up"], empty, empty, empty, | 398 | tbl.add_row((dev, data["up"], empty, empty, empty, |
298 | 396 | data["hwaddr"])) | 399 | data["hwaddr"])) |
299 | diff --git a/cloudinit/safeyaml.py b/cloudinit/safeyaml.py | |||
300 | index 7bcf9dd..3bd5e03 100644 | |||
301 | --- a/cloudinit/safeyaml.py | |||
302 | +++ b/cloudinit/safeyaml.py | |||
303 | @@ -17,6 +17,13 @@ _CustomSafeLoader.add_constructor( | |||
304 | 17 | _CustomSafeLoader.construct_python_unicode) | 17 | _CustomSafeLoader.construct_python_unicode) |
305 | 18 | 18 | ||
306 | 19 | 19 | ||
307 | 20 | class NoAliasSafeDumper(yaml.dumper.SafeDumper): | ||
308 | 21 | """A class which avoids constructing anchors/aliases on yaml dump""" | ||
309 | 22 | |||
310 | 23 | def ignore_aliases(self, data): | ||
311 | 24 | return True | ||
312 | 25 | |||
313 | 26 | |||
314 | 20 | def load(blob): | 27 | def load(blob): |
315 | 21 | return(yaml.load(blob, Loader=_CustomSafeLoader)) | 28 | return(yaml.load(blob, Loader=_CustomSafeLoader)) |
316 | 22 | 29 | ||
317 | diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py | |||
318 | index a4f998b..eccbee5 100644 | |||
319 | --- a/cloudinit/sources/DataSourceAzure.py | |||
320 | +++ b/cloudinit/sources/DataSourceAzure.py | |||
321 | @@ -627,9 +627,11 @@ class DataSourceAzure(sources.DataSource): | |||
322 | 627 | if self.ds_cfg['agent_command'] == AGENT_START_BUILTIN: | 627 | if self.ds_cfg['agent_command'] == AGENT_START_BUILTIN: |
323 | 628 | self.bounce_network_with_azure_hostname() | 628 | self.bounce_network_with_azure_hostname() |
324 | 629 | 629 | ||
325 | 630 | pubkey_info = self.cfg.get('_pubkeys', None) | ||
326 | 630 | metadata_func = partial(get_metadata_from_fabric, | 631 | metadata_func = partial(get_metadata_from_fabric, |
327 | 631 | fallback_lease_file=self. | 632 | fallback_lease_file=self. |
329 | 632 | dhclient_lease_file) | 633 | dhclient_lease_file, |
330 | 634 | pubkey_info=pubkey_info) | ||
331 | 633 | else: | 635 | else: |
332 | 634 | metadata_func = self.get_metadata_from_agent | 636 | metadata_func = self.get_metadata_from_agent |
333 | 635 | 637 | ||
334 | @@ -642,6 +644,7 @@ class DataSourceAzure(sources.DataSource): | |||
335 | 642 | "Error communicating with Azure fabric; You may experience." | 644 | "Error communicating with Azure fabric; You may experience." |
336 | 643 | "connectivity issues.", exc_info=True) | 645 | "connectivity issues.", exc_info=True) |
337 | 644 | return False | 646 | return False |
338 | 647 | |||
339 | 645 | util.del_file(REPORTED_READY_MARKER_FILE) | 648 | util.del_file(REPORTED_READY_MARKER_FILE) |
340 | 646 | util.del_file(REPROVISION_MARKER_FILE) | 649 | util.del_file(REPROVISION_MARKER_FILE) |
341 | 647 | return fabric_data | 650 | return fabric_data |
342 | @@ -909,13 +912,15 @@ def find_child(node, filter_func): | |||
343 | 909 | def load_azure_ovf_pubkeys(sshnode): | 912 | def load_azure_ovf_pubkeys(sshnode): |
344 | 910 | # This parses a 'SSH' node formatted like below, and returns | 913 | # This parses a 'SSH' node formatted like below, and returns |
345 | 911 | # an array of dicts. | 914 | # an array of dicts. |
348 | 912 | # [{'fp': '6BE7A7C3C8A8F4B123CCA5D0C2F1BE4CA7B63ED7', | 915 | # [{'fingerprint': '6BE7A7C3C8A8F4B123CCA5D0C2F1BE4CA7B63ED7', |
349 | 913 | # 'path': 'where/to/go'}] | 916 | # 'path': '/where/to/go'}] |
350 | 914 | # | 917 | # |
351 | 915 | # <SSH><PublicKeys> | 918 | # <SSH><PublicKeys> |
353 | 916 | # <PublicKey><Fingerprint>ABC</FingerPrint><Path>/ABC</Path> | 919 | # <PublicKey><Fingerprint>ABC</FingerPrint><Path>/x/y/z</Path> |
354 | 917 | # ... | 920 | # ... |
355 | 918 | # </PublicKeys></SSH> | 921 | # </PublicKeys></SSH> |
356 | 922 | # Under some circumstances, there may be a <Value> element along with the | ||
357 | 923 | # Fingerprint and Path. Pass those along if they appear. | ||
358 | 919 | results = find_child(sshnode, lambda n: n.localName == "PublicKeys") | 924 | results = find_child(sshnode, lambda n: n.localName == "PublicKeys") |
359 | 920 | if len(results) == 0: | 925 | if len(results) == 0: |
360 | 921 | return [] | 926 | return [] |
361 | diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py | |||
362 | index 9ccf2cd..4f2f6cc 100644 | |||
363 | --- a/cloudinit/sources/DataSourceEc2.py | |||
364 | +++ b/cloudinit/sources/DataSourceEc2.py | |||
365 | @@ -19,6 +19,7 @@ from cloudinit import sources | |||
366 | 19 | from cloudinit import url_helper as uhelp | 19 | from cloudinit import url_helper as uhelp |
367 | 20 | from cloudinit import util | 20 | from cloudinit import util |
368 | 21 | from cloudinit import warnings | 21 | from cloudinit import warnings |
369 | 22 | from cloudinit.event import EventType | ||
370 | 22 | 23 | ||
371 | 23 | LOG = logging.getLogger(__name__) | 24 | LOG = logging.getLogger(__name__) |
372 | 24 | 25 | ||
373 | @@ -107,6 +108,19 @@ class DataSourceEc2(sources.DataSource): | |||
374 | 107 | 'dynamic', {}).get('instance-identity', {}).get('document', {}) | 108 | 'dynamic', {}).get('instance-identity', {}).get('document', {}) |
375 | 108 | return True | 109 | return True |
376 | 109 | 110 | ||
377 | 111 | def is_classic_instance(self): | ||
378 | 112 | """Report if this instance type is Ec2 Classic (non-vpc).""" | ||
379 | 113 | if not self.metadata: | ||
380 | 114 | # Can return False on inconclusive as we are also called in | ||
381 | 115 | # network_config where metadata will be present. | ||
382 | 116 | # Secondary call site is in packaging postinst script. | ||
383 | 117 | return False | ||
384 | 118 | ifaces_md = self.metadata.get('network', {}).get('interfaces', {}) | ||
385 | 119 | for _mac, mac_data in ifaces_md.get('macs', {}).items(): | ||
386 | 120 | if 'vpc-id' in mac_data: | ||
387 | 121 | return False | ||
388 | 122 | return True | ||
389 | 123 | |||
390 | 110 | @property | 124 | @property |
391 | 111 | def launch_index(self): | 125 | def launch_index(self): |
392 | 112 | if not self.metadata: | 126 | if not self.metadata: |
393 | @@ -320,6 +334,13 @@ class DataSourceEc2(sources.DataSource): | |||
394 | 320 | if isinstance(net_md, dict): | 334 | if isinstance(net_md, dict): |
395 | 321 | result = convert_ec2_metadata_network_config( | 335 | result = convert_ec2_metadata_network_config( |
396 | 322 | net_md, macs_to_nics=macs_to_nics, fallback_nic=iface) | 336 | net_md, macs_to_nics=macs_to_nics, fallback_nic=iface) |
397 | 337 | # RELEASE_BLOCKER: Xenial debian/postinst needs to add | ||
398 | 338 | # EventType.BOOT on upgrade path for classic. | ||
399 | 339 | |||
400 | 340 | # Non-VPC (aka Classic) Ec2 instances need to rewrite the | ||
401 | 341 | # network config file every boot due to MAC address change. | ||
402 | 342 | if self.is_classic_instance(): | ||
403 | 343 | self.update_events['network'].add(EventType.BOOT) | ||
404 | 323 | else: | 344 | else: |
405 | 324 | LOG.warning("Metadata 'network' key not valid: %s.", net_md) | 345 | LOG.warning("Metadata 'network' key not valid: %s.", net_md) |
406 | 325 | self._network_config = result | 346 | self._network_config = result |
407 | @@ -442,7 +463,7 @@ def identify_aws(data): | |||
408 | 442 | if (data['uuid'].startswith('ec2') and | 463 | if (data['uuid'].startswith('ec2') and |
409 | 443 | (data['uuid_source'] == 'hypervisor' or | 464 | (data['uuid_source'] == 'hypervisor' or |
410 | 444 | data['uuid'] == data['serial'])): | 465 | data['uuid'] == data['serial'])): |
412 | 445 | return CloudNames.AWS | 466 | return CloudNames.AWS |
413 | 446 | 467 | ||
414 | 447 | return None | 468 | return None |
415 | 448 | 469 | ||
416 | diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py | |||
417 | index 3a3fcdf..70e7a5c 100644 | |||
418 | --- a/cloudinit/sources/DataSourceOVF.py | |||
419 | +++ b/cloudinit/sources/DataSourceOVF.py | |||
420 | @@ -15,6 +15,8 @@ import os | |||
421 | 15 | import re | 15 | import re |
422 | 16 | import time | 16 | import time |
423 | 17 | 17 | ||
424 | 18 | import six | ||
425 | 19 | |||
426 | 18 | from cloudinit import log as logging | 20 | from cloudinit import log as logging |
427 | 19 | from cloudinit import sources | 21 | from cloudinit import sources |
428 | 20 | from cloudinit import util | 22 | from cloudinit import util |
429 | @@ -434,7 +436,7 @@ def maybe_cdrom_device(devname): | |||
430 | 434 | """ | 436 | """ |
431 | 435 | if not devname: | 437 | if not devname: |
432 | 436 | return False | 438 | return False |
434 | 437 | elif not isinstance(devname, util.string_types): | 439 | elif not isinstance(devname, six.string_types): |
435 | 438 | raise ValueError("Unexpected input for devname: %s" % devname) | 440 | raise ValueError("Unexpected input for devname: %s" % devname) |
436 | 439 | 441 | ||
437 | 440 | # resolve '..' and multi '/' elements | 442 | # resolve '..' and multi '/' elements |
438 | diff --git a/cloudinit/sources/helpers/azure.py b/cloudinit/sources/helpers/azure.py | |||
439 | index e5696b1..2829dd2 100644 | |||
440 | --- a/cloudinit/sources/helpers/azure.py | |||
441 | +++ b/cloudinit/sources/helpers/azure.py | |||
442 | @@ -138,9 +138,36 @@ class OpenSSLManager(object): | |||
443 | 138 | self.certificate = certificate | 138 | self.certificate = certificate |
444 | 139 | LOG.debug('New certificate generated.') | 139 | LOG.debug('New certificate generated.') |
445 | 140 | 140 | ||
449 | 141 | def parse_certificates(self, certificates_xml): | 141 | @staticmethod |
450 | 142 | tag = ElementTree.fromstring(certificates_xml).find( | 142 | def _run_x509_action(action, cert): |
451 | 143 | './/Data') | 143 | cmd = ['openssl', 'x509', '-noout', action] |
452 | 144 | result, _ = util.subp(cmd, data=cert) | ||
453 | 145 | return result | ||
454 | 146 | |||
455 | 147 | def _get_ssh_key_from_cert(self, certificate): | ||
456 | 148 | pub_key = self._run_x509_action('-pubkey', certificate) | ||
457 | 149 | keygen_cmd = ['ssh-keygen', '-i', '-m', 'PKCS8', '-f', '/dev/stdin'] | ||
458 | 150 | ssh_key, _ = util.subp(keygen_cmd, data=pub_key) | ||
459 | 151 | return ssh_key | ||
460 | 152 | |||
461 | 153 | def _get_fingerprint_from_cert(self, certificate): | ||
462 | 154 | """openssl x509 formats fingerprints as so: | ||
463 | 155 | 'SHA1 Fingerprint=07:3E:19:D1:4D:1C:79:92:24:C6:A0:FD:8D:DA:\ | ||
464 | 156 | B6:A8:BF:27:D4:73\n' | ||
465 | 157 | |||
466 | 158 | Azure control plane passes that fingerprint as so: | ||
467 | 159 | '073E19D14D1C799224C6A0FD8DDAB6A8BF27D473' | ||
468 | 160 | """ | ||
469 | 161 | raw_fp = self._run_x509_action('-fingerprint', certificate) | ||
470 | 162 | eq = raw_fp.find('=') | ||
471 | 163 | octets = raw_fp[eq+1:-1].split(':') | ||
472 | 164 | return ''.join(octets) | ||
473 | 165 | |||
474 | 166 | def _decrypt_certs_from_xml(self, certificates_xml): | ||
475 | 167 | """Decrypt the certificates XML document using the our private key; | ||
476 | 168 | return the list of certs and private keys contained in the doc. | ||
477 | 169 | """ | ||
478 | 170 | tag = ElementTree.fromstring(certificates_xml).find('.//Data') | ||
479 | 144 | certificates_content = tag.text | 171 | certificates_content = tag.text |
480 | 145 | lines = [ | 172 | lines = [ |
481 | 146 | b'MIME-Version: 1.0', | 173 | b'MIME-Version: 1.0', |
482 | @@ -151,32 +178,30 @@ class OpenSSLManager(object): | |||
483 | 151 | certificates_content.encode('utf-8'), | 178 | certificates_content.encode('utf-8'), |
484 | 152 | ] | 179 | ] |
485 | 153 | with cd(self.tmpdir): | 180 | with cd(self.tmpdir): |
486 | 154 | with open('Certificates.p7m', 'wb') as f: | ||
487 | 155 | f.write(b'\n'.join(lines)) | ||
488 | 156 | out, _ = util.subp( | 181 | out, _ = util.subp( |
490 | 157 | 'openssl cms -decrypt -in Certificates.p7m -inkey' | 182 | 'openssl cms -decrypt -in /dev/stdin -inkey' |
491 | 158 | ' {private_key} -recip {certificate} | openssl pkcs12 -nodes' | 183 | ' {private_key} -recip {certificate} | openssl pkcs12 -nodes' |
492 | 159 | ' -password pass:'.format(**self.certificate_names), | 184 | ' -password pass:'.format(**self.certificate_names), |
495 | 160 | shell=True) | 185 | shell=True, data=b'\n'.join(lines)) |
496 | 161 | private_keys, certificates = [], [] | 186 | return out |
497 | 187 | |||
498 | 188 | def parse_certificates(self, certificates_xml): | ||
499 | 189 | """Given the Certificates XML document, return a dictionary of | ||
500 | 190 | fingerprints and associated SSH keys derived from the certs.""" | ||
501 | 191 | out = self._decrypt_certs_from_xml(certificates_xml) | ||
502 | 162 | current = [] | 192 | current = [] |
503 | 193 | keys = {} | ||
504 | 163 | for line in out.splitlines(): | 194 | for line in out.splitlines(): |
505 | 164 | current.append(line) | 195 | current.append(line) |
506 | 165 | if re.match(r'[-]+END .*?KEY[-]+$', line): | 196 | if re.match(r'[-]+END .*?KEY[-]+$', line): |
508 | 166 | private_keys.append('\n'.join(current)) | 197 | # ignore private_keys |
509 | 167 | current = [] | 198 | current = [] |
510 | 168 | elif re.match(r'[-]+END .*?CERTIFICATE[-]+$', line): | 199 | elif re.match(r'[-]+END .*?CERTIFICATE[-]+$', line): |
512 | 169 | certificates.append('\n'.join(current)) | 200 | certificate = '\n'.join(current) |
513 | 201 | ssh_key = self._get_ssh_key_from_cert(certificate) | ||
514 | 202 | fingerprint = self._get_fingerprint_from_cert(certificate) | ||
515 | 203 | keys[fingerprint] = ssh_key | ||
516 | 170 | current = [] | 204 | current = [] |
517 | 171 | keys = [] | ||
518 | 172 | for certificate in certificates: | ||
519 | 173 | with cd(self.tmpdir): | ||
520 | 174 | public_key, _ = util.subp( | ||
521 | 175 | 'openssl x509 -noout -pubkey |' | ||
522 | 176 | 'ssh-keygen -i -m PKCS8 -f /dev/stdin', | ||
523 | 177 | data=certificate, | ||
524 | 178 | shell=True) | ||
525 | 179 | keys.append(public_key) | ||
526 | 180 | return keys | 205 | return keys |
527 | 181 | 206 | ||
528 | 182 | 207 | ||
529 | @@ -206,7 +231,6 @@ class WALinuxAgentShim(object): | |||
530 | 206 | self.dhcpoptions = dhcp_options | 231 | self.dhcpoptions = dhcp_options |
531 | 207 | self._endpoint = None | 232 | self._endpoint = None |
532 | 208 | self.openssl_manager = None | 233 | self.openssl_manager = None |
533 | 209 | self.values = {} | ||
534 | 210 | self.lease_file = fallback_lease_file | 234 | self.lease_file = fallback_lease_file |
535 | 211 | 235 | ||
536 | 212 | def clean_up(self): | 236 | def clean_up(self): |
537 | @@ -328,8 +352,9 @@ class WALinuxAgentShim(object): | |||
538 | 328 | LOG.debug('Azure endpoint found at %s', endpoint_ip_address) | 352 | LOG.debug('Azure endpoint found at %s', endpoint_ip_address) |
539 | 329 | return endpoint_ip_address | 353 | return endpoint_ip_address |
540 | 330 | 354 | ||
543 | 331 | def register_with_azure_and_fetch_data(self): | 355 | def register_with_azure_and_fetch_data(self, pubkey_info=None): |
544 | 332 | self.openssl_manager = OpenSSLManager() | 356 | if self.openssl_manager is None: |
545 | 357 | self.openssl_manager = OpenSSLManager() | ||
546 | 333 | http_client = AzureEndpointHttpClient(self.openssl_manager.certificate) | 358 | http_client = AzureEndpointHttpClient(self.openssl_manager.certificate) |
547 | 334 | LOG.info('Registering with Azure...') | 359 | LOG.info('Registering with Azure...') |
548 | 335 | attempts = 0 | 360 | attempts = 0 |
549 | @@ -347,16 +372,37 @@ class WALinuxAgentShim(object): | |||
550 | 347 | attempts += 1 | 372 | attempts += 1 |
551 | 348 | LOG.debug('Successfully fetched GoalState XML.') | 373 | LOG.debug('Successfully fetched GoalState XML.') |
552 | 349 | goal_state = GoalState(response.contents, http_client) | 374 | goal_state = GoalState(response.contents, http_client) |
555 | 350 | public_keys = [] | 375 | ssh_keys = [] |
556 | 351 | if goal_state.certificates_xml is not None: | 376 | if goal_state.certificates_xml is not None and pubkey_info is not None: |
557 | 352 | LOG.debug('Certificate XML found; parsing out public keys.') | 377 | LOG.debug('Certificate XML found; parsing out public keys.') |
559 | 353 | public_keys = self.openssl_manager.parse_certificates( | 378 | keys_by_fingerprint = self.openssl_manager.parse_certificates( |
560 | 354 | goal_state.certificates_xml) | 379 | goal_state.certificates_xml) |
564 | 355 | data = { | 380 | ssh_keys = self._filter_pubkeys(keys_by_fingerprint, pubkey_info) |
562 | 356 | 'public-keys': public_keys, | ||
563 | 357 | } | ||
565 | 358 | self._report_ready(goal_state, http_client) | 381 | self._report_ready(goal_state, http_client) |
567 | 359 | return data | 382 | return {'public-keys': ssh_keys} |
568 | 383 | |||
569 | 384 | def _filter_pubkeys(self, keys_by_fingerprint, pubkey_info): | ||
570 | 385 | """cloud-init expects a straightforward array of keys to be dropped | ||
571 | 386 | into the user's authorized_keys file. Azure control plane exposes | ||
572 | 387 | multiple public keys to the VM via wireserver. Select just the | ||
573 | 388 | user's key(s) and return them, ignoring any other certs. | ||
574 | 389 | """ | ||
575 | 390 | keys = [] | ||
576 | 391 | for pubkey in pubkey_info: | ||
577 | 392 | if 'value' in pubkey and pubkey['value']: | ||
578 | 393 | keys.append(pubkey['value']) | ||
579 | 394 | elif 'fingerprint' in pubkey and pubkey['fingerprint']: | ||
580 | 395 | fingerprint = pubkey['fingerprint'] | ||
581 | 396 | if fingerprint in keys_by_fingerprint: | ||
582 | 397 | keys.append(keys_by_fingerprint[fingerprint]) | ||
583 | 398 | else: | ||
584 | 399 | LOG.warning("ovf-env.xml specified PublicKey fingerprint " | ||
585 | 400 | "%s not found in goalstate XML", fingerprint) | ||
586 | 401 | else: | ||
587 | 402 | LOG.warning("ovf-env.xml specified PublicKey with neither " | ||
588 | 403 | "value nor fingerprint: %s", pubkey) | ||
589 | 404 | |||
590 | 405 | return keys | ||
591 | 360 | 406 | ||
592 | 361 | def _report_ready(self, goal_state, http_client): | 407 | def _report_ready(self, goal_state, http_client): |
593 | 362 | LOG.debug('Reporting ready to Azure fabric.') | 408 | LOG.debug('Reporting ready to Azure fabric.') |
594 | @@ -373,11 +419,12 @@ class WALinuxAgentShim(object): | |||
595 | 373 | LOG.info('Reported ready to Azure fabric.') | 419 | LOG.info('Reported ready to Azure fabric.') |
596 | 374 | 420 | ||
597 | 375 | 421 | ||
599 | 376 | def get_metadata_from_fabric(fallback_lease_file=None, dhcp_opts=None): | 422 | def get_metadata_from_fabric(fallback_lease_file=None, dhcp_opts=None, |
600 | 423 | pubkey_info=None): | ||
601 | 377 | shim = WALinuxAgentShim(fallback_lease_file=fallback_lease_file, | 424 | shim = WALinuxAgentShim(fallback_lease_file=fallback_lease_file, |
602 | 378 | dhcp_options=dhcp_opts) | 425 | dhcp_options=dhcp_opts) |
603 | 379 | try: | 426 | try: |
605 | 380 | return shim.register_with_azure_and_fetch_data() | 427 | return shim.register_with_azure_and_fetch_data(pubkey_info=pubkey_info) |
606 | 381 | finally: | 428 | finally: |
607 | 382 | shim.clean_up() | 429 | shim.clean_up() |
608 | 383 | 430 | ||
609 | diff --git a/cloudinit/sources/helpers/openstack.py b/cloudinit/sources/helpers/openstack.py | |||
610 | index 9c29cea..8f06911 100644 | |||
611 | --- a/cloudinit/sources/helpers/openstack.py | |||
612 | +++ b/cloudinit/sources/helpers/openstack.py | |||
613 | @@ -67,7 +67,7 @@ OS_VERSIONS = ( | |||
614 | 67 | OS_ROCKY, | 67 | OS_ROCKY, |
615 | 68 | ) | 68 | ) |
616 | 69 | 69 | ||
618 | 70 | PHYSICAL_TYPES = ( | 70 | KNOWN_PHYSICAL_TYPES = ( |
619 | 71 | None, | 71 | None, |
620 | 72 | 'bgpovs', # not present in OpenStack upstream but used on OVH cloud. | 72 | 'bgpovs', # not present in OpenStack upstream but used on OVH cloud. |
621 | 73 | 'bridge', | 73 | 'bridge', |
622 | @@ -600,9 +600,7 @@ def convert_net_json(network_json=None, known_macs=None): | |||
623 | 600 | subnet['ipv6'] = True | 600 | subnet['ipv6'] = True |
624 | 601 | subnets.append(subnet) | 601 | subnets.append(subnet) |
625 | 602 | cfg.update({'subnets': subnets}) | 602 | cfg.update({'subnets': subnets}) |
629 | 603 | if link['type'] in PHYSICAL_TYPES: | 603 | if link['type'] in ['bond']: |
627 | 604 | cfg.update({'type': 'physical', 'mac_address': link_mac_addr}) | ||
628 | 605 | elif link['type'] in ['bond']: | ||
630 | 606 | params = {} | 604 | params = {} |
631 | 607 | if link_mac_addr: | 605 | if link_mac_addr: |
632 | 608 | params['mac_address'] = link_mac_addr | 606 | params['mac_address'] = link_mac_addr |
633 | @@ -641,8 +639,10 @@ def convert_net_json(network_json=None, known_macs=None): | |||
634 | 641 | curinfo.update({'mac': link['vlan_mac_address'], | 639 | curinfo.update({'mac': link['vlan_mac_address'], |
635 | 642 | 'name': name}) | 640 | 'name': name}) |
636 | 643 | else: | 641 | else: |
639 | 644 | raise ValueError( | 642 | if link['type'] not in KNOWN_PHYSICAL_TYPES: |
640 | 645 | 'Unknown network_data link type: %s' % link['type']) | 643 | LOG.warning('Unknown network_data link type (%s); treating as' |
641 | 644 | ' physical', link['type']) | ||
642 | 645 | cfg.update({'type': 'physical', 'mac_address': link_mac_addr}) | ||
643 | 646 | 646 | ||
644 | 647 | config.append(cfg) | 647 | config.append(cfg) |
645 | 648 | link_id_info[curinfo['id']] = curinfo | 648 | link_id_info[curinfo['id']] = curinfo |
646 | diff --git a/cloudinit/stages.py b/cloudinit/stages.py | |||
647 | index 8a06412..da7d349 100644 | |||
648 | --- a/cloudinit/stages.py | |||
649 | +++ b/cloudinit/stages.py | |||
650 | @@ -548,11 +548,11 @@ class Init(object): | |||
651 | 548 | with events.ReportEventStack("consume-user-data", | 548 | with events.ReportEventStack("consume-user-data", |
652 | 549 | "reading and applying user-data", | 549 | "reading and applying user-data", |
653 | 550 | parent=self.reporter): | 550 | parent=self.reporter): |
655 | 551 | self._consume_userdata(frequency) | 551 | self._consume_userdata(frequency) |
656 | 552 | with events.ReportEventStack("consume-vendor-data", | 552 | with events.ReportEventStack("consume-vendor-data", |
657 | 553 | "reading and applying vendor-data", | 553 | "reading and applying vendor-data", |
658 | 554 | parent=self.reporter): | 554 | parent=self.reporter): |
660 | 555 | self._consume_vendordata(frequency) | 555 | self._consume_vendordata(frequency) |
661 | 556 | 556 | ||
662 | 557 | # Perform post-consumption adjustments so that | 557 | # Perform post-consumption adjustments so that |
663 | 558 | # modules that run during the init stage reflect | 558 | # modules that run during the init stage reflect |
664 | diff --git a/cloudinit/tests/helpers.py b/cloudinit/tests/helpers.py | |||
665 | index 2eb7b0c..f41180f 100644 | |||
666 | --- a/cloudinit/tests/helpers.py | |||
667 | +++ b/cloudinit/tests/helpers.py | |||
668 | @@ -41,26 +41,6 @@ _real_subp = util.subp | |||
669 | 41 | SkipTest = unittest2.SkipTest | 41 | SkipTest = unittest2.SkipTest |
670 | 42 | skipIf = unittest2.skipIf | 42 | skipIf = unittest2.skipIf |
671 | 43 | 43 | ||
672 | 44 | # Used for detecting different python versions | ||
673 | 45 | PY2 = False | ||
674 | 46 | PY26 = False | ||
675 | 47 | PY27 = False | ||
676 | 48 | PY3 = False | ||
677 | 49 | |||
678 | 50 | _PY_VER = sys.version_info | ||
679 | 51 | _PY_MAJOR, _PY_MINOR, _PY_MICRO = _PY_VER[0:3] | ||
680 | 52 | if (_PY_MAJOR, _PY_MINOR) <= (2, 6): | ||
681 | 53 | if (_PY_MAJOR, _PY_MINOR) == (2, 6): | ||
682 | 54 | PY26 = True | ||
683 | 55 | if (_PY_MAJOR, _PY_MINOR) >= (2, 0): | ||
684 | 56 | PY2 = True | ||
685 | 57 | else: | ||
686 | 58 | if (_PY_MAJOR, _PY_MINOR) == (2, 7): | ||
687 | 59 | PY27 = True | ||
688 | 60 | PY2 = True | ||
689 | 61 | if (_PY_MAJOR, _PY_MINOR) >= (3, 0): | ||
690 | 62 | PY3 = True | ||
691 | 63 | |||
692 | 64 | 44 | ||
693 | 65 | # Makes the old path start | 45 | # Makes the old path start |
694 | 66 | # with new base instead of whatever | 46 | # with new base instead of whatever |
695 | @@ -207,6 +187,7 @@ class CiTestCase(TestCase): | |||
696 | 207 | if self.with_logs: | 187 | if self.with_logs: |
697 | 208 | # Remove the handler we setup | 188 | # Remove the handler we setup |
698 | 209 | logging.getLogger().handlers = self.old_handlers | 189 | logging.getLogger().handlers = self.old_handlers |
699 | 190 | logging.getLogger().level = None | ||
700 | 210 | util.subp = _real_subp | 191 | util.subp = _real_subp |
701 | 211 | super(CiTestCase, self).tearDown() | 192 | super(CiTestCase, self).tearDown() |
702 | 212 | 193 | ||
703 | @@ -356,7 +337,7 @@ class FilesystemMockingTestCase(ResourceUsingTestCase): | |||
704 | 356 | 337 | ||
705 | 357 | def patchOpen(self, new_root): | 338 | def patchOpen(self, new_root): |
706 | 358 | trap_func = retarget_many_wrapper(new_root, 1, open) | 339 | trap_func = retarget_many_wrapper(new_root, 1, open) |
708 | 359 | name = 'builtins.open' if PY3 else '__builtin__.open' | 340 | name = 'builtins.open' if six.PY3 else '__builtin__.open' |
709 | 360 | self.patched_funcs.enter_context(mock.patch(name, trap_func)) | 341 | self.patched_funcs.enter_context(mock.patch(name, trap_func)) |
710 | 361 | 342 | ||
711 | 362 | def patchStdoutAndStderr(self, stdout=None, stderr=None): | 343 | def patchStdoutAndStderr(self, stdout=None, stderr=None): |
712 | diff --git a/cloudinit/tests/test_netinfo.py b/cloudinit/tests/test_netinfo.py | |||
713 | index d76e768..1c8a791 100644 | |||
714 | --- a/cloudinit/tests/test_netinfo.py | |||
715 | +++ b/cloudinit/tests/test_netinfo.py | |||
716 | @@ -11,6 +11,7 @@ from cloudinit.tests.helpers import CiTestCase, mock, readResource | |||
717 | 11 | # Example ifconfig and route output | 11 | # Example ifconfig and route output |
718 | 12 | SAMPLE_OLD_IFCONFIG_OUT = readResource("netinfo/old-ifconfig-output") | 12 | SAMPLE_OLD_IFCONFIG_OUT = readResource("netinfo/old-ifconfig-output") |
719 | 13 | SAMPLE_NEW_IFCONFIG_OUT = readResource("netinfo/new-ifconfig-output") | 13 | SAMPLE_NEW_IFCONFIG_OUT = readResource("netinfo/new-ifconfig-output") |
720 | 14 | SAMPLE_FREEBSD_IFCONFIG_OUT = readResource("netinfo/freebsd-ifconfig-output") | ||
721 | 14 | SAMPLE_IPADDRSHOW_OUT = readResource("netinfo/sample-ipaddrshow-output") | 15 | SAMPLE_IPADDRSHOW_OUT = readResource("netinfo/sample-ipaddrshow-output") |
722 | 15 | SAMPLE_ROUTE_OUT_V4 = readResource("netinfo/sample-route-output-v4") | 16 | SAMPLE_ROUTE_OUT_V4 = readResource("netinfo/sample-route-output-v4") |
723 | 16 | SAMPLE_ROUTE_OUT_V6 = readResource("netinfo/sample-route-output-v6") | 17 | SAMPLE_ROUTE_OUT_V6 = readResource("netinfo/sample-route-output-v6") |
724 | @@ -18,6 +19,7 @@ SAMPLE_IPROUTE_OUT_V4 = readResource("netinfo/sample-iproute-output-v4") | |||
725 | 18 | SAMPLE_IPROUTE_OUT_V6 = readResource("netinfo/sample-iproute-output-v6") | 19 | SAMPLE_IPROUTE_OUT_V6 = readResource("netinfo/sample-iproute-output-v6") |
726 | 19 | NETDEV_FORMATTED_OUT = readResource("netinfo/netdev-formatted-output") | 20 | NETDEV_FORMATTED_OUT = readResource("netinfo/netdev-formatted-output") |
727 | 20 | ROUTE_FORMATTED_OUT = readResource("netinfo/route-formatted-output") | 21 | ROUTE_FORMATTED_OUT = readResource("netinfo/route-formatted-output") |
728 | 22 | FREEBSD_NETDEV_OUT = readResource("netinfo/freebsd-netdev-formatted-output") | ||
729 | 21 | 23 | ||
730 | 22 | 24 | ||
731 | 23 | class TestNetInfo(CiTestCase): | 25 | class TestNetInfo(CiTestCase): |
732 | @@ -45,6 +47,18 @@ class TestNetInfo(CiTestCase): | |||
733 | 45 | 47 | ||
734 | 46 | @mock.patch('cloudinit.netinfo.util.which') | 48 | @mock.patch('cloudinit.netinfo.util.which') |
735 | 47 | @mock.patch('cloudinit.netinfo.util.subp') | 49 | @mock.patch('cloudinit.netinfo.util.subp') |
736 | 50 | def test_netdev_freebsd_nettools_pformat(self, m_subp, m_which): | ||
737 | 51 | """netdev_pformat properly rendering netdev new nettools info.""" | ||
738 | 52 | m_subp.return_value = (SAMPLE_FREEBSD_IFCONFIG_OUT, '') | ||
739 | 53 | m_which.side_effect = lambda x: x if x == 'ifconfig' else None | ||
740 | 54 | content = netdev_pformat() | ||
741 | 55 | print() | ||
742 | 56 | print(content) | ||
743 | 57 | print() | ||
744 | 58 | self.assertEqual(FREEBSD_NETDEV_OUT, content) | ||
745 | 59 | |||
746 | 60 | @mock.patch('cloudinit.netinfo.util.which') | ||
747 | 61 | @mock.patch('cloudinit.netinfo.util.subp') | ||
748 | 48 | def test_netdev_iproute_pformat(self, m_subp, m_which): | 62 | def test_netdev_iproute_pformat(self, m_subp, m_which): |
749 | 49 | """netdev_pformat properly rendering ip route info.""" | 63 | """netdev_pformat properly rendering ip route info.""" |
750 | 50 | m_subp.return_value = (SAMPLE_IPADDRSHOW_OUT, '') | 64 | m_subp.return_value = (SAMPLE_IPADDRSHOW_OUT, '') |
751 | diff --git a/cloudinit/url_helper.py b/cloudinit/url_helper.py | |||
752 | index 396d69a..0af0d9e 100644 | |||
753 | --- a/cloudinit/url_helper.py | |||
754 | +++ b/cloudinit/url_helper.py | |||
755 | @@ -521,7 +521,7 @@ class OauthUrlHelper(object): | |||
756 | 521 | if extra_exception_cb: | 521 | if extra_exception_cb: |
757 | 522 | ret = extra_exception_cb(msg, exception) | 522 | ret = extra_exception_cb(msg, exception) |
758 | 523 | finally: | 523 | finally: |
760 | 524 | self.exception_cb(msg, exception) | 524 | self.exception_cb(msg, exception) |
761 | 525 | return ret | 525 | return ret |
762 | 526 | 526 | ||
763 | 527 | def _headers_cb(self, extra_headers_cb, url): | 527 | def _headers_cb(self, extra_headers_cb, url): |
764 | diff --git a/cloudinit/util.py b/cloudinit/util.py | |||
765 | index a8a232b..a192091 100644 | |||
766 | --- a/cloudinit/util.py | |||
767 | +++ b/cloudinit/util.py | |||
768 | @@ -51,11 +51,6 @@ from cloudinit import version | |||
769 | 51 | 51 | ||
770 | 52 | from cloudinit.settings import (CFG_BUILTIN) | 52 | from cloudinit.settings import (CFG_BUILTIN) |
771 | 53 | 53 | ||
772 | 54 | try: | ||
773 | 55 | string_types = (basestring,) | ||
774 | 56 | except NameError: | ||
775 | 57 | string_types = (str,) | ||
776 | 58 | |||
777 | 59 | _DNS_REDIRECT_IP = None | 54 | _DNS_REDIRECT_IP = None |
778 | 60 | LOG = logging.getLogger(__name__) | 55 | LOG = logging.getLogger(__name__) |
779 | 61 | 56 | ||
780 | @@ -77,7 +72,6 @@ CONTAINER_TESTS = (['systemd-detect-virt', '--quiet', '--container'], | |||
781 | 77 | PROC_CMDLINE = None | 72 | PROC_CMDLINE = None |
782 | 78 | 73 | ||
783 | 79 | _LSB_RELEASE = {} | 74 | _LSB_RELEASE = {} |
784 | 80 | PY26 = sys.version_info[0:2] == (2, 6) | ||
785 | 81 | 75 | ||
786 | 82 | 76 | ||
787 | 83 | def get_architecture(target=None): | 77 | def get_architecture(target=None): |
788 | @@ -125,7 +119,7 @@ def target_path(target, path=None): | |||
789 | 125 | # return 'path' inside target, accepting target as None | 119 | # return 'path' inside target, accepting target as None |
790 | 126 | if target in (None, ""): | 120 | if target in (None, ""): |
791 | 127 | target = "/" | 121 | target = "/" |
793 | 128 | elif not isinstance(target, string_types): | 122 | elif not isinstance(target, six.string_types): |
794 | 129 | raise ValueError("Unexpected input for target: %s" % target) | 123 | raise ValueError("Unexpected input for target: %s" % target) |
795 | 130 | else: | 124 | else: |
796 | 131 | target = os.path.abspath(target) | 125 | target = os.path.abspath(target) |
797 | @@ -1596,14 +1590,17 @@ def json_dumps(data): | |||
798 | 1596 | separators=(',', ': '), default=json_serialize_default) | 1590 | separators=(',', ': '), default=json_serialize_default) |
799 | 1597 | 1591 | ||
800 | 1598 | 1592 | ||
802 | 1599 | def yaml_dumps(obj, explicit_start=True, explicit_end=True): | 1593 | def yaml_dumps(obj, explicit_start=True, explicit_end=True, noalias=False): |
803 | 1600 | """Return data in nicely formatted yaml.""" | 1594 | """Return data in nicely formatted yaml.""" |
810 | 1601 | return yaml.safe_dump(obj, | 1595 | |
811 | 1602 | line_break="\n", | 1596 | return yaml.dump(obj, |
812 | 1603 | indent=4, | 1597 | line_break="\n", |
813 | 1604 | explicit_start=explicit_start, | 1598 | indent=4, |
814 | 1605 | explicit_end=explicit_end, | 1599 | explicit_start=explicit_start, |
815 | 1606 | default_flow_style=False) | 1600 | explicit_end=explicit_end, |
816 | 1601 | default_flow_style=False, | ||
817 | 1602 | Dumper=(safeyaml.NoAliasSafeDumper | ||
818 | 1603 | if noalias else yaml.dumper.Dumper)) | ||
819 | 1607 | 1604 | ||
820 | 1608 | 1605 | ||
821 | 1609 | def ensure_dir(path, mode=None): | 1606 | def ensure_dir(path, mode=None): |
822 | @@ -2817,9 +2814,6 @@ def load_shell_content(content, add_empty=False, empty_val=None): | |||
823 | 2817 | variables. Set their value to empty_val.""" | 2814 | variables. Set their value to empty_val.""" |
824 | 2818 | 2815 | ||
825 | 2819 | def _shlex_split(blob): | 2816 | def _shlex_split(blob): |
826 | 2820 | if PY26 and isinstance(blob, six.text_type): | ||
827 | 2821 | # Older versions don't support unicode input | ||
828 | 2822 | blob = blob.encode("utf8") | ||
829 | 2823 | return shlex.split(blob, comments=True) | 2817 | return shlex.split(blob, comments=True) |
830 | 2824 | 2818 | ||
831 | 2825 | data = {} | 2819 | data = {} |
832 | diff --git a/debian/changelog b/debian/changelog | |||
833 | index 70f1879..61f09d7 100644 | |||
834 | --- a/debian/changelog | |||
835 | +++ b/debian/changelog | |||
836 | @@ -1,3 +1,47 @@ | |||
837 | 1 | cloud-init (18.5-44-g7c07af28-0ubuntu1) disco; urgency=medium | ||
838 | 2 | |||
839 | 3 | * New upstream snapshot. | ||
840 | 4 | - Support locking user with usermod if passwd is not available. | ||
841 | 5 | [Scott Moser] | ||
842 | 6 | - Example for Microsoft Azure data disk added. [Anton Olifir] | ||
843 | 7 | - clean: correctly determine the path for excluding seed directory | ||
844 | 8 | (LP: #1818571) | ||
845 | 9 | - helpers/openstack: Treat unknown link types as physical (LP: #1639263) | ||
846 | 10 | - drop Python 2.6 support and our NIH version detection | ||
847 | 11 | - tip-pylint: Fix assignment-from-return-none errors | ||
848 | 12 | - net: append type:dhcp[46] only if dhcp[46] is True in v2 netconfig | ||
849 | 13 | [Kurt Stieger] (LP: #1818032) | ||
850 | 14 | - cc_apt_pipelining: stop disabling pipelining by default (LP: #1794982) | ||
851 | 15 | - tests: fix some slow tests and some leaking state | ||
852 | 16 | - util: don't determine string_types ourselves | ||
853 | 17 | - cc_rsyslog: Escape possible nested set (LP: #1816967) | ||
854 | 18 | - Enable encrypted_data_bag_secret support for Chef | ||
855 | 19 | [Eric Williams] (LP: #1817082) | ||
856 | 20 | - azure: Filter list of ssh keys pulled from fabric [Jason Zions (MSFT)] | ||
857 | 21 | - doc: update merging doc with fixes and some additional details/examples | ||
858 | 22 | - tests: integration test failure summary to use traceback if empty error | ||
859 | 23 | - This is to fix https://bugs.launchpad.net/cloud-init/+bug/1812676 | ||
860 | 24 | [Vitaly Kuznetsov] | ||
861 | 25 | - EC2: Rewrite network config on AWS Classic instances every boot | ||
862 | 26 | [Guilherme G. Piccoli] (LP: #1802073) | ||
863 | 27 | - netinfo: Adjust ifconfig output parsing for FreeBSD ipv6 entries | ||
864 | 28 | (LP: #1779672) | ||
865 | 29 | - netplan: Don't render yaml aliases when dumping netplan (LP: #1815051) | ||
866 | 30 | - add PyCharm IDE .idea/ path to .gitignore [Dominic Schlegel] | ||
867 | 31 | - correct grammar issue in instance metadata documentation | ||
868 | 32 | [Dominic Schlegel] (LP: #1802188) | ||
869 | 33 | - clean: cloud-init clean should not trace when run from within cloud_dir | ||
870 | 34 | (LP: #1795508) | ||
871 | 35 | - Resolve flake8 comparison and pycodestyle over-ident issues | ||
872 | 36 | [Paride Legovini] | ||
873 | 37 | * Update netplan dependency package (LP: #1813667) | ||
874 | 38 | * Fix build-depends-on-obsolete-package for dh-systemd | ||
875 | 39 | * Change Priority from extra to optional | ||
876 | 40 | * Override lintian warnings about WantedBy=cloud-init.target | ||
877 | 41 | * Change Maintainer to Ubuntu Developers | ||
878 | 42 | |||
879 | 43 | -- Daniel Watkins <oddbloke@ubuntu.com> Thu, 07 Mar 2019 10:32:26 -0500 | ||
880 | 44 | |||
881 | 1 | cloud-init (18.5-21-g8ee294d5-0ubuntu1) disco; urgency=medium | 45 | cloud-init (18.5-21-g8ee294d5-0ubuntu1) disco; urgency=medium |
882 | 2 | 46 | ||
883 | 3 | * New upstream snapshot. | 47 | * New upstream snapshot. |
884 | diff --git a/debian/cloud-init.lintian-overrides b/debian/cloud-init.lintian-overrides | |||
885 | 4 | new file mode 100644 | 48 | new file mode 100644 |
886 | index 0000000..58fac0d | |||
887 | --- /dev/null | |||
888 | +++ b/debian/cloud-init.lintian-overrides | |||
889 | @@ -0,0 +1,4 @@ | |||
890 | 1 | cloud-init binary: systemd-service-file-refers-to-unusual-wantedby-target lib/systemd/system/cloud-config.service cloud-init.target | ||
891 | 2 | cloud-init binary: systemd-service-file-refers-to-unusual-wantedby-target lib/systemd/system/cloud-final.service cloud-init.target | ||
892 | 3 | cloud-init binary: systemd-service-file-refers-to-unusual-wantedby-target lib/systemd/system/cloud-init-local.service cloud-init.target | ||
893 | 4 | cloud-init binary: systemd-service-file-refers-to-unusual-wantedby-target lib/systemd/system/cloud-init.service cloud-init.target | ||
894 | diff --git a/debian/control b/debian/control | |||
895 | index 282304a..b1e2e8f 100644 | |||
896 | --- a/debian/control | |||
897 | +++ b/debian/control | |||
898 | @@ -1,10 +1,9 @@ | |||
899 | 1 | Source: cloud-init | 1 | Source: cloud-init |
900 | 2 | Section: admin | 2 | Section: admin |
904 | 3 | Priority: extra | 3 | Priority: optional |
905 | 4 | Maintainer: Scott Moser <smoser@ubuntu.com> | 4 | Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
906 | 5 | Build-Depends: debhelper (>= 9), | 5 | Build-Depends: debhelper (>= 9.20160709), |
907 | 6 | dh-python, | 6 | dh-python, |
908 | 7 | dh-systemd, | ||
909 | 8 | iproute2, | 7 | iproute2, |
910 | 9 | pep8, | 8 | pep8, |
911 | 10 | po-debconf, | 9 | po-debconf, |
912 | @@ -36,7 +35,7 @@ Architecture: all | |||
913 | 36 | Depends: cloud-guest-utils | cloud-utils, | 35 | Depends: cloud-guest-utils | cloud-utils, |
914 | 37 | isc-dhcp-client, | 36 | isc-dhcp-client, |
915 | 38 | iproute2, | 37 | iproute2, |
917 | 39 | nplan | ifupdown, | 38 | netplan.io | ifupdown, |
918 | 40 | procps, | 39 | procps, |
919 | 41 | python3, | 40 | python3, |
920 | 42 | python3-requests, | 41 | python3-requests, |
921 | diff --git a/doc/examples/cloud-config-chef.txt b/doc/examples/cloud-config-chef.txt | |||
922 | index defc5a5..2320e01 100644 | |||
923 | --- a/doc/examples/cloud-config-chef.txt | |||
924 | +++ b/doc/examples/cloud-config-chef.txt | |||
925 | @@ -98,6 +98,9 @@ chef: | |||
926 | 98 | # to the install script | 98 | # to the install script |
927 | 99 | omnibus_version: "12.3.0" | 99 | omnibus_version: "12.3.0" |
928 | 100 | 100 | ||
929 | 101 | # If encrypted data bags are used, the client needs to have a secrets file | ||
930 | 102 | # configured to decrypt them | ||
931 | 103 | encrypted_data_bag_secret: "/etc/chef/encrypted_data_bag_secret" | ||
932 | 101 | 104 | ||
933 | 102 | # Capture all subprocess output into a logfile | 105 | # Capture all subprocess output into a logfile |
934 | 103 | # Useful for troubleshooting cloud-init issues | 106 | # Useful for troubleshooting cloud-init issues |
935 | diff --git a/doc/examples/cloud-config-disk-setup.txt b/doc/examples/cloud-config-disk-setup.txt | |||
936 | index 43a62a2..89d9ff5 100644 | |||
937 | --- a/doc/examples/cloud-config-disk-setup.txt | |||
938 | +++ b/doc/examples/cloud-config-disk-setup.txt | |||
939 | @@ -17,7 +17,7 @@ fs_setup: | |||
940 | 17 | device: ephemeral0 | 17 | device: ephemeral0 |
941 | 18 | partition: auto | 18 | partition: auto |
942 | 19 | 19 | ||
944 | 20 | # Default disk definitions for Windows Azure | 20 | # Default disk definitions for Microsoft Azure |
945 | 21 | # ------------------------------------------ | 21 | # ------------------------------------------ |
946 | 22 | 22 | ||
947 | 23 | device_aliases: {'ephemeral0': '/dev/sdb'} | 23 | device_aliases: {'ephemeral0': '/dev/sdb'} |
948 | @@ -34,6 +34,21 @@ fs_setup: | |||
949 | 34 | replace_fs: ntfs | 34 | replace_fs: ntfs |
950 | 35 | 35 | ||
951 | 36 | 36 | ||
952 | 37 | # Data disks definitions for Microsoft Azure | ||
953 | 38 | # ------------------------------------------ | ||
954 | 39 | |||
955 | 40 | disk_setup: | ||
956 | 41 | /dev/disk/azure/scsi1/lun0: | ||
957 | 42 | table_type: gpt | ||
958 | 43 | layout: True | ||
959 | 44 | overwrite: True | ||
960 | 45 | |||
961 | 46 | fs_setup: | ||
962 | 47 | - device: /dev/disk/azure/scsi1/lun0 | ||
963 | 48 | partition: 1 | ||
964 | 49 | filesystem: ext4 | ||
965 | 50 | |||
966 | 51 | |||
967 | 37 | # Default disk definitions for SmartOS | 52 | # Default disk definitions for SmartOS |
968 | 38 | # ------------------------------------ | 53 | # ------------------------------------ |
969 | 39 | 54 | ||
970 | @@ -242,7 +257,7 @@ fs_setup: | |||
971 | 242 | # | 257 | # |
972 | 243 | # "false": If an existing file system exists, skip the creation. | 258 | # "false": If an existing file system exists, skip the creation. |
973 | 244 | # | 259 | # |
975 | 245 | # <REPLACE_FS>: This is a special directive, used for Windows Azure that | 260 | # <REPLACE_FS>: This is a special directive, used for Microsoft Azure that |
976 | 246 | # instructs cloud-init to replace a file system of <FS_TYPE>. NOTE: | 261 | # instructs cloud-init to replace a file system of <FS_TYPE>. NOTE: |
977 | 247 | # unless you define a label, this requires the use of the 'any' partition | 262 | # unless you define a label, this requires the use of the 'any' partition |
978 | 248 | # directive. | 263 | # directive. |
979 | diff --git a/doc/rtd/topics/datasources/ec2.rst b/doc/rtd/topics/datasources/ec2.rst | |||
980 | index 64c325d..76beca9 100644 | |||
981 | --- a/doc/rtd/topics/datasources/ec2.rst | |||
982 | +++ b/doc/rtd/topics/datasources/ec2.rst | |||
983 | @@ -90,4 +90,15 @@ An example configuration with the default values is provided below: | |||
984 | 90 | max_wait: 120 | 90 | max_wait: 120 |
985 | 91 | timeout: 50 | 91 | timeout: 50 |
986 | 92 | 92 | ||
987 | 93 | Notes | ||
988 | 94 | ----- | ||
989 | 95 | * There are 2 types of EC2 instances network-wise: VPC ones (Virtual Private | ||
990 | 96 | Cloud) and Classic ones (also known as non-VPC). One major difference | ||
991 | 97 | between them is that Classic instances have their MAC address changed on | ||
992 | 98 | stop/restart operations, so cloud-init will recreate the network config | ||
993 | 99 | file for EC2 Classic instances every boot. On VPC instances this file is | ||
994 | 100 | generated only in the first boot of the instance. | ||
995 | 101 | The check for the instance type is performed by is_classic_instance() | ||
996 | 102 | method. | ||
997 | 103 | |||
998 | 93 | .. vi: textwidth=78 | 104 | .. vi: textwidth=78 |
999 | diff --git a/doc/rtd/topics/instancedata.rst b/doc/rtd/topics/instancedata.rst | |||
1000 | index 5d2dc94..231a008 100644 | |||
1001 | --- a/doc/rtd/topics/instancedata.rst | |||
1002 | +++ b/doc/rtd/topics/instancedata.rst | |||
1003 | @@ -4,7 +4,7 @@ | |||
1004 | 4 | Instance Metadata | 4 | Instance Metadata |
1005 | 5 | ***************** | 5 | ***************** |
1006 | 6 | 6 | ||
1008 | 7 | What is a instance data? | 7 | What is instance data? |
1009 | 8 | ======================== | 8 | ======================== |
1010 | 9 | 9 | ||
1011 | 10 | Instance data is the collection of all configuration data that cloud-init | 10 | Instance data is the collection of all configuration data that cloud-init |
1012 | diff --git a/doc/rtd/topics/merging.rst b/doc/rtd/topics/merging.rst | |||
1013 | index c75ca59..5f7ca18 100644 | |||
1014 | --- a/doc/rtd/topics/merging.rst | |||
1015 | +++ b/doc/rtd/topics/merging.rst | |||
1016 | @@ -21,12 +21,12 @@ For example. | |||
1017 | 21 | .. code-block:: yaml | 21 | .. code-block:: yaml |
1018 | 22 | 22 | ||
1019 | 23 | #cloud-config (1) | 23 | #cloud-config (1) |
1021 | 24 | run_cmd: | 24 | runcmd: |
1022 | 25 | - bash1 | 25 | - bash1 |
1023 | 26 | - bash2 | 26 | - bash2 |
1024 | 27 | 27 | ||
1025 | 28 | #cloud-config (2) | 28 | #cloud-config (2) |
1027 | 29 | run_cmd: | 29 | runcmd: |
1028 | 30 | - bash3 | 30 | - bash3 |
1029 | 31 | - bash4 | 31 | - bash4 |
1030 | 32 | 32 | ||
1031 | @@ -36,7 +36,7 @@ cloud-config object that contains the following. | |||
1032 | 36 | .. code-block:: yaml | 36 | .. code-block:: yaml |
1033 | 37 | 37 | ||
1034 | 38 | #cloud-config (merged) | 38 | #cloud-config (merged) |
1036 | 39 | run_cmd: | 39 | runcmd: |
1037 | 40 | - bash3 | 40 | - bash3 |
1038 | 41 | - bash4 | 41 | - bash4 |
1039 | 42 | 42 | ||
1040 | @@ -45,7 +45,7 @@ Typically this is not what users want; instead they would likely prefer: | |||
1041 | 45 | .. code-block:: yaml | 45 | .. code-block:: yaml |
1042 | 46 | 46 | ||
1043 | 47 | #cloud-config (merged) | 47 | #cloud-config (merged) |
1045 | 48 | run_cmd: | 48 | runcmd: |
1046 | 49 | - bash1 | 49 | - bash1 |
1047 | 50 | - bash2 | 50 | - bash2 |
1048 | 51 | - bash3 | 51 | - bash3 |
1049 | @@ -55,6 +55,45 @@ This way makes it easier to combine the various cloud-config objects you have | |||
1050 | 55 | into a more useful list, thus reducing duplication necessary to accomplish the | 55 | into a more useful list, thus reducing duplication necessary to accomplish the |
1051 | 56 | same result with the previous method. | 56 | same result with the previous method. |
1052 | 57 | 57 | ||
1053 | 58 | |||
1054 | 59 | Built-in Mergers | ||
1055 | 60 | ================ | ||
1056 | 61 | |||
1057 | 62 | Cloud-init provides merging for the following built-in types: | ||
1058 | 63 | |||
1059 | 64 | - Dict | ||
1060 | 65 | - List | ||
1061 | 66 | - String | ||
1062 | 67 | |||
1063 | 68 | The ``Dict`` merger has the following options which control what is done with | ||
1064 | 69 | values contained within the config. | ||
1065 | 70 | |||
1066 | 71 | - ``allow_delete``: Existing values not present in the new value can be deleted, defaults to False | ||
1067 | 72 | - ``no_replace``: Do not replace an existing value if one is already present, enabled by default. | ||
1068 | 73 | - ``replace``: Overwrite existing values with new ones. | ||
1069 | 74 | |||
1070 | 75 | The ``List`` merger has the following options which control what is done with | ||
1071 | 76 | the values contained within the config. | ||
1072 | 77 | |||
1073 | 78 | - ``append``: Add new value to the end of the list, defaults to False. | ||
1074 | 79 | - ``prepend``: Add new values to the start of the list, defaults to False. | ||
1075 | 80 | - ``no_replace``: Do not replace an existing value if one is already present, enabled by default. | ||
1076 | 81 | - ``replace``: Overwrite existing values with new ones. | ||
1077 | 82 | |||
1078 | 83 | The ``Str`` merger has the following options which control what is done with | ||
1079 | 84 | the values contained within the config. | ||
1080 | 85 | |||
1081 | 86 | - ``append``: Add new value to the end of the string, defaults to False. | ||
1082 | 87 | |||
1083 | 88 | Common options for all merge types which control how recursive merging is | ||
1084 | 89 | done on other types. | ||
1085 | 90 | |||
1086 | 91 | - ``recurse_dict``: If True merge the new values of the dictionary, defaults to True. | ||
1087 | 92 | - ``recurse_list``: If True merge the new values of the list, defaults to False. | ||
1088 | 93 | - ``recurse_array``: Alias for ``recurse_list``. | ||
1089 | 94 | - ``recurse_str``: If True merge the new values of the string, defaults to False. | ||
1090 | 95 | |||
1091 | 96 | |||
1092 | 58 | Customizability | 97 | Customizability |
1093 | 59 | =============== | 98 | =============== |
1094 | 60 | 99 | ||
1095 | @@ -164,8 +203,8 @@ string format (i.e. the second option above), for example: | |||
1096 | 164 | 203 | ||
1097 | 165 | .. code-block:: python | 204 | .. code-block:: python |
1098 | 166 | 205 | ||
1101 | 167 | {'merge_how': [{'name': 'list', 'settings': ['extend']}, | 206 | {'merge_how': [{'name': 'list', 'settings': ['append']}, |
1102 | 168 | {'name': 'dict', 'settings': []}, | 207 | {'name': 'dict', 'settings': ['no_replace', 'recurse_list']}, |
1103 | 169 | {'name': 'str', 'settings': ['append']}]} | 208 | {'name': 'str', 'settings': ['append']}]} |
1104 | 170 | 209 | ||
1105 | 171 | This would be the equivalent format for default string format but in dictionary | 210 | This would be the equivalent format for default string format but in dictionary |
1106 | @@ -201,4 +240,43 @@ Note, however, that merge algorithms are not used *across* types of | |||
1107 | 201 | configuration. As was the case before merging was implemented, | 240 | configuration. As was the case before merging was implemented, |
1108 | 202 | user-data will overwrite conf.d configuration without merging. | 241 | user-data will overwrite conf.d configuration without merging. |
1109 | 203 | 242 | ||
1110 | 243 | Example cloud-config | ||
1111 | 244 | ==================== | ||
1112 | 245 | |||
1113 | 246 | A common request is to include multiple ``runcmd`` directives in different | ||
1114 | 247 | files and merge all of the commands together. To achieve this, we must modify | ||
1115 | 248 | the default merging to allow for dictionaries to join list values. | ||
1116 | 249 | |||
1117 | 250 | |||
1118 | 251 | The first config | ||
1119 | 252 | |||
1120 | 253 | .. code-block:: yaml | ||
1121 | 254 | |||
1122 | 255 | #cloud-config | ||
1123 | 256 | merge_how: | ||
1124 | 257 | - name: list | ||
1125 | 258 | settings: [append] | ||
1126 | 259 | - name: dict | ||
1127 | 260 | settings: [no_replace, recurse_list] | ||
1128 | 261 | |||
1129 | 262 | runcmd: | ||
1130 | 263 | - bash1 | ||
1131 | 264 | - bash2 | ||
1132 | 265 | |||
1133 | 266 | The second config | ||
1134 | 267 | |||
1135 | 268 | .. code-block:: yaml | ||
1136 | 269 | |||
1137 | 270 | #cloud-config | ||
1138 | 271 | merge_how: | ||
1139 | 272 | - name: list | ||
1140 | 273 | settings: [append] | ||
1141 | 274 | - name: dict | ||
1142 | 275 | settings: [no_replace, recurse_list] | ||
1143 | 276 | |||
1144 | 277 | runcmd: | ||
1145 | 278 | - bash3 | ||
1146 | 279 | - bash4 | ||
1147 | 280 | |||
1148 | 281 | |||
1149 | 204 | .. vi: textwidth=78 | 282 | .. vi: textwidth=78 |
1150 | diff --git a/templates/chef_client.rb.tmpl b/templates/chef_client.rb.tmpl | |||
1151 | index cbb6b15..99978d3 100644 | |||
1152 | --- a/templates/chef_client.rb.tmpl | |||
1153 | +++ b/templates/chef_client.rb.tmpl | |||
1154 | @@ -1,6 +1,6 @@ | |||
1155 | 1 | ## template:jinja | 1 | ## template:jinja |
1156 | 2 | {# | 2 | {# |
1158 | 3 | This file is only utilized if the module 'cc_chef' is enabled in | 3 | This file is only utilized if the module 'cc_chef' is enabled in |
1159 | 4 | cloud-config. Specifically, in order to enable it | 4 | cloud-config. Specifically, in order to enable it |
1160 | 5 | you need to add the following to config: | 5 | you need to add the following to config: |
1161 | 6 | chef: | 6 | chef: |
1162 | @@ -56,3 +56,6 @@ pid_file "{{pid_file}}" | |||
1163 | 56 | {% if show_time %} | 56 | {% if show_time %} |
1164 | 57 | Chef::Log::Formatter.show_time = true | 57 | Chef::Log::Formatter.show_time = true |
1165 | 58 | {% endif %} | 58 | {% endif %} |
1166 | 59 | {% if encrypted_data_bag_secret %} | ||
1167 | 60 | encrypted_data_bag_secret "{{encrypted_data_bag_secret}}" | ||
1168 | 61 | {% endif %} | ||
1169 | diff --git a/tests/cloud_tests/verify.py b/tests/cloud_tests/verify.py | |||
1170 | index 9911ecf..7018f4d 100644 | |||
1171 | --- a/tests/cloud_tests/verify.py | |||
1172 | +++ b/tests/cloud_tests/verify.py | |||
1173 | @@ -61,12 +61,17 @@ def format_test_failures(test_result): | |||
1174 | 61 | if not test_result['failures']: | 61 | if not test_result['failures']: |
1175 | 62 | return '' | 62 | return '' |
1176 | 63 | failure_hdr = ' test failures:' | 63 | failure_hdr = ' test failures:' |
1178 | 64 | failure_fmt = ' * {module}.{class}.{function}\n {error}' | 64 | failure_fmt = ' * {module}.{class}.{function}\n ' |
1179 | 65 | output = [] | 65 | output = [] |
1180 | 66 | for failure in test_result['failures']: | 66 | for failure in test_result['failures']: |
1181 | 67 | if not output: | 67 | if not output: |
1182 | 68 | output = [failure_hdr] | 68 | output = [failure_hdr] |
1184 | 69 | output.append(failure_fmt.format(**failure)) | 69 | msg = failure_fmt.format(**failure) |
1185 | 70 | if failure.get('error'): | ||
1186 | 71 | msg += failure['error'] | ||
1187 | 72 | else: | ||
1188 | 73 | msg += failure.get('traceback', '') | ||
1189 | 74 | output.append(msg) | ||
1190 | 70 | return '\n'.join(output) | 75 | return '\n'.join(output) |
1191 | 71 | 76 | ||
1192 | 72 | 77 | ||
1193 | diff --git a/tests/data/azure/parse_certificates_fingerprints b/tests/data/azure/parse_certificates_fingerprints | |||
1194 | 73 | new file mode 100644 | 78 | new file mode 100644 |
1195 | index 0000000..f7293c5 | |||
1196 | --- /dev/null | |||
1197 | +++ b/tests/data/azure/parse_certificates_fingerprints | |||
1198 | @@ -0,0 +1,4 @@ | |||
1199 | 1 | ECEDEB3B8488D31AF3BC4CCED493F64B7D27D7B1 | ||
1200 | 2 | 073E19D14D1C799224C6A0FD8DDAB6A8BF27D473 | ||
1201 | 3 | 4C16E7FAD6297D74A9B25EB8F0A12808CEBE293E | ||
1202 | 4 | 929130695289B450FE45DCD5F6EF0CDE69865867 | ||
1203 | diff --git a/tests/data/azure/parse_certificates_pem b/tests/data/azure/parse_certificates_pem | |||
1204 | 0 | new file mode 100644 | 5 | new file mode 100644 |
1205 | index 0000000..3521ea3 | |||
1206 | --- /dev/null | |||
1207 | +++ b/tests/data/azure/parse_certificates_pem | |||
1208 | @@ -0,0 +1,152 @@ | |||
1209 | 1 | Bag Attributes | ||
1210 | 2 | localKeyID: 01 00 00 00 | ||
1211 | 3 | Microsoft CSP Name: Microsoft Enhanced Cryptographic Provider v1.0 | ||
1212 | 4 | Key Attributes | ||
1213 | 5 | X509v3 Key Usage: 10 | ||
1214 | 6 | -----BEGIN PRIVATE KEY----- | ||
1215 | 7 | MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDlEe5fUqwdrQTP | ||
1216 | 8 | W2oVlGK2f31q/8ULT8KmOTyUvL0RPdJQ69vvHOc5Q2CKg2eviHC2LWhF8WmpnZj6 | ||
1217 | 9 | 61RL0GeFGizwvU8Moebw5p3oqdcgoGpHVtxf+mr4QcWF58/Fwez0dA4hcsimVNBz | ||
1218 | 10 | eNpBBUIKNBMTBG+4d6hcQBUAGKUdGRcCGEyTqXLU0MgHjxC9JgVqWJl+X2LcAGj5 | ||
1219 | 11 | 7J+tGYGTLzKJmeCeGVNN5ZtJ0T85MYHCKQk1/FElK+Kq5akovXffQHjlnCPcx0NJ | ||
1220 | 12 | 47NBjlPaFp2gjnAChn79bT4iCjOFZ9avWpqRpeU517UCnY7djOr3fuod/MSQyh3L | ||
1221 | 13 | Wuem1tWBAgMBAAECggEBAM4ZXQRs6Kjmo95BHGiAEnSqrlgX+dycjcBq3QPh8KZT | ||
1222 | 14 | nifqnf48XhnackENy7tWIjr3DctoUq4mOp8AHt77ijhqfaa4XSg7fwKeK9NLBGC5 | ||
1223 | 15 | lAXNtAey0o2894/sKrd+LMkgphoYIUnuI4LRaGV56potkj/ZDP/GwTcG/R4SDnTn | ||
1224 | 16 | C1Nb05PNTAPQtPZrgPo7TdM6gGsTnFbVrYHQLyg2Sq/osHfF15YohB01esRLCAwb | ||
1225 | 17 | EF8JkRC4hWIZoV7BsyQ39232zAJQGGla7+wKFs3kObwh3VnFkQpT94KZnNiZuEfG | ||
1226 | 18 | x5pW4Pn3gXgNsftscXsaNe/M9mYZqo//Qw7NvUIvAvECgYEA9AVveyK0HOA06fhh | ||
1227 | 19 | +3hUWdvw7Pbrl+e06jO9+bT1RjQMbHKyI60DZyVGuAySN86iChJRoJr5c6xj+iXU | ||
1228 | 20 | cR6BVJDjGH5t1tyiK2aYf6hEpK9/j8Z54UiVQ486zPP0PGfT2TO4lBLK+8AUmoaH | ||
1229 | 21 | gk21ul8QeVCeCJa/o+xEoRFvzcUCgYEA8FCbbvInrUtNY+9eKaUYoNodsgBVjm5X | ||
1230 | 22 | I0YPUL9D4d+1nvupHSV2NVmQl0w1RaJwrNTafrl5LkqjhQbmuWNta6QgfZzSA3LB | ||
1231 | 23 | lWXo1Mm0azKdcD3qMGbvn0Q3zU+yGNEgmB/Yju3/NtgYRG6tc+FCWRbPbiCnZWT8 | ||
1232 | 24 | v3C2Y0XggI0CgYEA2/jCZBgGkTkzue5kNVJlh5OS/aog+pCvL6hxCtarfBuTT3ed | ||
1233 | 25 | Sje+p46cz3DVpmUpATc+Si8py7KNdYQAm/BJ2be6X+woi9Xcgo87zWgcaPCjZzId | ||
1234 | 26 | 0I2jsIE/Gl6XvpRCDrxnGWRPgt3GNP4szbPLrDPiH9oie8+Y9eYYf7G+PZkCgYEA | ||
1235 | 27 | nRSzZOPYV4f/QDF4pVQLMykfe/iH9B/fyWjEHg3He19VQmRReIHCMMEoqBziPXAe | ||
1236 | 28 | onpHj8oAkeer1wpZyhhZr6CKtFDLXgGm09bXSC/IRMHC81klORovyzU2HHfZfCtG | ||
1237 | 29 | WOmIDnU2+0xpIGIP8sztJ3qnf97MTJSkOSadsWo9gwkCgYEAh5AQmJQmck88Dff2 | ||
1238 | 30 | qIfJIX8d+BDw47BFJ89OmMFjGV8TNB+JO+AV4Vkodg4hxKpLqTFZTTUFgoYfy5u1 | ||
1239 | 31 | 1/BhAjpmCDCrzubCFhx+8VEoM2+2+MmnuQoMAm9+/mD/IidwRaARgXgvEmp7sfdt | ||
1240 | 32 | RyWd+p2lYvFkC/jORQtDMY4uW1o= | ||
1241 | 33 | -----END PRIVATE KEY----- | ||
1242 | 34 | Bag Attributes | ||
1243 | 35 | localKeyID: 02 00 00 00 | ||
1244 | 36 | Microsoft CSP Name: Microsoft Strong Cryptographic Provider | ||
1245 | 37 | Key Attributes | ||
1246 | 38 | X509v3 Key Usage: 10 | ||
1247 | 39 | -----BEGIN PRIVATE KEY----- | ||
1248 | 40 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDlQhPrZwVQYFV4 | ||
1249 | 41 | FBc0H1iTXYaznMpwZvEITKtXWACzTdguUderEVOkXW3HTi5HvC2rMayt0nqo3zcd | ||
1250 | 42 | x1eGiqdjpZQ/wMrkz9wNEM/nNMsXntEwxk0jCVNKB/jz6vf+BOtrSI01SritAGZW | ||
1251 | 43 | dpKoTUyztT8C2mA3X6D8g3m4Dd07ltnzxaDqAQIU5jBHh3f/Q14tlPNZWUIiqVTC | ||
1252 | 44 | gDxgAe7MDmfs9h3CInTBX1XM5J4UsLTL23/padgeSvP5YF5qr1+0c7Tdftxr2lwA | ||
1253 | 45 | N3rLkisf5EiLAToVyJJlgP/exo2I8DaIKe7DZzD3Y1CrurOpkcMKYu5kM1Htlbua | ||
1254 | 46 | tDkAa2oDAgMBAAECggEAOvdueS9DyiMlCKAeQb1IQosdQOh0l0ma+FgEABC2CWhd | ||
1255 | 47 | 0LgjQTBRM6cGO+urcq7/jhdWQ1UuUG4tVn71z7itCi/F/Enhxc2C22d2GhFVpWsn | ||
1256 | 48 | giSXJYpZ/mIjkdVfWNo6FRuRmmHwMys1p0qTOS+8qUJWhSzW75csqJZGgeUrAI61 | ||
1257 | 49 | LBV5F0SGR7dR2xZfy7PeDs9xpD0QivDt5DpsZWPaPvw4QlhdLgw6/YU1h9vtm6ci | ||
1258 | 50 | xLjnPRLZ7JMpcQHO8dUDl6FiEI7yQ11BDm253VQAVMddYRPQABn7SpEF8kD/aZVh | ||
1259 | 51 | 2Clvz61Rz80SKjPUthMPLWMCRp7zB0xDMzt3/1i+tQKBgQD6Ar1/oD3eFnRnpi4u | ||
1260 | 52 | n/hdHJtMuXWNfUA4dspNjP6WGOid9sgIeUUdif1XyVJ+afITzvgpWc7nUWIqG2bQ | ||
1261 | 53 | WxJ/4q2rjUdvjNXTy1voVungR2jD5WLQ9DKeaTR0yCliWlx4JgdPG7qGI5MMwsr+ | ||
1262 | 54 | R/PUoUUhGeEX+o/sCSieO3iUrQKBgQDqwBEMvIdhAv/CK2sG3fsKYX8rFT55ZNX3 | ||
1263 | 55 | Tix9DbUGY3wQColNuI8U1nDlxE9U6VOfT9RPqKelBLCgbzB23kdEJnjSlnqlTxrx | ||
1264 | 56 | E+Hkndyf2ckdJAR3XNxoQ6SRLJNBsgoBj/z5tlfZE9/Jc+uh0mYy3e6g6XCVPBcz | ||
1265 | 57 | MgoIc+ofbwKBgQCGQhZ1hR30N+bHCozeaPW9OvGDIE0qcEqeh9xYDRFilXnF6pK9 | ||
1266 | 58 | SjJ9jG7KR8jPLiHb1VebDSl5O1EV/6UU2vNyTc6pw7LLCryBgkGW4aWy1WZDXNnW | ||
1267 | 59 | EG1meGS9GghvUss5kmJ2bxOZmV0Mi0brisQ8OWagQf+JGvtS7BAt+Q3l+QKBgAb9 | ||
1268 | 60 | 8YQPmXiqPjPqVyW9Ntz4SnFeEJ5NApJ7IZgX8GxgSjGwHqbR+HEGchZl4ncE/Bii | ||
1269 | 61 | qBA3Vcb0fM5KgYcI19aPzsl28fA6ivLjRLcqfIfGVNcpW3iyq13vpdctHLW4N9QU | ||
1270 | 62 | FdTaOYOds+ysJziKq8CYG6NvUIshXw+HTgUybqbBAoGBAIIOqcmmtgOClAwipA17 | ||
1271 | 63 | dAHsI9Sjk+J0+d4JU6o+5TsmhUfUKIjXf5+xqJkJcQZMEe5GhxcCuYkgFicvh4Hz | ||
1272 | 64 | kv2H/EU35LcJTqC6KTKZOWIbGcn1cqsvwm3GQJffYDiO8fRZSwCaif2J3F2lfH4Y | ||
1273 | 65 | R/fA67HXFSTT+OncdRpY1NOn | ||
1274 | 66 | -----END PRIVATE KEY----- | ||
1275 | 67 | Bag Attributes: <Empty Attributes> | ||
1276 | 68 | subject=/CN=CRP/OU=AzureRT/O=Microsoft Corporation/L=Redmond/ST=WA/C=US | ||
1277 | 69 | issuer=/CN=Root Agency | ||
1278 | 70 | -----BEGIN CERTIFICATE----- | ||
1279 | 71 | MIIB+TCCAeOgAwIBAgIBATANBgkqhkiG9w0BAQUFADAWMRQwEgYDVQQDDAtSb290 | ||
1280 | 72 | IEFnZW5jeTAeFw0xOTAyMTUxOTA0MDRaFw0yOTAyMTUxOTE0MDRaMGwxDDAKBgNV | ||
1281 | 73 | BAMMA0NSUDEQMA4GA1UECwwHQXp1cmVSVDEeMBwGA1UECgwVTWljcm9zb2Z0IENv | ||
1282 | 74 | cnBvcmF0aW9uMRAwDgYDVQQHDAdSZWRtb25kMQswCQYDVQQIDAJXQTELMAkGA1UE | ||
1283 | 75 | BhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIlPjJXzrRih4C | ||
1284 | 76 | k/XsoI01oqo7IUxH3dA2F7vHGXQoIpKCp8Qe6Z6cFfdD8Uj+s+B1BX6hngwzIwjN | ||
1285 | 77 | jE/23X3SALVzJVWzX4Y/IEjbgsuao6sOyNyB18wIU9YzZkVGj68fmMlUw3LnhPbe | ||
1286 | 78 | eWkufZaJCaLyhQOwlRMbOcn48D6Ys8fccOyXNzpq3rH1OzeQpxS2M8zaJYP4/VZ/ | ||
1287 | 79 | sf6KRpI7bP+QwyFvNKfhcaO9/gj4kMo9lVGjvDU20FW6g8UVNJCV9N4GO6mOcyqo | ||
1288 | 80 | OhuhVfjCNGgW7N1qi0TIVn0/MQM4l4dcT2R7Z/bV9fhMJLjGsy5A4TLAdRrhKUHT | ||
1289 | 81 | bzi9HyDvAgMBAAEwDQYJKoZIhvcNAQEFBQADAQA= | ||
1290 | 82 | -----END CERTIFICATE----- | ||
1291 | 83 | Bag Attributes | ||
1292 | 84 | localKeyID: 01 00 00 00 | ||
1293 | 85 | subject=/C=US/ST=WASHINGTON/L=Seattle/O=Microsoft/OU=Azure/CN=AnhVo/emailAddress=redacted@microsoft.com | ||
1294 | 86 | issuer=/C=US/ST=WASHINGTON/L=Seattle/O=Microsoft/OU=Azure/CN=AnhVo/emailAddress=redacted@microsoft.com | ||
1295 | 87 | -----BEGIN CERTIFICATE----- | ||
1296 | 88 | MIID7TCCAtWgAwIBAgIJALQS3yMg3R41MA0GCSqGSIb3DQEBCwUAMIGMMQswCQYD | ||
1297 | 89 | VQQGEwJVUzETMBEGA1UECAwKV0FTSElOR1RPTjEQMA4GA1UEBwwHU2VhdHRsZTES | ||
1298 | 90 | MBAGA1UECgwJTWljcm9zb2Z0MQ4wDAYDVQQLDAVBenVyZTEOMAwGA1UEAwwFQW5o | ||
1299 | 91 | Vm8xIjAgBgkqhkiG9w0BCQEWE2FuaHZvQG1pY3Jvc29mdC5jb20wHhcNMTkwMjE0 | ||
1300 | 92 | MjMxMjQwWhcNMjExMTEwMjMxMjQwWjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAgM | ||
1301 | 93 | CldBU0hJTkdUT04xEDAOBgNVBAcMB1NlYXR0bGUxEjAQBgNVBAoMCU1pY3Jvc29m | ||
1302 | 94 | dDEOMAwGA1UECwwFQXp1cmUxDjAMBgNVBAMMBUFuaFZvMSIwIAYJKoZIhvcNAQkB | ||
1303 | 95 | FhNhbmh2b0BtaWNyb3NvZnQuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB | ||
1304 | 96 | CgKCAQEA5RHuX1KsHa0Ez1tqFZRitn99av/FC0/Cpjk8lLy9ET3SUOvb7xznOUNg | ||
1305 | 97 | ioNnr4hwti1oRfFpqZ2Y+utUS9BnhRos8L1PDKHm8Oad6KnXIKBqR1bcX/pq+EHF | ||
1306 | 98 | hefPxcHs9HQOIXLIplTQc3jaQQVCCjQTEwRvuHeoXEAVABilHRkXAhhMk6ly1NDI | ||
1307 | 99 | B48QvSYFaliZfl9i3ABo+eyfrRmBky8yiZngnhlTTeWbSdE/OTGBwikJNfxRJSvi | ||
1308 | 100 | quWpKL1330B45Zwj3MdDSeOzQY5T2hadoI5wAoZ+/W0+IgozhWfWr1qakaXlOde1 | ||
1309 | 101 | Ap2O3Yzq937qHfzEkMody1rnptbVgQIDAQABo1AwTjAdBgNVHQ4EFgQUPvdgLiv3 | ||
1310 | 102 | pAk4r0QTPZU3PFOZJvgwHwYDVR0jBBgwFoAUPvdgLiv3pAk4r0QTPZU3PFOZJvgw | ||
1311 | 103 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVUHZT+h9+uCPLTEl5IDg | ||
1312 | 104 | kqd9WpzXA7PJd/V+7DeDDTkEd06FIKTWZLfxLVVDjQJnQqubQb//e0zGu1qKbXnX | ||
1313 | 105 | R7xqWabGU4eyPeUFWddmt1OHhxKLU3HbJNJJdL6XKiQtpGGUQt/mqNQ/DEr6hhNF | ||
1314 | 106 | im5I79iA8H/dXA2gyZrj5Rxea4mtsaYO0mfp1NrFtJpAh2Djy4B1lBXBIv4DWG9e | ||
1315 | 107 | mMEwzcLCOZj2cOMA6+mdLMUjYCvIRtnn5MKUHyZX5EmX79wsqMTvVpddlVLB9Kgz | ||
1316 | 108 | Qnvft9+SBWh9+F3ip7BsL6Q4Q9v8eHRbnP0ya7ddlgh64uwf9VOfZZdKCnwqudJP | ||
1317 | 109 | 3g== | ||
1318 | 110 | -----END CERTIFICATE----- | ||
1319 | 111 | Bag Attributes | ||
1320 | 112 | localKeyID: 02 00 00 00 | ||
1321 | 113 | subject=/CN=/subscriptions/redacted/resourcegroups/redacted/providers/Microsoft.Compute/virtualMachines/redacted | ||
1322 | 114 | issuer=/CN=Microsoft.ManagedIdentity | ||
1323 | 115 | -----BEGIN CERTIFICATE----- | ||
1324 | 116 | MIIDnTCCAoWgAwIBAgIUB2lauSRccvFkoJybUfIwOUqBN7MwDQYJKoZIhvcNAQEL | ||
1325 | 117 | BQAwJDEiMCAGA1UEAxMZTWljcm9zb2Z0Lk1hbmFnZWRJZGVudGl0eTAeFw0xOTAy | ||
1326 | 118 | MTUxOTA5MDBaFw0xOTA4MTQxOTA5MDBaMIGUMYGRMIGOBgNVBAMTgYYvc3Vic2Ny | ||
1327 | 119 | aXB0aW9ucy8yN2I3NTBjZC1lZDQzLTQyZmQtOTA0NC04ZDc1ZTEyNGFlNTUvcmVz | ||
1328 | 120 | b3VyY2Vncm91cHMvYW5oZXh0cmFzc2gvcHJvdmlkZXJzL01pY3Jvc29mdC5Db21w | ||
1329 | 121 | dXRlL3ZpcnR1YWxNYWNoaW5lcy9hbmh0ZXN0Y2VydDCCASIwDQYJKoZIhvcNAQEB | ||
1330 | 122 | BQADggEPADCCAQoCggEBAOVCE+tnBVBgVXgUFzQfWJNdhrOcynBm8QhMq1dYALNN | ||
1331 | 123 | 2C5R16sRU6RdbcdOLke8LasxrK3SeqjfNx3HV4aKp2OllD/AyuTP3A0Qz+c0yxee | ||
1332 | 124 | 0TDGTSMJU0oH+PPq9/4E62tIjTVKuK0AZlZ2kqhNTLO1PwLaYDdfoPyDebgN3TuW | ||
1333 | 125 | 2fPFoOoBAhTmMEeHd/9DXi2U81lZQiKpVMKAPGAB7swOZ+z2HcIidMFfVczknhSw | ||
1334 | 126 | tMvbf+lp2B5K8/lgXmqvX7RztN1+3GvaXAA3esuSKx/kSIsBOhXIkmWA/97GjYjw | ||
1335 | 127 | Nogp7sNnMPdjUKu6s6mRwwpi7mQzUe2Vu5q0OQBragMCAwEAAaNWMFQwDgYDVR0P | ||
1336 | 128 | AQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYD | ||
1337 | 129 | VR0jBBgwFoAUOJvzEsriQWdJBndPrK+Me1bCPjYwDQYJKoZIhvcNAQELBQADggEB | ||
1338 | 130 | AFGP/g8o7Hv/to11M0UqfzJuW/AyH9RZtSRcNQFLZUndwweQ6fap8lFsA4REUdqe | ||
1339 | 131 | 7Quqp5JNNY1XzKLWXMPoheIDH1A8FFXdsAroArzlNs9tO3TlIHE8A7HxEVZEmR4b | ||
1340 | 132 | 7ZiixmkQPS2RkjEoV/GM6fheBrzuFn7X5kVZyE6cC5sfcebn8xhk3ZcXI0VmpdT0 | ||
1341 | 133 | jFBsf5IvFCIXXLLhJI4KXc8VMoKFU1jT9na/jyaoGmfwovKj4ib8s2aiXGAp7Y38 | ||
1342 | 134 | UCmY+bJapWom6Piy5Jzi/p/kzMVdJcSa+GqpuFxBoQYEVs2XYVl7cGu/wPM+NToC | ||
1343 | 135 | pkSoWwF1QAnHn0eokR9E1rU= | ||
1344 | 136 | -----END CERTIFICATE----- | ||
1345 | 137 | Bag Attributes: <Empty Attributes> | ||
1346 | 138 | subject=/CN=CRP/OU=AzureRT/O=Microsoft Corporation/L=Redmond/ST=WA/C=US | ||
1347 | 139 | issuer=/CN=Root Agency | ||
1348 | 140 | -----BEGIN CERTIFICATE----- | ||
1349 | 141 | MIIB+TCCAeOgAwIBAgIBATANBgkqhkiG9w0BAQUFADAWMRQwEgYDVQQDDAtSb290 | ||
1350 | 142 | IEFnZW5jeTAeFw0xOTAyMTUxOTA0MDRaFw0yOTAyMTUxOTE0MDRaMGwxDDAKBgNV | ||
1351 | 143 | BAMMA0NSUDEQMA4GA1UECwwHQXp1cmVSVDEeMBwGA1UECgwVTWljcm9zb2Z0IENv | ||
1352 | 144 | cnBvcmF0aW9uMRAwDgYDVQQHDAdSZWRtb25kMQswCQYDVQQIDAJXQTELMAkGA1UE | ||
1353 | 145 | BhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHU9IDclbKVYVb | ||
1354 | 146 | Yuv0+zViX+wTwlKspslmy/uf3hkWLh7pyzyrq70S7qtSW2EGixUPxZS/R8pOLHoi | ||
1355 | 147 | nlKF9ILgj0gVTCJsSwnWpXRg3rhZwIVoYMHN50BHS1SqVD0lsWNMXmo76LoJcjmW | ||
1356 | 148 | vwIznvj5C/gnhU+K7+c3m7AlCyU2wjwpBAEYj7PQs6l/wTqpEiaqC5NytNBd7qp+ | ||
1357 | 149 | lYYysVrpa1PFL0Nj4MMZARIfjkiJtL9qDhy9YZeJRQ6q/Fhz0kjvkZnfxixfKF4y | ||
1358 | 150 | WzOfhBrAtpF6oOnuYKk3hxjh9KjTTX4/U8zdLojalX09iyHyEjwJKGlGEpzh1aY7 | ||
1359 | 151 | t5btUyvpAgMBAAEwDQYJKoZIhvcNAQEFBQADAQA= | ||
1360 | 152 | -----END CERTIFICATE----- | ||
1361 | diff --git a/tests/data/azure/pubkey_extract_cert b/tests/data/azure/pubkey_extract_cert | |||
1362 | 0 | new file mode 100644 | 153 | new file mode 100644 |
1363 | index 0000000..ce9b852 | |||
1364 | --- /dev/null | |||
1365 | +++ b/tests/data/azure/pubkey_extract_cert | |||
1366 | @@ -0,0 +1,13 @@ | |||
1367 | 1 | -----BEGIN CERTIFICATE----- | ||
1368 | 2 | MIIB+TCCAeOgAwIBAgIBATANBgkqhkiG9w0BAQUFADAWMRQwEgYDVQQDDAtSb290 | ||
1369 | 3 | IEFnZW5jeTAeFw0xOTAyMTUxOTA0MDRaFw0yOTAyMTUxOTE0MDRaMGwxDDAKBgNV | ||
1370 | 4 | BAMMA0NSUDEQMA4GA1UECwwHQXp1cmVSVDEeMBwGA1UECgwVTWljcm9zb2Z0IENv | ||
1371 | 5 | cnBvcmF0aW9uMRAwDgYDVQQHDAdSZWRtb25kMQswCQYDVQQIDAJXQTELMAkGA1UE | ||
1372 | 6 | BhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHU9IDclbKVYVb | ||
1373 | 7 | Yuv0+zViX+wTwlKspslmy/uf3hkWLh7pyzyrq70S7qtSW2EGixUPxZS/R8pOLHoi | ||
1374 | 8 | nlKF9ILgj0gVTCJsSwnWpXRg3rhZwIVoYMHN50BHS1SqVD0lsWNMXmo76LoJcjmW | ||
1375 | 9 | vwIznvj5C/gnhU+K7+c3m7AlCyU2wjwpBAEYj7PQs6l/wTqpEiaqC5NytNBd7qp+ | ||
1376 | 10 | lYYysVrpa1PFL0Nj4MMZARIfjkiJtL9qDhy9YZeJRQ6q/Fhz0kjvkZnfxixfKF4y | ||
1377 | 11 | WzOfhBrAtpF6oOnuYKk3hxjh9KjTTX4/U8zdLojalX09iyHyEjwJKGlGEpzh1aY7 | ||
1378 | 12 | t5btUyvpAgMBAAEwDQYJKoZIhvcNAQEFBQADAQA= | ||
1379 | 13 | -----END CERTIFICATE----- | ||
1380 | diff --git a/tests/data/azure/pubkey_extract_ssh_key b/tests/data/azure/pubkey_extract_ssh_key | |||
1381 | 0 | new file mode 100644 | 14 | new file mode 100644 |
1382 | index 0000000..54d749e | |||
1383 | --- /dev/null | |||
1384 | +++ b/tests/data/azure/pubkey_extract_ssh_key | |||
1385 | @@ -0,0 +1 @@ | |||
1386 | 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHU9IDclbKVYVbYuv0+zViX+wTwlKspslmy/uf3hkWLh7pyzyrq70S7qtSW2EGixUPxZS/R8pOLHoinlKF9ILgj0gVTCJsSwnWpXRg3rhZwIVoYMHN50BHS1SqVD0lsWNMXmo76LoJcjmWvwIznvj5C/gnhU+K7+c3m7AlCyU2wjwpBAEYj7PQs6l/wTqpEiaqC5NytNBd7qp+lYYysVrpa1PFL0Nj4MMZARIfjkiJtL9qDhy9YZeJRQ6q/Fhz0kjvkZnfxixfKF4yWzOfhBrAtpF6oOnuYKk3hxjh9KjTTX4/U8zdLojalX09iyHyEjwJKGlGEpzh1aY7t5btUyvp | ||
1387 | diff --git a/tests/data/netinfo/freebsd-ifconfig-output b/tests/data/netinfo/freebsd-ifconfig-output | |||
1388 | 0 | new file mode 100644 | 2 | new file mode 100644 |
1389 | index 0000000..3de15a5 | |||
1390 | --- /dev/null | |||
1391 | +++ b/tests/data/netinfo/freebsd-ifconfig-output | |||
1392 | @@ -0,0 +1,17 @@ | |||
1393 | 1 | vtnet0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 | ||
1394 | 2 | options=6c07bb<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,TSO4,TSO6,LRO,VLAN_HWTSO,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6> | ||
1395 | 3 | ether fa:16:3e:14:1f:99 | ||
1396 | 4 | hwaddr fa:16:3e:14:1f:99 | ||
1397 | 5 | inet 10.1.80.61 netmask 0xfffff000 broadcast 10.1.95.255 | ||
1398 | 6 | nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL> | ||
1399 | 7 | media: Ethernet 10Gbase-T <full-duplex> | ||
1400 | 8 | status: active | ||
1401 | 9 | pflog0: flags=0<> metric 0 mtu 33160 | ||
1402 | 10 | pfsync0: flags=0<> metric 0 mtu 1500 | ||
1403 | 11 | syncpeer: 0.0.0.0 maxupd: 128 defer: off | ||
1404 | 12 | lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384 | ||
1405 | 13 | options=600003<RXCSUM,TXCSUM,RXCSUM_IPV6,TXCSUM_IPV6> | ||
1406 | 14 | inet6 ::1 prefixlen 128 | ||
1407 | 15 | inet6 fe80::1%lo0 prefixlen 64 scopeid 0x4 | ||
1408 | 16 | inet 127.0.0.1 netmask 0xff000000 | ||
1409 | 17 | nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL> | ||
1410 | diff --git a/tests/data/netinfo/freebsd-netdev-formatted-output b/tests/data/netinfo/freebsd-netdev-formatted-output | |||
1411 | 0 | new file mode 100644 | 18 | new file mode 100644 |
1412 | index 0000000..a9d2ac1 | |||
1413 | --- /dev/null | |||
1414 | +++ b/tests/data/netinfo/freebsd-netdev-formatted-output | |||
1415 | @@ -0,0 +1,11 @@ | |||
1416 | 1 | +++++++++++++++++++++++++++++++Net device info+++++++++++++++++++++++++++++++ | ||
1417 | 2 | +---------+-------+----------------+------------+-------+-------------------+ | ||
1418 | 3 | | Device | Up | Address | Mask | Scope | Hw-Address | | ||
1419 | 4 | +---------+-------+----------------+------------+-------+-------------------+ | ||
1420 | 5 | | lo0 | True | 127.0.0.1 | 0xff000000 | . | . | | ||
1421 | 6 | | lo0 | True | ::1/128 | . | . | . | | ||
1422 | 7 | | lo0 | True | fe80::1%lo0/64 | . | 0x4 | . | | ||
1423 | 8 | | pflog0 | False | . | . | . | . | | ||
1424 | 9 | | pfsync0 | False | . | . | . | . | | ||
1425 | 10 | | vtnet0 | True | 10.1.80.61 | 0xfffff000 | . | fa:16:3e:14:1f:99 | | ||
1426 | 11 | +---------+-------+----------------+------------+-------+-------------------+ | ||
1427 | diff --git a/tests/unittests/test_datasource/test_azure.py b/tests/unittests/test_datasource/test_azure.py | |||
1428 | index 417d86a..6b05b8f 100644 | |||
1429 | --- a/tests/unittests/test_datasource/test_azure.py | |||
1430 | +++ b/tests/unittests/test_datasource/test_azure.py | |||
1431 | @@ -11,7 +11,7 @@ from cloudinit.util import (b64e, decode_binary, load_file, write_file, | |||
1432 | 11 | from cloudinit.version import version_string as vs | 11 | from cloudinit.version import version_string as vs |
1433 | 12 | from cloudinit.tests.helpers import ( | 12 | from cloudinit.tests.helpers import ( |
1434 | 13 | HttprettyTestCase, CiTestCase, populate_dir, mock, wrap_and_call, | 13 | HttprettyTestCase, CiTestCase, populate_dir, mock, wrap_and_call, |
1436 | 14 | ExitStack, PY26, SkipTest) | 14 | ExitStack) |
1437 | 15 | 15 | ||
1438 | 16 | import crypt | 16 | import crypt |
1439 | 17 | import httpretty | 17 | import httpretty |
1440 | @@ -221,8 +221,6 @@ class TestAzureDataSource(CiTestCase): | |||
1441 | 221 | 221 | ||
1442 | 222 | def setUp(self): | 222 | def setUp(self): |
1443 | 223 | super(TestAzureDataSource, self).setUp() | 223 | super(TestAzureDataSource, self).setUp() |
1444 | 224 | if PY26: | ||
1445 | 225 | raise SkipTest("Does not work on python 2.6") | ||
1446 | 226 | self.tmp = self.tmp_dir() | 224 | self.tmp = self.tmp_dir() |
1447 | 227 | 225 | ||
1448 | 228 | # patch cloud_dir, so our 'seed_dir' is guaranteed empty | 226 | # patch cloud_dir, so our 'seed_dir' is guaranteed empty |
1449 | @@ -1692,6 +1690,7 @@ class TestPreprovisioningPollIMDS(CiTestCase): | |||
1450 | 1692 | self.paths = helpers.Paths({'cloud_dir': self.tmp}) | 1690 | self.paths = helpers.Paths({'cloud_dir': self.tmp}) |
1451 | 1693 | dsaz.BUILTIN_DS_CONFIG['data_dir'] = self.waagent_d | 1691 | dsaz.BUILTIN_DS_CONFIG['data_dir'] = self.waagent_d |
1452 | 1694 | 1692 | ||
1453 | 1693 | @mock.patch('time.sleep', mock.MagicMock()) | ||
1454 | 1695 | @mock.patch(MOCKPATH + 'EphemeralDHCPv4') | 1694 | @mock.patch(MOCKPATH + 'EphemeralDHCPv4') |
1455 | 1696 | def test_poll_imds_re_dhcp_on_timeout(self, m_dhcpv4, report_ready_func, | 1695 | def test_poll_imds_re_dhcp_on_timeout(self, m_dhcpv4, report_ready_func, |
1456 | 1697 | fake_resp, m_media_switch, m_dhcp, | 1696 | fake_resp, m_media_switch, m_dhcp, |
1457 | diff --git a/tests/unittests/test_datasource/test_azure_helper.py b/tests/unittests/test_datasource/test_azure_helper.py | |||
1458 | index 26b2b93..0255616 100644 | |||
1459 | --- a/tests/unittests/test_datasource/test_azure_helper.py | |||
1460 | +++ b/tests/unittests/test_datasource/test_azure_helper.py | |||
1461 | @@ -1,11 +1,13 @@ | |||
1462 | 1 | # This file is part of cloud-init. See LICENSE file for license information. | 1 | # This file is part of cloud-init. See LICENSE file for license information. |
1463 | 2 | 2 | ||
1464 | 3 | import os | 3 | import os |
1465 | 4 | import unittest2 | ||
1466 | 4 | from textwrap import dedent | 5 | from textwrap import dedent |
1467 | 5 | 6 | ||
1468 | 6 | from cloudinit.sources.helpers import azure as azure_helper | 7 | from cloudinit.sources.helpers import azure as azure_helper |
1469 | 7 | from cloudinit.tests.helpers import CiTestCase, ExitStack, mock, populate_dir | 8 | from cloudinit.tests.helpers import CiTestCase, ExitStack, mock, populate_dir |
1470 | 8 | 9 | ||
1471 | 10 | from cloudinit.util import load_file | ||
1472 | 9 | from cloudinit.sources.helpers.azure import WALinuxAgentShim as wa_shim | 11 | from cloudinit.sources.helpers.azure import WALinuxAgentShim as wa_shim |
1473 | 10 | 12 | ||
1474 | 11 | GOAL_STATE_TEMPLATE = """\ | 13 | GOAL_STATE_TEMPLATE = """\ |
1475 | @@ -289,6 +291,50 @@ class TestOpenSSLManager(CiTestCase): | |||
1476 | 289 | self.assertEqual([mock.call(manager.tmpdir)], del_dir.call_args_list) | 291 | self.assertEqual([mock.call(manager.tmpdir)], del_dir.call_args_list) |
1477 | 290 | 292 | ||
1478 | 291 | 293 | ||
1479 | 294 | class TestOpenSSLManagerActions(CiTestCase): | ||
1480 | 295 | |||
1481 | 296 | def setUp(self): | ||
1482 | 297 | super(TestOpenSSLManagerActions, self).setUp() | ||
1483 | 298 | |||
1484 | 299 | self.allowed_subp = True | ||
1485 | 300 | |||
1486 | 301 | def _data_file(self, name): | ||
1487 | 302 | path = 'tests/data/azure' | ||
1488 | 303 | return os.path.join(path, name) | ||
1489 | 304 | |||
1490 | 305 | @unittest2.skip("todo move to cloud_test") | ||
1491 | 306 | def test_pubkey_extract(self): | ||
1492 | 307 | cert = load_file(self._data_file('pubkey_extract_cert')) | ||
1493 | 308 | good_key = load_file(self._data_file('pubkey_extract_ssh_key')) | ||
1494 | 309 | sslmgr = azure_helper.OpenSSLManager() | ||
1495 | 310 | key = sslmgr._get_ssh_key_from_cert(cert) | ||
1496 | 311 | self.assertEqual(good_key, key) | ||
1497 | 312 | |||
1498 | 313 | good_fingerprint = '073E19D14D1C799224C6A0FD8DDAB6A8BF27D473' | ||
1499 | 314 | fingerprint = sslmgr._get_fingerprint_from_cert(cert) | ||
1500 | 315 | self.assertEqual(good_fingerprint, fingerprint) | ||
1501 | 316 | |||
1502 | 317 | @unittest2.skip("todo move to cloud_test") | ||
1503 | 318 | @mock.patch.object(azure_helper.OpenSSLManager, '_decrypt_certs_from_xml') | ||
1504 | 319 | def test_parse_certificates(self, mock_decrypt_certs): | ||
1505 | 320 | """Azure control plane puts private keys as well as certificates | ||
1506 | 321 | into the Certificates XML object. Make sure only the public keys | ||
1507 | 322 | from certs are extracted and that fingerprints are converted to | ||
1508 | 323 | the form specified in the ovf-env.xml file. | ||
1509 | 324 | """ | ||
1510 | 325 | cert_contents = load_file(self._data_file('parse_certificates_pem')) | ||
1511 | 326 | fingerprints = load_file(self._data_file( | ||
1512 | 327 | 'parse_certificates_fingerprints') | ||
1513 | 328 | ).splitlines() | ||
1514 | 329 | mock_decrypt_certs.return_value = cert_contents | ||
1515 | 330 | sslmgr = azure_helper.OpenSSLManager() | ||
1516 | 331 | keys_by_fp = sslmgr.parse_certificates('') | ||
1517 | 332 | for fp in keys_by_fp.keys(): | ||
1518 | 333 | self.assertIn(fp, fingerprints) | ||
1519 | 334 | for fp in fingerprints: | ||
1520 | 335 | self.assertIn(fp, keys_by_fp) | ||
1521 | 336 | |||
1522 | 337 | |||
1523 | 292 | class TestWALinuxAgentShim(CiTestCase): | 338 | class TestWALinuxAgentShim(CiTestCase): |
1524 | 293 | 339 | ||
1525 | 294 | def setUp(self): | 340 | def setUp(self): |
1526 | @@ -329,18 +375,31 @@ class TestWALinuxAgentShim(CiTestCase): | |||
1527 | 329 | 375 | ||
1528 | 330 | def test_certificates_used_to_determine_public_keys(self): | 376 | def test_certificates_used_to_determine_public_keys(self): |
1529 | 331 | shim = wa_shim() | 377 | shim = wa_shim() |
1531 | 332 | data = shim.register_with_azure_and_fetch_data() | 378 | """if register_with_azure_and_fetch_data() isn't passed some info about |
1532 | 379 | the user's public keys, there's no point in even trying to parse | ||
1533 | 380 | the certificates | ||
1534 | 381 | """ | ||
1535 | 382 | mypk = [{'fingerprint': 'fp1', 'path': 'path1'}, | ||
1536 | 383 | {'fingerprint': 'fp3', 'path': 'path3', 'value': ''}] | ||
1537 | 384 | certs = {'fp1': 'expected-key', | ||
1538 | 385 | 'fp2': 'should-not-be-found', | ||
1539 | 386 | 'fp3': 'expected-no-value-key', | ||
1540 | 387 | } | ||
1541 | 388 | sslmgr = self.OpenSSLManager.return_value | ||
1542 | 389 | sslmgr.parse_certificates.return_value = certs | ||
1543 | 390 | data = shim.register_with_azure_and_fetch_data(pubkey_info=mypk) | ||
1544 | 333 | self.assertEqual( | 391 | self.assertEqual( |
1545 | 334 | [mock.call(self.GoalState.return_value.certificates_xml)], | 392 | [mock.call(self.GoalState.return_value.certificates_xml)], |
1550 | 335 | self.OpenSSLManager.return_value.parse_certificates.call_args_list) | 393 | sslmgr.parse_certificates.call_args_list) |
1551 | 336 | self.assertEqual( | 394 | self.assertIn('expected-key', data['public-keys']) |
1552 | 337 | self.OpenSSLManager.return_value.parse_certificates.return_value, | 395 | self.assertIn('expected-no-value-key', data['public-keys']) |
1553 | 338 | data['public-keys']) | 396 | self.assertNotIn('should-not-be-found', data['public-keys']) |
1554 | 339 | 397 | ||
1555 | 340 | def test_absent_certificates_produces_empty_public_keys(self): | 398 | def test_absent_certificates_produces_empty_public_keys(self): |
1556 | 399 | mypk = [{'fingerprint': 'fp1', 'path': 'path1'}] | ||
1557 | 341 | self.GoalState.return_value.certificates_xml = None | 400 | self.GoalState.return_value.certificates_xml = None |
1558 | 342 | shim = wa_shim() | 401 | shim = wa_shim() |
1560 | 343 | data = shim.register_with_azure_and_fetch_data() | 402 | data = shim.register_with_azure_and_fetch_data(pubkey_info=mypk) |
1561 | 344 | self.assertEqual([], data['public-keys']) | 403 | self.assertEqual([], data['public-keys']) |
1562 | 345 | 404 | ||
1563 | 346 | def test_correct_url_used_for_report_ready(self): | 405 | def test_correct_url_used_for_report_ready(self): |
1564 | diff --git a/tests/unittests/test_datasource/test_configdrive.py b/tests/unittests/test_datasource/test_configdrive.py | |||
1565 | index dcdabea..520c50f 100644 | |||
1566 | --- a/tests/unittests/test_datasource/test_configdrive.py | |||
1567 | +++ b/tests/unittests/test_datasource/test_configdrive.py | |||
1568 | @@ -268,8 +268,7 @@ class TestConfigDriveDataSource(CiTestCase): | |||
1569 | 268 | exists_mock = mocks.enter_context( | 268 | exists_mock = mocks.enter_context( |
1570 | 269 | mock.patch.object(os.path, 'exists', | 269 | mock.patch.object(os.path, 'exists', |
1571 | 270 | side_effect=exists_side_effect())) | 270 | side_effect=exists_side_effect())) |
1574 | 271 | device = cfg_ds.device_name_to_device(name) | 271 | self.assertEqual(dev_name, cfg_ds.device_name_to_device(name)) |
1573 | 272 | self.assertEqual(dev_name, device) | ||
1575 | 273 | 272 | ||
1576 | 274 | find_mock.assert_called_once_with(mock.ANY) | 273 | find_mock.assert_called_once_with(mock.ANY) |
1577 | 275 | self.assertEqual(exists_mock.call_count, 2) | 274 | self.assertEqual(exists_mock.call_count, 2) |
1578 | @@ -296,8 +295,7 @@ class TestConfigDriveDataSource(CiTestCase): | |||
1579 | 296 | exists_mock = mocks.enter_context( | 295 | exists_mock = mocks.enter_context( |
1580 | 297 | mock.patch.object(os.path, 'exists', | 296 | mock.patch.object(os.path, 'exists', |
1581 | 298 | return_value=True)) | 297 | return_value=True)) |
1584 | 299 | device = cfg_ds.device_name_to_device(name) | 298 | self.assertEqual(dev_name, cfg_ds.device_name_to_device(name)) |
1583 | 300 | self.assertEqual(dev_name, device) | ||
1585 | 301 | 299 | ||
1586 | 302 | find_mock.assert_called_once_with(mock.ANY) | 300 | find_mock.assert_called_once_with(mock.ANY) |
1587 | 303 | exists_mock.assert_called_once_with(mock.ANY) | 301 | exists_mock.assert_called_once_with(mock.ANY) |
1588 | @@ -331,8 +329,7 @@ class TestConfigDriveDataSource(CiTestCase): | |||
1589 | 331 | yield True | 329 | yield True |
1590 | 332 | with mock.patch.object(os.path, 'exists', | 330 | with mock.patch.object(os.path, 'exists', |
1591 | 333 | side_effect=exists_side_effect()): | 331 | side_effect=exists_side_effect()): |
1594 | 334 | device = cfg_ds.device_name_to_device(name) | 332 | self.assertEqual(dev_name, cfg_ds.device_name_to_device(name)) |
1593 | 335 | self.assertEqual(dev_name, device) | ||
1595 | 336 | # We don't assert the call count for os.path.exists() because | 333 | # We don't assert the call count for os.path.exists() because |
1596 | 337 | # not all of the entries in name_tests results in two calls to | 334 | # not all of the entries in name_tests results in two calls to |
1597 | 338 | # that function. Specifically, 'root2k' doesn't seem to call | 335 | # that function. Specifically, 'root2k' doesn't seem to call |
1598 | @@ -359,8 +356,7 @@ class TestConfigDriveDataSource(CiTestCase): | |||
1599 | 359 | } | 356 | } |
1600 | 360 | for name, dev_name in name_tests.items(): | 357 | for name, dev_name in name_tests.items(): |
1601 | 361 | with mock.patch.object(os.path, 'exists', return_value=True): | 358 | with mock.patch.object(os.path, 'exists', return_value=True): |
1604 | 362 | device = cfg_ds.device_name_to_device(name) | 359 | self.assertEqual(dev_name, cfg_ds.device_name_to_device(name)) |
1603 | 363 | self.assertEqual(dev_name, device) | ||
1605 | 364 | 360 | ||
1606 | 365 | def test_dir_valid(self): | 361 | def test_dir_valid(self): |
1607 | 366 | """Verify a dir is read as such.""" | 362 | """Verify a dir is read as such.""" |
1608 | @@ -604,6 +600,9 @@ class TestNetJson(CiTestCase): | |||
1609 | 604 | 600 | ||
1610 | 605 | 601 | ||
1611 | 606 | class TestConvertNetworkData(CiTestCase): | 602 | class TestConvertNetworkData(CiTestCase): |
1612 | 603 | |||
1613 | 604 | with_logs = True | ||
1614 | 605 | |||
1615 | 607 | def setUp(self): | 606 | def setUp(self): |
1616 | 608 | super(TestConvertNetworkData, self).setUp() | 607 | super(TestConvertNetworkData, self).setUp() |
1617 | 609 | self.tmp = self.tmp_dir() | 608 | self.tmp = self.tmp_dir() |
1618 | @@ -730,6 +729,26 @@ class TestConvertNetworkData(CiTestCase): | |||
1619 | 730 | 'enp0s2': 'fa:16:3e:d4:57:ad'} | 729 | 'enp0s2': 'fa:16:3e:d4:57:ad'} |
1620 | 731 | self.assertEqual(expected, config_name2mac) | 730 | self.assertEqual(expected, config_name2mac) |
1621 | 732 | 731 | ||
1622 | 732 | def test_unknown_device_types_accepted(self): | ||
1623 | 733 | # If we don't recognise a link, we should treat it as physical for a | ||
1624 | 734 | # best-effort boot | ||
1625 | 735 | my_netdata = deepcopy(NETWORK_DATA) | ||
1626 | 736 | my_netdata['links'][0]['type'] = 'my-special-link-type' | ||
1627 | 737 | |||
1628 | 738 | ncfg = openstack.convert_net_json(my_netdata, known_macs=KNOWN_MACS) | ||
1629 | 739 | config_name2mac = {} | ||
1630 | 740 | for n in ncfg['config']: | ||
1631 | 741 | if n['type'] == 'physical': | ||
1632 | 742 | config_name2mac[n['name']] = n['mac_address'] | ||
1633 | 743 | |||
1634 | 744 | expected = {'nic0': 'fa:16:3e:05:30:fe', 'enp0s1': 'fa:16:3e:69:b0:58', | ||
1635 | 745 | 'enp0s2': 'fa:16:3e:d4:57:ad'} | ||
1636 | 746 | self.assertEqual(expected, config_name2mac) | ||
1637 | 747 | |||
1638 | 748 | # We should, however, warn the user that we don't recognise the type | ||
1639 | 749 | self.assertIn('Unknown network_data link type (my-special-link-type)', | ||
1640 | 750 | self.logs.getvalue()) | ||
1641 | 751 | |||
1642 | 733 | 752 | ||
1643 | 734 | def cfg_ds_from_dir(base_d, files=None): | 753 | def cfg_ds_from_dir(base_d, files=None): |
1644 | 735 | run = os.path.join(base_d, "run") | 754 | run = os.path.join(base_d, "run") |
1645 | diff --git a/tests/unittests/test_datasource/test_ec2.py b/tests/unittests/test_datasource/test_ec2.py | |||
1646 | index 1a5956d..20d59bf 100644 | |||
1647 | --- a/tests/unittests/test_datasource/test_ec2.py | |||
1648 | +++ b/tests/unittests/test_datasource/test_ec2.py | |||
1649 | @@ -401,6 +401,30 @@ class TestEc2(test_helpers.HttprettyTestCase): | |||
1650 | 401 | ds.metadata = DEFAULT_METADATA | 401 | ds.metadata = DEFAULT_METADATA |
1651 | 402 | self.assertEqual('my-identity-id', ds.get_instance_id()) | 402 | self.assertEqual('my-identity-id', ds.get_instance_id()) |
1652 | 403 | 403 | ||
1653 | 404 | def test_classic_instance_true(self): | ||
1654 | 405 | """If no vpc-id in metadata, is_classic_instance must return true.""" | ||
1655 | 406 | md_copy = copy.deepcopy(DEFAULT_METADATA) | ||
1656 | 407 | ifaces_md = md_copy.get('network', {}).get('interfaces', {}) | ||
1657 | 408 | for _mac, mac_data in ifaces_md.get('macs', {}).items(): | ||
1658 | 409 | if 'vpc-id' in mac_data: | ||
1659 | 410 | del mac_data['vpc-id'] | ||
1660 | 411 | |||
1661 | 412 | ds = self._setup_ds( | ||
1662 | 413 | platform_data=self.valid_platform_data, | ||
1663 | 414 | sys_cfg={'datasource': {'Ec2': {'strict_id': False}}}, | ||
1664 | 415 | md={'md': md_copy}) | ||
1665 | 416 | self.assertTrue(ds.get_data()) | ||
1666 | 417 | self.assertTrue(ds.is_classic_instance()) | ||
1667 | 418 | |||
1668 | 419 | def test_classic_instance_false(self): | ||
1669 | 420 | """If vpc-id in metadata, is_classic_instance must return false.""" | ||
1670 | 421 | ds = self._setup_ds( | ||
1671 | 422 | platform_data=self.valid_platform_data, | ||
1672 | 423 | sys_cfg={'datasource': {'Ec2': {'strict_id': False}}}, | ||
1673 | 424 | md={'md': DEFAULT_METADATA}) | ||
1674 | 425 | self.assertTrue(ds.get_data()) | ||
1675 | 426 | self.assertFalse(ds.is_classic_instance()) | ||
1676 | 427 | |||
1677 | 404 | @mock.patch('cloudinit.net.dhcp.maybe_perform_dhcp_discovery') | 428 | @mock.patch('cloudinit.net.dhcp.maybe_perform_dhcp_discovery') |
1678 | 405 | def test_valid_platform_with_strict_true(self, m_dhcp): | 429 | def test_valid_platform_with_strict_true(self, m_dhcp): |
1679 | 406 | """Valid platform data should return true with strict_id true.""" | 430 | """Valid platform data should return true with strict_id true.""" |
1680 | diff --git a/tests/unittests/test_distros/test_create_users.py b/tests/unittests/test_distros/test_create_users.py | |||
1681 | index c3f258d..4062495 100644 | |||
1682 | --- a/tests/unittests/test_distros/test_create_users.py | |||
1683 | +++ b/tests/unittests/test_distros/test_create_users.py | |||
1684 | @@ -240,4 +240,32 @@ class TestCreateUser(CiTestCase): | |||
1685 | 240 | [mock.call(set(['auth1']), user), # not disabled | 240 | [mock.call(set(['auth1']), user), # not disabled |
1686 | 241 | mock.call(set(['key1']), 'foouser', options=disable_prefix)]) | 241 | mock.call(set(['key1']), 'foouser', options=disable_prefix)]) |
1687 | 242 | 242 | ||
1688 | 243 | @mock.patch("cloudinit.distros.util.which") | ||
1689 | 244 | def test_lock_with_usermod_if_no_passwd(self, m_which, m_subp, | ||
1690 | 245 | m_is_snappy): | ||
1691 | 246 | """Lock uses usermod --lock if no 'passwd' cmd available.""" | ||
1692 | 247 | m_which.side_effect = lambda m: m in ('usermod',) | ||
1693 | 248 | self.dist.lock_passwd("bob") | ||
1694 | 249 | self.assertEqual( | ||
1695 | 250 | [mock.call(['usermod', '--lock', 'bob'])], | ||
1696 | 251 | m_subp.call_args_list) | ||
1697 | 252 | |||
1698 | 253 | @mock.patch("cloudinit.distros.util.which") | ||
1699 | 254 | def test_lock_with_passwd_if_available(self, m_which, m_subp, | ||
1700 | 255 | m_is_snappy): | ||
1701 | 256 | """Lock with only passwd will use passwd.""" | ||
1702 | 257 | m_which.side_effect = lambda m: m in ('passwd',) | ||
1703 | 258 | self.dist.lock_passwd("bob") | ||
1704 | 259 | self.assertEqual( | ||
1705 | 260 | [mock.call(['passwd', '-l', 'bob'])], | ||
1706 | 261 | m_subp.call_args_list) | ||
1707 | 262 | |||
1708 | 263 | @mock.patch("cloudinit.distros.util.which") | ||
1709 | 264 | def test_lock_raises_runtime_if_no_commands(self, m_which, m_subp, | ||
1710 | 265 | m_is_snappy): | ||
1711 | 266 | """Lock with no commands available raises RuntimeError.""" | ||
1712 | 267 | m_which.return_value = None | ||
1713 | 268 | with self.assertRaises(RuntimeError): | ||
1714 | 269 | self.dist.lock_passwd("bob") | ||
1715 | 270 | |||
1716 | 243 | # vi: ts=4 expandtab | 271 | # vi: ts=4 expandtab |
1717 | diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py | |||
1718 | index e986b59..e453040 100644 | |||
1719 | --- a/tests/unittests/test_distros/test_netconfig.py | |||
1720 | +++ b/tests/unittests/test_distros/test_netconfig.py | |||
1721 | @@ -407,7 +407,7 @@ class TestNetCfgDistroUbuntuNetplan(TestNetCfgDistroBase): | |||
1722 | 407 | self.assertEqual(0o644, get_mode(cfgpath, tmpd)) | 407 | self.assertEqual(0o644, get_mode(cfgpath, tmpd)) |
1723 | 408 | 408 | ||
1724 | 409 | def netplan_path(self): | 409 | def netplan_path(self): |
1726 | 410 | return '/etc/netplan/50-cloud-init.yaml' | 410 | return '/etc/netplan/50-cloud-init.yaml' |
1727 | 411 | 411 | ||
1728 | 412 | def test_apply_network_config_v1_to_netplan_ub(self): | 412 | def test_apply_network_config_v1_to_netplan_ub(self): |
1729 | 413 | expected_cfgs = { | 413 | expected_cfgs = { |
1730 | diff --git a/tests/unittests/test_ds_identify.py b/tests/unittests/test_ds_identify.py | |||
1731 | index 756b4fb..d00c1b4 100644 | |||
1732 | --- a/tests/unittests/test_ds_identify.py | |||
1733 | +++ b/tests/unittests/test_ds_identify.py | |||
1734 | @@ -441,7 +441,7 @@ class TestDsIdentify(DsIdentifyBase): | |||
1735 | 441 | nova does not identify itself on platforms other than intel. | 441 | nova does not identify itself on platforms other than intel. |
1736 | 442 | https://bugs.launchpad.net/cloud-init/+bugs?field.tag=dsid-nova""" | 442 | https://bugs.launchpad.net/cloud-init/+bugs?field.tag=dsid-nova""" |
1737 | 443 | 443 | ||
1739 | 444 | data = VALID_CFG['OpenStack'].copy() | 444 | data = copy.deepcopy(VALID_CFG['OpenStack']) |
1740 | 445 | del data['files'][P_PRODUCT_NAME] | 445 | del data['files'][P_PRODUCT_NAME] |
1741 | 446 | data.update({'policy_dmi': POLICY_FOUND_OR_MAYBE, | 446 | data.update({'policy_dmi': POLICY_FOUND_OR_MAYBE, |
1742 | 447 | 'policy_no_dmi': POLICY_FOUND_OR_MAYBE}) | 447 | 'policy_no_dmi': POLICY_FOUND_OR_MAYBE}) |
1743 | diff --git a/tests/unittests/test_handler/test_handler_chef.py b/tests/unittests/test_handler/test_handler_chef.py | |||
1744 | index b16532e..f431126 100644 | |||
1745 | --- a/tests/unittests/test_handler/test_handler_chef.py | |||
1746 | +++ b/tests/unittests/test_handler/test_handler_chef.py | |||
1747 | @@ -145,6 +145,7 @@ class TestChef(FilesystemMockingTestCase): | |||
1748 | 145 | file_backup_path "/var/backups/chef" | 145 | file_backup_path "/var/backups/chef" |
1749 | 146 | pid_file "/var/run/chef/client.pid" | 146 | pid_file "/var/run/chef/client.pid" |
1750 | 147 | Chef::Log::Formatter.show_time = true | 147 | Chef::Log::Formatter.show_time = true |
1751 | 148 | encrypted_data_bag_secret "/etc/chef/encrypted_data_bag_secret" | ||
1752 | 148 | """ | 149 | """ |
1753 | 149 | tpl_file = util.load_file('templates/chef_client.rb.tmpl') | 150 | tpl_file = util.load_file('templates/chef_client.rb.tmpl') |
1754 | 150 | self.patchUtils(self.tmp) | 151 | self.patchUtils(self.tmp) |
1755 | @@ -157,6 +158,8 @@ class TestChef(FilesystemMockingTestCase): | |||
1756 | 157 | 'validation_name': 'bob', | 158 | 'validation_name': 'bob', |
1757 | 158 | 'validation_key': "/etc/chef/vkey.pem", | 159 | 'validation_key': "/etc/chef/vkey.pem", |
1758 | 159 | 'validation_cert': "this is my cert", | 160 | 'validation_cert': "this is my cert", |
1759 | 161 | 'encrypted_data_bag_secret': | ||
1760 | 162 | '/etc/chef/encrypted_data_bag_secret' | ||
1761 | 160 | }, | 163 | }, |
1762 | 161 | } | 164 | } |
1763 | 162 | cc_chef.handle('chef', cfg, self.fetch_cloud('ubuntu'), LOG, []) | 165 | cc_chef.handle('chef', cfg, self.fetch_cloud('ubuntu'), LOG, []) |
1764 | diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py | |||
1765 | index e041e97..e3b9e02 100644 | |||
1766 | --- a/tests/unittests/test_net.py | |||
1767 | +++ b/tests/unittests/test_net.py | |||
1768 | @@ -19,6 +19,7 @@ import gzip | |||
1769 | 19 | import io | 19 | import io |
1770 | 20 | import json | 20 | import json |
1771 | 21 | import os | 21 | import os |
1772 | 22 | import re | ||
1773 | 22 | import textwrap | 23 | import textwrap |
1774 | 23 | import yaml | 24 | import yaml |
1775 | 24 | 25 | ||
1776 | @@ -103,6 +104,326 @@ STATIC_EXPECTED_1 = { | |||
1777 | 103 | 'address': '10.0.0.2'}], | 104 | 'address': '10.0.0.2'}], |
1778 | 104 | } | 105 | } |
1779 | 105 | 106 | ||
1780 | 107 | V1_NAMESERVER_ALIAS = """ | ||
1781 | 108 | config: | ||
1782 | 109 | - id: eno1 | ||
1783 | 110 | mac_address: 08:94:ef:51:ae:e0 | ||
1784 | 111 | mtu: 1500 | ||
1785 | 112 | name: eno1 | ||
1786 | 113 | subnets: | ||
1787 | 114 | - type: manual | ||
1788 | 115 | type: physical | ||
1789 | 116 | - id: eno2 | ||
1790 | 117 | mac_address: 08:94:ef:51:ae:e1 | ||
1791 | 118 | mtu: 1500 | ||
1792 | 119 | name: eno2 | ||
1793 | 120 | subnets: | ||
1794 | 121 | - type: manual | ||
1795 | 122 | type: physical | ||
1796 | 123 | - id: eno3 | ||
1797 | 124 | mac_address: 08:94:ef:51:ae:de | ||
1798 | 125 | mtu: 1500 | ||
1799 | 126 | name: eno3 | ||
1800 | 127 | subnets: | ||
1801 | 128 | - type: manual | ||
1802 | 129 | type: physical | ||
1803 | 130 | - bond_interfaces: | ||
1804 | 131 | - eno1 | ||
1805 | 132 | - eno3 | ||
1806 | 133 | id: bondM | ||
1807 | 134 | mac_address: 08:94:ef:51:ae:e0 | ||
1808 | 135 | mtu: 1500 | ||
1809 | 136 | name: bondM | ||
1810 | 137 | params: | ||
1811 | 138 | bond-downdelay: 0 | ||
1812 | 139 | bond-lacp-rate: fast | ||
1813 | 140 | bond-miimon: 100 | ||
1814 | 141 | bond-mode: 802.3ad | ||
1815 | 142 | bond-updelay: 0 | ||
1816 | 143 | bond-xmit-hash-policy: layer3+4 | ||
1817 | 144 | subnets: | ||
1818 | 145 | - address: 10.101.10.47/23 | ||
1819 | 146 | gateway: 10.101.11.254 | ||
1820 | 147 | type: static | ||
1821 | 148 | type: bond | ||
1822 | 149 | - id: eno4 | ||
1823 | 150 | mac_address: 08:94:ef:51:ae:df | ||
1824 | 151 | mtu: 1500 | ||
1825 | 152 | name: eno4 | ||
1826 | 153 | subnets: | ||
1827 | 154 | - type: manual | ||
1828 | 155 | type: physical | ||
1829 | 156 | - id: enp0s20f0u1u6 | ||
1830 | 157 | mac_address: 0a:94:ef:51:a4:b9 | ||
1831 | 158 | mtu: 1500 | ||
1832 | 159 | name: enp0s20f0u1u6 | ||
1833 | 160 | subnets: | ||
1834 | 161 | - type: manual | ||
1835 | 162 | type: physical | ||
1836 | 163 | - id: enp216s0f0 | ||
1837 | 164 | mac_address: 68:05:ca:81:7c:e8 | ||
1838 | 165 | mtu: 9000 | ||
1839 | 166 | name: enp216s0f0 | ||
1840 | 167 | subnets: | ||
1841 | 168 | - type: manual | ||
1842 | 169 | type: physical | ||
1843 | 170 | - id: enp216s0f1 | ||
1844 | 171 | mac_address: 68:05:ca:81:7c:e9 | ||
1845 | 172 | mtu: 9000 | ||
1846 | 173 | name: enp216s0f1 | ||
1847 | 174 | subnets: | ||
1848 | 175 | - type: manual | ||
1849 | 176 | type: physical | ||
1850 | 177 | - id: enp47s0f0 | ||
1851 | 178 | mac_address: 68:05:ca:64:d3:6c | ||
1852 | 179 | mtu: 9000 | ||
1853 | 180 | name: enp47s0f0 | ||
1854 | 181 | subnets: | ||
1855 | 182 | - type: manual | ||
1856 | 183 | type: physical | ||
1857 | 184 | - bond_interfaces: | ||
1858 | 185 | - enp216s0f0 | ||
1859 | 186 | - enp47s0f0 | ||
1860 | 187 | id: bond0 | ||
1861 | 188 | mac_address: 68:05:ca:64:d3:6c | ||
1862 | 189 | mtu: 9000 | ||
1863 | 190 | name: bond0 | ||
1864 | 191 | params: | ||
1865 | 192 | bond-downdelay: 0 | ||
1866 | 193 | bond-lacp-rate: fast | ||
1867 | 194 | bond-miimon: 100 | ||
1868 | 195 | bond-mode: 802.3ad | ||
1869 | 196 | bond-updelay: 0 | ||
1870 | 197 | bond-xmit-hash-policy: layer3+4 | ||
1871 | 198 | subnets: | ||
1872 | 199 | - type: manual | ||
1873 | 200 | type: bond | ||
1874 | 201 | - id: bond0.3502 | ||
1875 | 202 | mtu: 9000 | ||
1876 | 203 | name: bond0.3502 | ||
1877 | 204 | subnets: | ||
1878 | 205 | - address: 172.20.80.4/25 | ||
1879 | 206 | type: static | ||
1880 | 207 | type: vlan | ||
1881 | 208 | vlan_id: 3502 | ||
1882 | 209 | vlan_link: bond0 | ||
1883 | 210 | - id: bond0.3503 | ||
1884 | 211 | mtu: 9000 | ||
1885 | 212 | name: bond0.3503 | ||
1886 | 213 | subnets: | ||
1887 | 214 | - address: 172.20.80.129/25 | ||
1888 | 215 | type: static | ||
1889 | 216 | type: vlan | ||
1890 | 217 | vlan_id: 3503 | ||
1891 | 218 | vlan_link: bond0 | ||
1892 | 219 | - id: enp47s0f1 | ||
1893 | 220 | mac_address: 68:05:ca:64:d3:6d | ||
1894 | 221 | mtu: 9000 | ||
1895 | 222 | name: enp47s0f1 | ||
1896 | 223 | subnets: | ||
1897 | 224 | - type: manual | ||
1898 | 225 | type: physical | ||
1899 | 226 | - bond_interfaces: | ||
1900 | 227 | - enp216s0f1 | ||
1901 | 228 | - enp47s0f1 | ||
1902 | 229 | id: bond1 | ||
1903 | 230 | mac_address: 68:05:ca:64:d3:6d | ||
1904 | 231 | mtu: 9000 | ||
1905 | 232 | name: bond1 | ||
1906 | 233 | params: | ||
1907 | 234 | bond-downdelay: 0 | ||
1908 | 235 | bond-lacp-rate: fast | ||
1909 | 236 | bond-miimon: 100 | ||
1910 | 237 | bond-mode: 802.3ad | ||
1911 | 238 | bond-updelay: 0 | ||
1912 | 239 | bond-xmit-hash-policy: layer3+4 | ||
1913 | 240 | subnets: | ||
1914 | 241 | - address: 10.101.8.65/26 | ||
1915 | 242 | routes: | ||
1916 | 243 | - destination: 213.119.192.0/24 | ||
1917 | 244 | gateway: 10.101.8.126 | ||
1918 | 245 | metric: 0 | ||
1919 | 246 | type: static | ||
1920 | 247 | type: bond | ||
1921 | 248 | - address: | ||
1922 | 249 | - 10.101.10.1 | ||
1923 | 250 | - 10.101.10.2 | ||
1924 | 251 | - 10.101.10.3 | ||
1925 | 252 | - 10.101.10.5 | ||
1926 | 253 | search: | ||
1927 | 254 | - foo.bar | ||
1928 | 255 | - maas | ||
1929 | 256 | type: nameserver | ||
1930 | 257 | version: 1 | ||
1931 | 258 | """ | ||
1932 | 259 | |||
1933 | 260 | NETPLAN_NO_ALIAS = """ | ||
1934 | 261 | network: | ||
1935 | 262 | version: 2 | ||
1936 | 263 | ethernets: | ||
1937 | 264 | eno1: | ||
1938 | 265 | match: | ||
1939 | 266 | macaddress: 08:94:ef:51:ae:e0 | ||
1940 | 267 | mtu: 1500 | ||
1941 | 268 | set-name: eno1 | ||
1942 | 269 | eno2: | ||
1943 | 270 | match: | ||
1944 | 271 | macaddress: 08:94:ef:51:ae:e1 | ||
1945 | 272 | mtu: 1500 | ||
1946 | 273 | set-name: eno2 | ||
1947 | 274 | eno3: | ||
1948 | 275 | match: | ||
1949 | 276 | macaddress: 08:94:ef:51:ae:de | ||
1950 | 277 | mtu: 1500 | ||
1951 | 278 | set-name: eno3 | ||
1952 | 279 | eno4: | ||
1953 | 280 | match: | ||
1954 | 281 | macaddress: 08:94:ef:51:ae:df | ||
1955 | 282 | mtu: 1500 | ||
1956 | 283 | set-name: eno4 | ||
1957 | 284 | enp0s20f0u1u6: | ||
1958 | 285 | match: | ||
1959 | 286 | macaddress: 0a:94:ef:51:a4:b9 | ||
1960 | 287 | mtu: 1500 | ||
1961 | 288 | set-name: enp0s20f0u1u6 | ||
1962 | 289 | enp216s0f0: | ||
1963 | 290 | match: | ||
1964 | 291 | macaddress: 68:05:ca:81:7c:e8 | ||
1965 | 292 | mtu: 9000 | ||
1966 | 293 | set-name: enp216s0f0 | ||
1967 | 294 | enp216s0f1: | ||
1968 | 295 | match: | ||
1969 | 296 | macaddress: 68:05:ca:81:7c:e9 | ||
1970 | 297 | mtu: 9000 | ||
1971 | 298 | set-name: enp216s0f1 | ||
1972 | 299 | enp47s0f0: | ||
1973 | 300 | match: | ||
1974 | 301 | macaddress: 68:05:ca:64:d3:6c | ||
1975 | 302 | mtu: 9000 | ||
1976 | 303 | set-name: enp47s0f0 | ||
1977 | 304 | enp47s0f1: | ||
1978 | 305 | match: | ||
1979 | 306 | macaddress: 68:05:ca:64:d3:6d | ||
1980 | 307 | mtu: 9000 | ||
1981 | 308 | set-name: enp47s0f1 | ||
1982 | 309 | bonds: | ||
1983 | 310 | bond0: | ||
1984 | 311 | interfaces: | ||
1985 | 312 | - enp216s0f0 | ||
1986 | 313 | - enp47s0f0 | ||
1987 | 314 | macaddress: 68:05:ca:64:d3:6c | ||
1988 | 315 | mtu: 9000 | ||
1989 | 316 | parameters: | ||
1990 | 317 | down-delay: 0 | ||
1991 | 318 | lacp-rate: fast | ||
1992 | 319 | mii-monitor-interval: 100 | ||
1993 | 320 | mode: 802.3ad | ||
1994 | 321 | transmit-hash-policy: layer3+4 | ||
1995 | 322 | up-delay: 0 | ||
1996 | 323 | bond1: | ||
1997 | 324 | addresses: | ||
1998 | 325 | - 10.101.8.65/26 | ||
1999 | 326 | interfaces: | ||
2000 | 327 | - enp216s0f1 | ||
2001 | 328 | - enp47s0f1 | ||
2002 | 329 | macaddress: 68:05:ca:64:d3:6d | ||
2003 | 330 | mtu: 9000 | ||
2004 | 331 | nameservers: | ||
2005 | 332 | addresses: | ||
2006 | 333 | - 10.101.10.1 | ||
2007 | 334 | - 10.101.10.2 | ||
2008 | 335 | - 10.101.10.3 | ||
2009 | 336 | - 10.101.10.5 | ||
2010 | 337 | search: | ||
2011 | 338 | - foo.bar | ||
2012 | 339 | - maas | ||
2013 | 340 | parameters: | ||
2014 | 341 | down-delay: 0 | ||
2015 | 342 | lacp-rate: fast | ||
2016 | 343 | mii-monitor-interval: 100 | ||
2017 | 344 | mode: 802.3ad | ||
2018 | 345 | transmit-hash-policy: layer3+4 | ||
2019 | 346 | up-delay: 0 | ||
2020 | 347 | routes: | ||
2021 | 348 | - metric: 0 | ||
2022 | 349 | to: 213.119.192.0/24 | ||
2023 | 350 | via: 10.101.8.126 | ||
2024 | 351 | bondM: | ||
2025 | 352 | addresses: | ||
2026 | 353 | - 10.101.10.47/23 | ||
2027 | 354 | gateway4: 10.101.11.254 | ||
2028 | 355 | interfaces: | ||
2029 | 356 | - eno1 | ||
2030 | 357 | - eno3 | ||
2031 | 358 | macaddress: 08:94:ef:51:ae:e0 | ||
2032 | 359 | mtu: 1500 | ||
2033 | 360 | nameservers: | ||
2034 | 361 | addresses: | ||
2035 | 362 | - 10.101.10.1 | ||
2036 | 363 | - 10.101.10.2 | ||
2037 | 364 | - 10.101.10.3 | ||
2038 | 365 | - 10.101.10.5 | ||
2039 | 366 | search: | ||
2040 | 367 | - foo.bar | ||
2041 | 368 | - maas | ||
2042 | 369 | parameters: | ||
2043 | 370 | down-delay: 0 | ||
2044 | 371 | lacp-rate: fast | ||
2045 | 372 | mii-monitor-interval: 100 | ||
2046 | 373 | mode: 802.3ad | ||
2047 | 374 | transmit-hash-policy: layer3+4 | ||
2048 | 375 | up-delay: 0 | ||
2049 | 376 | vlans: | ||
2050 | 377 | bond0.3502: | ||
2051 | 378 | addresses: | ||
2052 | 379 | - 172.20.80.4/25 | ||
2053 | 380 | id: 3502 | ||
2054 | 381 | link: bond0 | ||
2055 | 382 | mtu: 9000 | ||
2056 | 383 | nameservers: | ||
2057 | 384 | addresses: | ||
2058 | 385 | - 10.101.10.1 | ||
2059 | 386 | - 10.101.10.2 | ||
2060 | 387 | - 10.101.10.3 | ||
2061 | 388 | - 10.101.10.5 | ||
2062 | 389 | search: | ||
2063 | 390 | - foo.bar | ||
2064 | 391 | - maas | ||
2065 | 392 | bond0.3503: | ||
2066 | 393 | addresses: | ||
2067 | 394 | - 172.20.80.129/25 | ||
2068 | 395 | id: 3503 | ||
2069 | 396 | link: bond0 | ||
2070 | 397 | mtu: 9000 | ||
2071 | 398 | nameservers: | ||
2072 | 399 | addresses: | ||
2073 | 400 | - 10.101.10.1 | ||
2074 | 401 | - 10.101.10.2 | ||
2075 | 402 | - 10.101.10.3 | ||
2076 | 403 | - 10.101.10.5 | ||
2077 | 404 | search: | ||
2078 | 405 | - foo.bar | ||
2079 | 406 | - maas | ||
2080 | 407 | """ | ||
2081 | 408 | |||
2082 | 409 | NETPLAN_DHCP_FALSE = """ | ||
2083 | 410 | version: 2 | ||
2084 | 411 | ethernets: | ||
2085 | 412 | ens3: | ||
2086 | 413 | match: | ||
2087 | 414 | macaddress: 52:54:00:ab:cd:ef | ||
2088 | 415 | dhcp4: false | ||
2089 | 416 | dhcp6: false | ||
2090 | 417 | addresses: | ||
2091 | 418 | - 192.168.42.100/24 | ||
2092 | 419 | - 2001:db8::100/32 | ||
2093 | 420 | gateway4: 192.168.42.1 | ||
2094 | 421 | gateway6: 2001:db8::1 | ||
2095 | 422 | nameservers: | ||
2096 | 423 | search: [example.com] | ||
2097 | 424 | addresses: [192.168.42.53, 1.1.1.1] | ||
2098 | 425 | """ | ||
2099 | 426 | |||
2100 | 106 | # Examples (and expected outputs for various renderers). | 427 | # Examples (and expected outputs for various renderers). |
2101 | 107 | OS_SAMPLES = [ | 428 | OS_SAMPLES = [ |
2102 | 108 | { | 429 | { |
2103 | @@ -2286,6 +2607,50 @@ USERCTL=no | |||
2104 | 2286 | config = sysconfig.ConfigObj(nm_cfg) | 2607 | config = sysconfig.ConfigObj(nm_cfg) |
2105 | 2287 | self.assertIn('ifcfg-rh', config['main']['plugins']) | 2608 | self.assertIn('ifcfg-rh', config['main']['plugins']) |
2106 | 2288 | 2609 | ||
2107 | 2610 | def test_netplan_dhcp_false_disable_dhcp_in_state(self): | ||
2108 | 2611 | """netplan config with dhcp[46]: False should not add dhcp in state""" | ||
2109 | 2612 | net_config = yaml.load(NETPLAN_DHCP_FALSE) | ||
2110 | 2613 | ns = network_state.parse_net_config_data(net_config, | ||
2111 | 2614 | skip_broken=False) | ||
2112 | 2615 | |||
2113 | 2616 | dhcp_found = [snet for iface in ns.iter_interfaces() | ||
2114 | 2617 | for snet in iface['subnets'] if 'dhcp' in snet['type']] | ||
2115 | 2618 | |||
2116 | 2619 | self.assertEqual([], dhcp_found) | ||
2117 | 2620 | |||
2118 | 2621 | def test_netplan_dhcp_false_no_dhcp_in_sysconfig(self): | ||
2119 | 2622 | """netplan cfg with dhcp[46]: False should not have bootproto=dhcp""" | ||
2120 | 2623 | |||
2121 | 2624 | entry = { | ||
2122 | 2625 | 'yaml': NETPLAN_DHCP_FALSE, | ||
2123 | 2626 | 'expected_sysconfig': { | ||
2124 | 2627 | 'ifcfg-ens3': textwrap.dedent("""\ | ||
2125 | 2628 | BOOTPROTO=none | ||
2126 | 2629 | DEFROUTE=yes | ||
2127 | 2630 | DEVICE=ens3 | ||
2128 | 2631 | DNS1=192.168.42.53 | ||
2129 | 2632 | DNS2=1.1.1.1 | ||
2130 | 2633 | DOMAIN=example.com | ||
2131 | 2634 | GATEWAY=192.168.42.1 | ||
2132 | 2635 | HWADDR=52:54:00:ab:cd:ef | ||
2133 | 2636 | IPADDR=192.168.42.100 | ||
2134 | 2637 | IPV6ADDR=2001:db8::100/32 | ||
2135 | 2638 | IPV6INIT=yes | ||
2136 | 2639 | IPV6_DEFAULTGW=2001:db8::1 | ||
2137 | 2640 | NETMASK=255.255.255.0 | ||
2138 | 2641 | NM_CONTROLLED=no | ||
2139 | 2642 | ONBOOT=yes | ||
2140 | 2643 | STARTMODE=auto | ||
2141 | 2644 | TYPE=Ethernet | ||
2142 | 2645 | USERCTL=no | ||
2143 | 2646 | """), | ||
2144 | 2647 | } | ||
2145 | 2648 | } | ||
2146 | 2649 | |||
2147 | 2650 | found = self._render_and_read(network_config=yaml.load(entry['yaml'])) | ||
2148 | 2651 | self._compare_files_to_expected(entry['expected_sysconfig'], found) | ||
2149 | 2652 | self._assert_headers(found) | ||
2150 | 2653 | |||
2151 | 2289 | 2654 | ||
2152 | 2290 | class TestOpenSuseSysConfigRendering(CiTestCase): | 2655 | class TestOpenSuseSysConfigRendering(CiTestCase): |
2153 | 2291 | 2656 | ||
2154 | @@ -3065,6 +3430,38 @@ class TestNetplanRoundTrip(CiTestCase): | |||
2155 | 3065 | entry['expected_netplan'].splitlines(), | 3430 | entry['expected_netplan'].splitlines(), |
2156 | 3066 | files['/etc/netplan/50-cloud-init.yaml'].splitlines()) | 3431 | files['/etc/netplan/50-cloud-init.yaml'].splitlines()) |
2157 | 3067 | 3432 | ||
2158 | 3433 | def test_render_output_has_yaml_no_aliases(self): | ||
2159 | 3434 | entry = { | ||
2160 | 3435 | 'yaml': V1_NAMESERVER_ALIAS, | ||
2161 | 3436 | 'expected_netplan': NETPLAN_NO_ALIAS, | ||
2162 | 3437 | } | ||
2163 | 3438 | network_config = yaml.load(entry['yaml']) | ||
2164 | 3439 | ns = network_state.parse_net_config_data(network_config) | ||
2165 | 3440 | files = self._render_and_read(state=ns) | ||
2166 | 3441 | # check for alias | ||
2167 | 3442 | content = files['/etc/netplan/50-cloud-init.yaml'] | ||
2168 | 3443 | |||
2169 | 3444 | # test load the yaml to ensure we don't render something not loadable | ||
2170 | 3445 | # this allows single aliases, but not duplicate ones | ||
2171 | 3446 | parsed = yaml.load(files['/etc/netplan/50-cloud-init.yaml']) | ||
2172 | 3447 | self.assertNotEqual(None, parsed) | ||
2173 | 3448 | |||
2174 | 3449 | # now look for any alias, avoid rendering them entirely | ||
2175 | 3450 | # generate the first anchor string using the template | ||
2176 | 3451 | # as of this writing, looks like "&id001" | ||
2177 | 3452 | anchor = r'&' + yaml.serializer.Serializer.ANCHOR_TEMPLATE % 1 | ||
2178 | 3453 | found_alias = re.search(anchor, content, re.MULTILINE) | ||
2179 | 3454 | if found_alias: | ||
2180 | 3455 | msg = "Error at: %s\nContent:\n%s" % (found_alias, content) | ||
2181 | 3456 | raise ValueError('Found yaml alias in rendered netplan: ' + msg) | ||
2182 | 3457 | |||
2183 | 3458 | print(entry['expected_netplan']) | ||
2184 | 3459 | print('-- expected ^ | v rendered --') | ||
2185 | 3460 | print(files['/etc/netplan/50-cloud-init.yaml']) | ||
2186 | 3461 | self.assertEqual( | ||
2187 | 3462 | entry['expected_netplan'].splitlines(), | ||
2188 | 3463 | files['/etc/netplan/50-cloud-init.yaml'].splitlines()) | ||
2189 | 3464 | |||
2190 | 3068 | 3465 | ||
2191 | 3069 | class TestEniRoundTrip(CiTestCase): | 3466 | class TestEniRoundTrip(CiTestCase): |
2192 | 3070 | 3467 | ||
2193 | diff --git a/tools/cloud-init-per b/tools/cloud-init-per | |||
2194 | index 7d6754b..eae3e93 100755 | |||
2195 | --- a/tools/cloud-init-per | |||
2196 | +++ b/tools/cloud-init-per | |||
2197 | @@ -38,7 +38,7 @@ fi | |||
2198 | 38 | [ "$1" = "-h" -o "$1" = "--help" ] && { Usage ; exit 0; } | 38 | [ "$1" = "-h" -o "$1" = "--help" ] && { Usage ; exit 0; } |
2199 | 39 | [ $# -ge 3 ] || { Usage 1>&2; exit 1; } | 39 | [ $# -ge 3 ] || { Usage 1>&2; exit 1; } |
2200 | 40 | freq=$1 | 40 | freq=$1 |
2202 | 41 | name=$2 | 41 | name=${2/-/_} |
2203 | 42 | shift 2; | 42 | shift 2; |
2204 | 43 | 43 | ||
2205 | 44 | [ "${name#*/}" = "${name}" ] || fail "name cannot contain a /" | 44 | [ "${name#*/}" = "${name}" ] || fail "name cannot contain a /" |
2206 | @@ -53,6 +53,12 @@ esac | |||
2207 | 53 | [ -d "${sem%/*}" ] || mkdir -p "${sem%/*}" || | 53 | [ -d "${sem%/*}" ] || mkdir -p "${sem%/*}" || |
2208 | 54 | fail "failed to make directory for ${sem}" | 54 | fail "failed to make directory for ${sem}" |
2209 | 55 | 55 | ||
2210 | 56 | # Rename legacy sem files with dashes in their names. Do not overwrite existing | ||
2211 | 57 | # sem files to prevent clobbering those which may have been created from calls | ||
2212 | 58 | # outside of cloud-init. | ||
2213 | 59 | sem_legacy="${sem/_/-}" | ||
2214 | 60 | [ "$sem" != "$sem_legacy" -a -e "$sem_legacy" ] && mv -n "$sem_legacy" "$sem" | ||
2215 | 61 | |||
2216 | 56 | [ "$freq" != "always" -a -e "$sem" ] && exit 0 | 62 | [ "$freq" != "always" -a -e "$sem" ] && exit 0 |
2217 | 57 | "$@" | 63 | "$@" |
2218 | 58 | ret=$? | 64 | ret=$? |
FAILED: Continuous integration, rev:8a0521081e0 45944233b49eca1 b9fc09cffb033e /code.launchpad .net/~daniel- thewatkins/ cloud-init/ +git/cloud- init/+merge/ 364099/ +edit-commit- message
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
https:/ /jenkins. ubuntu. com/server/ job/cloud- init-ci/ 618/
Executed test runs:
SUCCESS: Checkout
SUCCESS: Unit & Style Tests
SUCCESS: Ubuntu LTS: Build
SUCCESS: Ubuntu LTS: Integration
IN_PROGRESS: Declarative: Post Actions
Click here to trigger a rebuild: /jenkins. ubuntu. com/server/ job/cloud- init-ci/ 618/rebuild
https:/