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

Proposed by Chad Smith
Status: Merged
Merged at revision: e4ea57a37ee1834199ba06235a75b12f2cd0f99f
Proposed branch: ~chad.smith/cloud-init:ubuntu/devel
Merge into: cloud-init:ubuntu/devel
Diff against target: 666 lines (+278/-63)
19 files modified
HACKING.rst (+8/-0)
cloudinit/cmd/status.py (+5/-2)
cloudinit/cmd/tests/test_status.py (+18/-3)
cloudinit/config/cc_power_state_change.py (+1/-0)
cloudinit/config/cc_rh_subscription.py (+2/-3)
cloudinit/distros/freebsd.py (+3/-8)
cloudinit/util.py (+4/-0)
debian/changelog (+11/-0)
doc/rtd/topics/boot.rst (+10/-3)
doc/rtd/topics/capabilities.rst (+153/-7)
doc/rtd/topics/debugging.rst (+1/-0)
doc/rtd/topics/modules.rst (+2/-0)
tests/cloud_tests/collect.py (+7/-0)
tests/cloud_tests/platforms/lxd/instance.py (+19/-24)
tests/cloud_tests/testcases.yaml (+21/-6)
tests/cloud_tests/testcases/base.py (+4/-2)
tests/cloud_tests/verify.py (+1/-1)
tests/unittests/test_ds_identify.py (+5/-3)
tools/ds-identify (+3/-1)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Scott Moser Pending
Review via email: mp+337088@code.launchpad.net

Description of the change

Sync latest cloud-init master to Bionic for release

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

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

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

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

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

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

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/HACKING.rst b/HACKING.rst
2index 93e3f42..3bb555c 100644
3--- a/HACKING.rst
4+++ b/HACKING.rst
5@@ -16,6 +16,14 @@ Do these things once
6 When prompted for 'Project contact' or 'Canonical Project Manager' enter
7 'Scott Moser'.
8
9+* Configure git with your email and name for commit messages.
10+
11+ Your name will appear in commit messages and will also be used in
12+ changelogs or release notes. Give yourself credit!::
13+
14+ git config user.name "Your Name"
15+ git config user.email "Your Email"
16+
17 * Clone the upstream `repository`_ on Launchpad::
18
19 git clone https://git.launchpad.net/cloud-init
20diff --git a/cloudinit/cmd/status.py b/cloudinit/cmd/status.py
21index 3e5d0d0..d7aaee9 100644
22--- a/cloudinit/cmd/status.py
23+++ b/cloudinit/cmd/status.py
24@@ -93,6 +93,8 @@ def _is_cloudinit_disabled(disable_file, paths):
25 elif not os.path.exists(os.path.join(paths.run_dir, 'enabled')):
26 is_disabled = True
27 reason = 'Cloud-init disabled by cloud-init-generator'
28+ else:
29+ reason = 'Cloud-init enabled by systemd cloud-init-generator'
30 return (is_disabled, reason)
31
32
33@@ -127,10 +129,11 @@ def _get_status_details(paths):
34 status_detail = value
35 elif isinstance(value, dict):
36 errors.extend(value.get('errors', []))
37+ start = value.get('start') or 0
38 finished = value.get('finished') or 0
39- if finished == 0:
40+ if finished == 0 and start != 0:
41 status = STATUS_RUNNING
42- event_time = max(value.get('start', 0), finished)
43+ event_time = max(start, finished)
44 if event_time > latest_event:
45 latest_event = event_time
46 if errors:
47diff --git a/cloudinit/cmd/tests/test_status.py b/cloudinit/cmd/tests/test_status.py
48index 6d4a11e..a7c0a91 100644
49--- a/cloudinit/cmd/tests/test_status.py
50+++ b/cloudinit/cmd/tests/test_status.py
51@@ -93,6 +93,19 @@ class TestStatus(CiTestCase):
52 self.assertTrue(is_disabled, 'expected disabled cloud-init')
53 self.assertEqual('Cloud-init disabled by cloud-init-generator', reason)
54
55+ def test__is_cloudinit_disabled_false_when_enabled_in_systemd(self):
56+ '''Report enabled when systemd generator creates the enabled file.'''
57+ enabled_file = os.path.join(self.paths.run_dir, 'enabled')
58+ write_file(enabled_file, '')
59+ (is_disabled, reason) = wrap_and_call(
60+ 'cloudinit.cmd.status',
61+ {'uses_systemd': True,
62+ 'get_cmdline': 'something ignored'},
63+ status._is_cloudinit_disabled, self.disable_file, self.paths)
64+ self.assertFalse(is_disabled, 'expected enabled cloud-init')
65+ self.assertEqual(
66+ 'Cloud-init enabled by systemd cloud-init-generator', reason)
67+
68 def test_status_returns_not_run(self):
69 '''When status.json does not exist yet, return 'not run'.'''
70 self.assertFalse(
71@@ -137,8 +150,9 @@ class TestStatus(CiTestCase):
72 self.assertEqual(expected, m_stdout.getvalue())
73
74 def test_status_returns_running(self):
75- '''Report running when status file exists but isn't finished.'''
76- write_json(self.status_file, {'v1': {'init': {'finished': None}}})
77+ '''Report running when status exists with an unfinished stage.'''
78+ write_json(self.status_file,
79+ {'v1': {'init': {'start': 1, 'finished': None}}})
80 cmdargs = myargs(long=False, wait=False)
81 with mock.patch('sys.stdout', new_callable=StringIO) as m_stdout:
82 retcode = wrap_and_call(
83@@ -338,7 +352,8 @@ class TestStatus(CiTestCase):
84
85 def test_status_main(self):
86 '''status.main can be run as a standalone script.'''
87- write_json(self.status_file, {'v1': {'init': {'finished': None}}})
88+ write_json(self.status_file,
89+ {'v1': {'init': {'start': 1, 'finished': None}}})
90 with self.assertRaises(SystemExit) as context_manager:
91 with mock.patch('sys.stdout', new_callable=StringIO) as m_stdout:
92 wrap_and_call(
93diff --git a/cloudinit/config/cc_power_state_change.py b/cloudinit/config/cc_power_state_change.py
94index eba58b0..4da3a58 100644
95--- a/cloudinit/config/cc_power_state_change.py
96+++ b/cloudinit/config/cc_power_state_change.py
97@@ -194,6 +194,7 @@ def doexit(sysexit):
98
99
100 def execmd(exe_args, output=None, data_in=None):
101+ ret = 1
102 try:
103 proc = subprocess.Popen(exe_args, stdin=subprocess.PIPE,
104 stdout=output, stderr=subprocess.STDOUT)
105diff --git a/cloudinit/config/cc_rh_subscription.py b/cloudinit/config/cc_rh_subscription.py
106index a9d21e7..530808c 100644
107--- a/cloudinit/config/cc_rh_subscription.py
108+++ b/cloudinit/config/cc_rh_subscription.py
109@@ -276,9 +276,8 @@ class SubscriptionManager(object):
110 cmd = ['attach', '--auto']
111 try:
112 return_out, return_err = self._sub_man_cli(cmd)
113- except util.ProcessExecutionError:
114- self.log_warn("Auto-attach failed with: "
115- "{0}]".format(return_err.strip()))
116+ except util.ProcessExecutionError as e:
117+ self.log_warn("Auto-attach failed with: {0}".format(e))
118 return False
119 for line in return_out.split("\n"):
120 if line is not "":
121diff --git a/cloudinit/distros/freebsd.py b/cloudinit/distros/freebsd.py
122index bad112f..aa468bc 100644
123--- a/cloudinit/distros/freebsd.py
124+++ b/cloudinit/distros/freebsd.py
125@@ -116,6 +116,7 @@ class Distro(distros.Distro):
126 (out, err) = util.subp(['ifconfig', '-a'])
127 ifconfigoutput = [x for x in (out.strip()).splitlines()
128 if len(x.split()) > 0]
129+ bsddev = 'NOT_FOUND'
130 for line in ifconfigoutput:
131 m = re.match('^\w+', line)
132 if m:
133@@ -347,15 +348,9 @@ class Distro(distros.Distro):
134 bymac[Distro.get_interface_mac(n)] = {
135 'name': n, 'up': self.is_up(n), 'downable': None}
136
137+ nics_with_addresses = set()
138 if check_downable:
139- nics_with_addresses = set()
140- ipv6 = self.get_ipv6()
141- ipv4 = self.get_ipv4()
142- for bytes_out in (ipv6, ipv4):
143- for i in ipv6:
144- nics_with_addresses.update(i)
145- for i in ipv4:
146- nics_with_addresses.update(i)
147+ nics_with_addresses = set(self.get_ipv4() + self.get_ipv6())
148
149 for d in bymac.values():
150 d['downable'] = (d['up'] is False or
151diff --git a/cloudinit/util.py b/cloudinit/util.py
152index 9976400..338fb97 100644
153--- a/cloudinit/util.py
154+++ b/cloudinit/util.py
155@@ -1587,6 +1587,10 @@ def mount_cb(device, callback, data=None, rw=False, mtype=None, sync=True):
156 mtypes = list(mtype)
157 elif mtype is None:
158 mtypes = None
159+ else:
160+ raise TypeError(
161+ 'Unsupported type provided for mtype parameter: {_type}'.format(
162+ _type=type(mtype)))
163
164 # clean up 'mtype' input a bit based on platform.
165 platsys = platform.system().lower()
166diff --git a/debian/changelog b/debian/changelog
167index 8879717..314d8d5 100644
168--- a/debian/changelog
169+++ b/debian/changelog
170@@ -1,3 +1,14 @@
171+cloud-init (17.2-30-gf7deaf15-0ubuntu1) bionic; urgency=medium
172+
173+ * New upstream snapshot.
174+ - docs: Update RTD content for cloud-init subcommands.
175+ - OVF: Extend well-known labels to include OVFENV. (LP: #1698669)
176+ - Fix potential cases of uninitialized variables. (LP: #1744796)
177+ - tests: Collect script output as binary, collect systemd journal, fix lxd.
178+ - HACKING.rst: mention setting user name and email via git config.
179+
180+ -- Chad Smith <chad.smith@canonical.com> Fri, 02 Feb 2018 09:51:14 -0700
181+
182 cloud-init (17.2-25-gc03bdd3d-0ubuntu1) bionic; urgency=medium
183
184 * New upstream snapshot.
185diff --git a/doc/rtd/topics/boot.rst b/doc/rtd/topics/boot.rst
186index 859409a..f2976fd 100644
187--- a/doc/rtd/topics/boot.rst
188+++ b/doc/rtd/topics/boot.rst
189@@ -1,3 +1,5 @@
190+.. _boot_stages:
191+
192 ***********
193 Boot Stages
194 ***********
195@@ -74,7 +76,7 @@ Network
196 * **systemd service**: ``cloud-init.service``
197 * **runs**: After local stage and configured networking is up.
198 * **blocks**: As much of remaining boot as possible.
199- * **modules**: ``init_modules``
200+ * **modules**: ``cloud_init_modules`` in **/etc/cloud/cloud.cfg**
201
202 This stage requires all configured networking to be online, as it will fully
203 process any user-data that is found. Here, processing means:
204@@ -104,7 +106,7 @@ Config
205 * **systemd service**: ``cloud-config.service``
206 * **runs**: After network stage.
207 * **blocks**: None.
208- * **modules**: ``config_modules``
209+ * **modules**: ``cloud_config_modules`` in **/etc/cloud/cloud.cfg**
210
211 This stage runs config modules only. Modules that do not really have an
212 effect on other stages of boot are run here.
213@@ -115,7 +117,7 @@ Final
214 * **systemd service**: ``cloud-final.service``
215 * **runs**: As final part of boot (traditional "rc.local")
216 * **blocks**: None.
217- * **modules**: ``final_modules``
218+ * **modules**: ``cloud_final_modules`` in **/etc/cloud/cloud.cfg**
219
220 This stage runs as late in boot as possible. Any scripts that a user is
221 accustomed to running after logging into a system should run correctly here.
222@@ -125,4 +127,9 @@ Things that run here include
223 * configuration management plugins (puppet, chef, salt-minion)
224 * user-scripts (including ``runcmd``).
225
226+For scripts external to cloud-init looking to wait until cloud-init
227+finished, the ``cloud-init status`` subcommand can help block external
228+scripts until cloud-init is done without having to write your own systemd
229+units dependency chains. See :ref:`cli_status` for more info.
230+
231 .. vi: textwidth=78
232diff --git a/doc/rtd/topics/capabilities.rst b/doc/rtd/topics/capabilities.rst
233index 31eaba5..ae3a0c7 100644
234--- a/doc/rtd/topics/capabilities.rst
235+++ b/doc/rtd/topics/capabilities.rst
236@@ -1,3 +1,5 @@
237+.. _capabilities:
238+
239 ************
240 Capabilities
241 ************
242@@ -39,17 +41,19 @@ Currently defined feature names include:
243 see :ref:`network_config_v2` documentation for examples.
244
245
246-CLI Interface :
247+CLI Interface
248+=============
249
250-``cloud-init features`` will print out each feature supported. If cloud-init
251-does not have the features subcommand, it also does not support any features
252-described in this document.
253+ The command line documentation is accessible on any cloud-init
254+installed system:
255
256 .. code-block:: bash
257
258 % cloud-init --help
259- usage: cloud-init [-h] [--version] [--file FILES] [--debug] [--force]
260- {init,modules,query,single,dhclient-hook,features} ...
261+ usage: cloud-init [-h] [--version] [--file FILES]
262+ [--debug] [--force]
263+ {init,modules,single,dhclient-hook,features,analyze,devel,collect-logs,clean,status}
264+ ...
265
266 optional arguments:
267 -h, --help show this help message and exit
268@@ -61,7 +65,7 @@ described in this document.
269 your own risk)
270
271 Subcommands:
272- {init,modules,single,dhclient-hook,features,analyze,devel}
273+ {init,modules,single,dhclient-hook,features,analyze,devel,collect-logs,clean,status}
274 init initializes cloud-init and performs initial modules
275 modules activates modules using a given configuration key
276 single run a single module
277@@ -69,11 +73,153 @@ described in this document.
278 features list defined features
279 analyze Devel tool: Analyze cloud-init logs and data
280 devel Run development tools
281+ collect-logs Collect and tar all cloud-init debug info
282+ clean Remove logs and artifacts so cloud-init can re-run.
283+ status Report cloud-init status or wait on completion.
284+
285+CLI Subcommand details
286+======================
287+
288+.. _cli_features:
289+
290+cloud-init features
291+-------------------
292+Print out each feature supported. If cloud-init does not have the
293+features subcommand, it also does not support any features described in
294+this document.
295+
296+.. code-block:: bash
297
298 % cloud-init features
299 NETWORK_CONFIG_V1
300 NETWORK_CONFIG_V2
301
302+.. _cli_status:
303+
304+cloud-init status
305+-----------------
306+Report whether cloud-init is running, done, disabled or errored. Exits
307+non-zero if an error is detected in cloud-init.
308+ * **--long**: Detailed status information.
309+ * **--wait**: Block until cloud-init completes.
310+
311+.. code-block:: bash
312+
313+ % cloud-init status --long
314+ status: done
315+ time: Wed, 17 Jan 2018 20:41:59 +0000
316+ detail:
317+ DataSourceNoCloud [seed=/var/lib/cloud/seed/nocloud-net][dsmode=net]
318+
319+ # Cloud-init running still short versus long options
320+ % cloud-init status
321+ status: running
322+ % cloud-init status --long
323+ status: running
324+ time: Fri, 26 Jan 2018 21:39:43 +0000
325+ detail:
326+ Running in stage: init-local
327+
328+.. _cli_collect_logs:
329+
330+cloud-init collect-logs
331+-----------------------
332+Collect and tar cloud-init generated logs, data files and system
333+information for triage. This subcommand is integrated with apport.
334+
335+**Note**: Ubuntu users can file bugs with `ubuntu-bug cloud-init` to
336+automaticaly attach these logs to a bug report.
337+
338+Logs collected are:
339+
340+ * /var/log/cloud-init*log
341+ * /run/cloud-init
342+ * cloud-init package version
343+ * dmesg output
344+ * journalctl output
345+ * /var/lib/cloud/instance/user-data.txt
346+
347+.. _cli_analyze:
348+
349+cloud-init analyze
350+------------------
351+Get detailed reports of where cloud-init spends most of its time. See
352+:ref:`boot_time_analysis` for more info.
353+
354+ * **blame** Report ordered by most costly operations.
355+ * **dump** Machine-readable JSON dump of all cloud-init tracked events.
356+ * **show** show time-ordered report of the cost of operations during each
357+ boot stage.
358+
359+.. _cli_devel:
360+
361+cloud-init devel
362+----------------
363+Collection of development tools under active development. These tools will
364+likely be promoted to top-level subcommands when stable.
365+
366+ * ``cloud-init devel schema``: A **#cloud-config** format and schema
367+ validator. It accepts a cloud-config yaml file and annotates potential
368+ schema errors locally without the need for deployment. Schema
369+ validation is work in progress and supports a subset of cloud-config
370+ modules.
371+
372+.. _cli_clean:
373+
374+cloud-init clean
375+----------------
376+Remove cloud-init artifacts from /var/lib/cloud and optionally reboot the
377+machine to so cloud-init re-runs all stages as it did on first boot.
378+
379+ * **--logs**: Optionally remove /var/log/cloud-init*log files.
380+ * **--reboot**: Reboot the system after removing artifacts.
381+
382+.. _cli_init:
383+
384+cloud-init init
385+---------------
386+Generally run by OS init systems to execute cloud-init's stages
387+*init* and *init-local*. See :ref:`boot_stages` for more info.
388+Can be run on the commandline, but is generally gated to run only once
389+due to semaphores in **/var/lib/cloud/instance/sem/** and
390+**/var/lib/cloud/sem**.
391+
392+ * **--local**: Run *init-local* stage instead of *init*.
393+
394+.. _cli_modules:
395+
396+cloud-init modules
397+------------------
398+Generally run by OS init systems to execute *modules:config* and
399+*modules:final* boot stages. This executes cloud config :ref:`modules`
400+configured to run in the init, config and final stages. The modules are
401+declared to run in various boot stages in the file
402+**/etc/cloud/cloud.cfg** under keys **cloud_init_modules**,
403+**cloud_init_modules** and **cloud_init_modules**. Can be run on the
404+commandline, but each module is gated to run only once due to semaphores
405+in ``/var/lib/cloud/``.
406+
407+ * **--mode (init|config|final)**: Run *modules:init*, *modules:config* or
408+ *modules:final* cloud-init stages. See :ref:`boot_stages` for more info.
409+
410+.. _cli_single:
411+
412+cloud-init single
413+-----------------
414+Attempt to run a single named cloud config module. The following example
415+re-runs the cc_set_hostname module ignoring the module default frequency
416+of once-per-instance:
417+
418+ * **--name**: The cloud-config module name to run
419+ * **--frequency**: Optionally override the declared module frequency
420+ with one of (always|once-per-instance|once)
421+
422+.. code-block:: bash
423+
424+ % cloud-init single --name set_hostname --frequency always
425+
426+**Note**: Mileage may vary trying to re-run each cloud-config module, as
427+some are not idempotent.
428
429 .. _Cloud-init: https://launchpad.net/cloud-init
430 .. vi: textwidth=78
431diff --git a/doc/rtd/topics/debugging.rst b/doc/rtd/topics/debugging.rst
432index 4e43dd5..c2b47ed 100644
433--- a/doc/rtd/topics/debugging.rst
434+++ b/doc/rtd/topics/debugging.rst
435@@ -7,6 +7,7 @@ Overview
436 This topic will discuss general approaches for test and debug of cloud-init on
437 deployed instances.
438
439+.. _boot_time_analysis:
440
441 Boot Time Analysis - cloud-init analyze
442 ======================================
443diff --git a/doc/rtd/topics/modules.rst b/doc/rtd/topics/modules.rst
444index cdb0f41..7b14675 100644
445--- a/doc/rtd/topics/modules.rst
446+++ b/doc/rtd/topics/modules.rst
447@@ -1,3 +1,5 @@
448+.. _modules:
449+
450 *******
451 Modules
452 *******
453diff --git a/tests/cloud_tests/collect.py b/tests/cloud_tests/collect.py
454index 33acbb1..5ea88e5 100644
455--- a/tests/cloud_tests/collect.py
456+++ b/tests/cloud_tests/collect.py
457@@ -24,6 +24,13 @@ def collect_script(instance, base_dir, script, script_name):
458 (out, err, exit) = instance.run_script(
459 script.encode(), rcs=False,
460 description='collect: {}'.format(script_name))
461+ if err:
462+ LOG.debug("collect script %s had stderr: %s", script_name, err)
463+ if not isinstance(out, bytes):
464+ raise util.PlatformError(
465+ "Collection of '%s' returned type %s, expected bytes: %s" %
466+ (script_name, type(out), out))
467+
468 c_util.write_file(os.path.join(base_dir, script_name), out)
469
470
471diff --git a/tests/cloud_tests/platforms/lxd/instance.py b/tests/cloud_tests/platforms/lxd/instance.py
472index 0d697c0..d2d2a1f 100644
473--- a/tests/cloud_tests/platforms/lxd/instance.py
474+++ b/tests/cloud_tests/platforms/lxd/instance.py
475@@ -6,6 +6,8 @@ import os
476 import shutil
477 from tempfile import mkdtemp
478
479+from cloudinit.util import subp, ProcessExecutionError
480+
481 from ..instances import Instance
482
483
484@@ -29,6 +31,7 @@ class LXDInstance(Instance):
485 platform, name, properties, config, features)
486 self.tmpd = mkdtemp(prefix="%s-%s" % (type(self).__name__, name))
487 self._setup_console_log()
488+ self.name = name
489
490 @property
491 def pylxd_container(self):
492@@ -55,33 +58,25 @@ class LXDInstance(Instance):
493 if env is None:
494 env = {}
495
496- if stdin is not None:
497- # pylxd does not support input to execute.
498- # https://github.com/lxc/pylxd/issues/244
499- #
500- # The solution here is write a tmp file in the container
501- # and then execute a shell that sets it standard in to
502- # be from that file, removes it, and calls the comand.
503- tmpf = self.tmpfile()
504- self.write_data(tmpf, stdin)
505- ncmd = 'exec <"{tmpf}"; rm -f "{tmpf}"; exec "$@"'
506- command = (['sh', '-c', ncmd.format(tmpf=tmpf), 'stdinhack'] +
507- list(command))
508+ env_args = []
509+ if env:
510+ env_args = ['env'] + ["%s=%s" for k, v in env.items()]
511
512 # ensure instance is running and execute the command
513 self.start()
514- # execute returns a ContainerExecuteResult, named tuple
515- # (exit_code, stdout, stderr)
516- res = self.pylxd_container.execute(command, environment=env)
517-
518- # get out, exit and err from pylxd return
519- if not hasattr(res, 'exit_code'):
520- # pylxd 2.1.3 and earlier only return out and err, no exit
521- raise RuntimeError(
522- "No 'exit_code' in pylxd.container.execute return.\n"
523- "pylxd > 2.2 is required.")
524-
525- return res.stdout, res.stderr, res.exit_code
526+
527+ # Use cmdline client due to https://github.com/lxc/pylxd/issues/268
528+ exit_code = 0
529+ try:
530+ stdout, stderr = subp(
531+ ['lxc', 'exec', self.name, '--'] + env_args + list(command),
532+ data=stdin, decode=False)
533+ except ProcessExecutionError as e:
534+ exit_code = e.exit_code
535+ stdout = e.stdout
536+ stderr = e.stderr
537+
538+ return stdout, stderr, exit_code
539
540 def read_data(self, remote_path, decode=False):
541 """Read data from instance filesystem.
542diff --git a/tests/cloud_tests/testcases.yaml b/tests/cloud_tests/testcases.yaml
543index 7183e01..8e0fb62 100644
544--- a/tests/cloud_tests/testcases.yaml
545+++ b/tests/cloud_tests/testcases.yaml
546@@ -7,22 +7,37 @@ base_test_data:
547 #cloud-config
548 collect_scripts:
549 cloud-init.log: |
550- #!/bin/bash
551+ #!/bin/sh
552 cat /var/log/cloud-init.log
553 cloud-init-output.log: |
554- #!/bin/bash
555+ #!/bin/sh
556 cat /var/log/cloud-init-output.log
557 instance-id: |
558- #!/bin/bash
559+ #!/bin/sh
560 cat /run/cloud-init/.instance-id
561 result.json: |
562- #!/bin/bash
563+ #!/bin/sh
564 cat /run/cloud-init/result.json
565 status.json: |
566- #!/bin/bash
567+ #!/bin/sh
568 cat /run/cloud-init/status.json
569 cloud-init-version: |
570- #!/bin/bash
571+ #!/bin/sh
572 dpkg-query -W -f='${Version}' cloud-init
573+ system.journal.gz: |
574+ #!/bin/sh
575+ [ -d /run/systemd ] || { echo "not systemd."; exit 0; }
576+ fail() { echo "ERROR:" "$@" 1>&2; exit 1; }
577+ journal=""
578+ for d in /run/log/journal /var/log/journal; do
579+ for f in $d/*/system.journal; do
580+ [ -f "$f" ] || continue
581+ [ -z "$journal" ] ||
582+ fail "multiple journal found: $f $journal."
583+ journal="$f"
584+ done
585+ done
586+ [ -f "$journal" ] || fail "no journal file found."
587+ gzip --to-stdout "$journal"
588
589 # vi: ts=4 expandtab
590diff --git a/tests/cloud_tests/testcases/base.py b/tests/cloud_tests/testcases/base.py
591index 1c5b540..20e9595 100644
592--- a/tests/cloud_tests/testcases/base.py
593+++ b/tests/cloud_tests/testcases/base.py
594@@ -30,12 +30,14 @@ class CloudTestCase(unittest.TestCase):
595 raise AssertionError('Key "{}" not in cloud config'.format(name))
596 return self.cloud_config[name]
597
598- def get_data_file(self, name):
599+ def get_data_file(self, name, decode=True):
600 """Get data file failing test if it is not present."""
601 if name not in self.data:
602 raise AssertionError('File "{}" missing from collect data'
603 .format(name))
604- return self.data[name]
605+ if not decode:
606+ return self.data[name]
607+ return self.data[name].decode('utf-8')
608
609 def get_instance_id(self):
610 """Get recorded instance id."""
611diff --git a/tests/cloud_tests/verify.py b/tests/cloud_tests/verify.py
612index fc1efcf..2a9fd52 100644
613--- a/tests/cloud_tests/verify.py
614+++ b/tests/cloud_tests/verify.py
615@@ -29,7 +29,7 @@ def verify_data(base_dir, tests):
616 data = {}
617 test_dir = os.path.join(base_dir, test_name)
618 for script_name in os.listdir(test_dir):
619- with open(os.path.join(test_dir, script_name), 'r') as fp:
620+ with open(os.path.join(test_dir, script_name), 'rb') as fp:
621 data[script_name] = fp.read()
622
623 # get test suite and launch tests
624diff --git a/tests/unittests/test_ds_identify.py b/tests/unittests/test_ds_identify.py
625index ad6c5cf..31cc622 100644
626--- a/tests/unittests/test_ds_identify.py
627+++ b/tests/unittests/test_ds_identify.py
628@@ -338,7 +338,7 @@ class TestDsIdentify(CiTestCase):
629 self._test_ds_found('OVF-vmware-customization')
630
631 def test_ovf_on_vmware_iso_found_by_cdrom_with_matching_fs_label(self):
632- """OVF is identified when iso9660 cdrom label has ovf-transport."""
633+ """OVF is identified by well-known iso9660 labels."""
634 ovf_cdrom_by_label = copy.deepcopy(VALID_CFG['OVF'])
635 # Unset matching cdrom ovf schema content
636 ovf_cdrom_by_label['files']['dev/sr0'] = 'No content match'
637@@ -346,10 +346,12 @@ class TestDsIdentify(CiTestCase):
638 ovf_cdrom_by_label, rc=RC_NOT_FOUND, policy_dmi="disabled")
639
640 # Add recognized labels
641- for valid_fs_label in ['ovf-transport', 'OVF-TRANSPORT']:
642+ valid_ovf_labels = ['ovf-transport', 'OVF-TRANSPORT',
643+ "OVFENV", "ovfenv"]
644+ for valid_ovf_label in valid_ovf_labels:
645 ovf_cdrom_by_label['mocks'][0]['out'] = blkid_out([
646 {'DEVNAME': 'sr0', 'TYPE': 'iso9660',
647- 'LABEL': valid_fs_label}])
648+ 'LABEL': valid_ovf_label}])
649 self._check_via_dict(
650 ovf_cdrom_by_label, rc=RC_FOUND, dslist=['OVF', DS_NONE])
651
652diff --git a/tools/ds-identify b/tools/ds-identify
653index 374c3ad..cd26824 100755
654--- a/tools/ds-identify
655+++ b/tools/ds-identify
656@@ -664,7 +664,9 @@ is_cdrom_ovf() {
657 esac
658
659 # fast path known 'OVF' labels
660- [ "$label" = "OVF-TRANSPORT" -o "$label" = "ovf-transport" ] && return 0
661+ case "$label" in
662+ OVF-TRANSPORT|ovf-transport|OVFENV|ovfenv) return 0;;
663+ esac
664
665 # explicitly skip known labels of other types. rd_rdfe is azure.
666 case "$label" in

Subscribers

People subscribed via source and target branches