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

Proposed by Chad Smith
Status: Merged
Merged at revision: e5596beef0618204dedd94e1cd593a8cfb6bb6c7
Proposed branch: ~chad.smith/cloud-init:ubuntu/artful
Merge into: cloud-init:ubuntu/artful
Diff against target: 656 lines (+630/-0)
4 files modified
debian/changelog (+9/-0)
debian/patches/cpick-11172924-IBMCloud-Disable-config-drive-and-nocloud-only-if (+230/-0)
debian/patches/cpick-6ef92c98-IBMCloud-recognize-provisioning-environment-during (+389/-0)
debian/patches/series (+2/-0)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Scott Moser Pending
Review via email: mp+344901@code.launchpad.net

Commit message

Cherry pick fixes for IBMCloud upgrades from 17.1 -> 18.2 (artful-proposed)

Addresses the following bugs:
LP: #1766401
LP: #1767166

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:5a8b71fdc0b9313bfac2c8eedde1ced0004d5d2a
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1085/
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/1085/rebuild

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

PASSED: Continuous integration, rev:e5596beef0618204dedd94e1cd593a8cfb6bb6c7
https://jenkins.ubuntu.com/server/job/cloud-init-ci/1087/
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/1087/rebuild

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/debian/changelog b/debian/changelog
index 982d0c8..c438253 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
1cloud-init (18.2-4-g05926e48-0ubuntu1~17.10.2) artful-proposed; urgency=medium
2
3 * cherry-pick 11172924: IBMCloud: Disable config-drive and nocloud
4 only if IBMCloud (LP: #1766401)
5 * cherry-pick 6ef92c98: IBMCloud: recognize provisioning environment
6 during debug (LP: #1767166)
7
8 -- Chad Smith <chad.smith@canonical.com> Tue, 01 May 2018 10:36:14 -0600
9
1cloud-init (18.2-4-g05926e48-0ubuntu1~17.10.1) artful-proposed; urgency=medium10cloud-init (18.2-4-g05926e48-0ubuntu1~17.10.1) artful-proposed; urgency=medium
211
3 * debian/new-upstream-snapshot: Remove script, now maintained elsewhere.12 * debian/new-upstream-snapshot: Remove script, now maintained elsewhere.
diff --git a/debian/patches/cpick-11172924-IBMCloud-Disable-config-drive-and-nocloud-only-if b/debian/patches/cpick-11172924-IBMCloud-Disable-config-drive-and-nocloud-only-if
4new file mode 10064413new file mode 100644
index 0000000..534aa73
--- /dev/null
+++ b/debian/patches/cpick-11172924-IBMCloud-Disable-config-drive-and-nocloud-only-if
@@ -0,0 +1,230 @@
1From 11172924a48a47a7231d19d9cefe628dfddda8bf Mon Sep 17 00:00:00 2001
2From: Scott Moser <smoser@ubuntu.com>
3Date: Mon, 30 Apr 2018 13:21:51 -0600
4Subject: [PATCH] IBMCloud: Disable config-drive and nocloud only if IBMCloud
5 is enabled.
6
7Ubuntu images on IBMCloud for 16.04 have some seed data in
8/var/lib/cloud/data/seed/nocloud-net. In order to have systems with
9IBMCloud enabled, we modified ds-identify detection to skip that seed
10if the system was on IBMCloud. That change did not consider the
11fact that IBMCloud might not be in the datasource list.
12
13There was similar logic in the ConfigDrive datasource in ds-identify
14and the datasource itself.
15
16Config drive is now updated to only check and avoid IBMCloud if IBMCloud
17is enabled. The check in ds-identify for nocloud was dropped. If a
18user provides a nocloud seed on IBMCloud, then that can be used.
19
20This means that systems running Xenial will continue to get their
21old datasources.
22
23LP: #1766401
24---
25 cloudinit/sources/DataSourceConfigDrive.py | 11 +++--
26 tests/unittests/test_ds_identify.py | 77 +++++++++++++++++++++++++++---
27 tools/ds-identify | 17 +++++--
28 3 files changed, 91 insertions(+), 14 deletions(-)
29
30--- a/cloudinit/sources/DataSourceConfigDrive.py
31+++ b/cloudinit/sources/DataSourceConfigDrive.py
32@@ -69,7 +69,8 @@
33 util.logexc(LOG, "Failed reading config drive from %s", sdir)
34
35 if not found:
36- for dev in find_candidate_devs():
37+ dslist = self.sys_cfg.get('datasource_list')
38+ for dev in find_candidate_devs(dslist=dslist):
39 try:
40 # Set mtype if freebsd and turn off sync
41 if dev.startswith("/dev/cd"):
42@@ -211,7 +212,7 @@
43 util.logexc(LOG, "Failed writing file: %s", filename)
44
45
46-def find_candidate_devs(probe_optical=True):
47+def find_candidate_devs(probe_optical=True, dslist=None):
48 """Return a list of devices that may contain the config drive.
49
50 The returned list is sorted by search order where the first item has
51@@ -227,6 +228,9 @@
52 * either vfat or iso9660 formated
53 * labeled with 'config-2' or 'CONFIG-2'
54 """
55+ if dslist is None:
56+ dslist = []
57+
58 # query optical drive to get it in blkid cache for 2.6 kernels
59 if probe_optical:
60 for device in OPTICAL_DEVICES:
61@@ -257,7 +261,8 @@
62 devices = [d for d in candidates
63 if d in by_label or not util.is_partition(d)]
64
65- if devices:
66+ LOG.debug("devices=%s dslist=%s", devices, dslist)
67+ if devices and "IBMCloud" in dslist:
68 # IBMCloud uses config-2 label, but limited to a single UUID.
69 ibm_platform, ibm_path = get_ibm_platform()
70 if ibm_path in devices:
71--- a/tests/unittests/test_ds_identify.py
72+++ b/tests/unittests/test_ds_identify.py
73@@ -178,17 +178,18 @@
74 data, RC_FOUND, dslist=[data.get('ds'), DS_NONE])
75
76 def _check_via_dict(self, data, rc, dslist=None, **kwargs):
77- found_rc, out, err, cfg, files = self._call_via_dict(data, **kwargs)
78+ ret = self._call_via_dict(data, **kwargs)
79 good = False
80 try:
81- self.assertEqual(rc, found_rc)
82+ self.assertEqual(rc, ret.rc)
83 if dslist is not None:
84- self.assertEqual(dslist, cfg['datasource_list'])
85+ self.assertEqual(dslist, ret.cfg['datasource_list'])
86 good = True
87 finally:
88 if not good:
89- _print_run_output(rc, out, err, cfg, files)
90- return rc, out, err, cfg, files
91+ _print_run_output(ret.rc, ret.stdout, ret.stderr, ret.cfg,
92+ ret.files)
93+ return ret
94
95 def test_wb_print_variables(self):
96 """_print_info reports an array of discovered variables to stderr."""
97@@ -237,13 +238,40 @@
98 def test_config_drive(self):
99 """ConfigDrive datasource has a disk with LABEL=config-2."""
100 self._test_ds_found('ConfigDrive')
101- return
102
103 def test_config_drive_upper(self):
104 """ConfigDrive datasource has a disk with LABEL=CONFIG-2."""
105 self._test_ds_found('ConfigDriveUpper')
106 return
107
108+ def test_config_drive_seed(self):
109+ """Config Drive seed directory."""
110+ self._test_ds_found('ConfigDrive-seed')
111+
112+ def test_config_drive_interacts_with_ibmcloud_config_disk(self):
113+ """Verify ConfigDrive interaction with IBMCloud.
114+
115+ If ConfigDrive is enabled and not IBMCloud, then ConfigDrive
116+ should claim the ibmcloud 'config-2' disk.
117+ If IBMCloud is enabled, then ConfigDrive should skip."""
118+ data = copy.deepcopy(VALID_CFG['IBMCloud-config-2'])
119+ files = data.get('files', {})
120+ if not files:
121+ data['files'] = files
122+ cfgpath = 'etc/cloud/cloud.cfg.d/99_networklayer_common.cfg'
123+
124+ # with list including IBMCloud, config drive should be not found.
125+ files[cfgpath] = 'datasource_list: [ ConfigDrive, IBMCloud ]\n'
126+ ret = self._check_via_dict(data, shell_true)
127+ self.assertEqual(
128+ ret.cfg.get('datasource_list'), ['IBMCloud', 'None'])
129+
130+ # But if IBMCloud is not enabled, config drive should claim this.
131+ files[cfgpath] = 'datasource_list: [ ConfigDrive, NoCloud ]\n'
132+ ret = self._check_via_dict(data, shell_true)
133+ self.assertEqual(
134+ ret.cfg.get('datasource_list'), ['ConfigDrive', 'None'])
135+
136 def test_ibmcloud_template_userdata_in_provisioning(self):
137 """Template provisioned with user-data during provisioning stage.
138
139@@ -295,6 +323,37 @@
140 self._check_via_dict(
141 data, rc=RC_FOUND, dslist=['ConfigDrive', DS_NONE])
142
143+ def test_ibmcloud_with_nocloud_seed(self):
144+ """NoCloud seed should be preferred over IBMCloud.
145+
146+ A nocloud seed should be preferred over IBMCloud even if enabled.
147+ Ubuntu 16.04 images have <vlc>/seed/nocloud-net. LP: #1766401."""
148+ data = copy.deepcopy(VALID_CFG['IBMCloud-config-2'])
149+ files = data.get('files', {})
150+ if not files:
151+ data['files'] = files
152+ files.update(VALID_CFG['NoCloud-seed']['files'])
153+ ret = self._check_via_dict(data, shell_true)
154+ self.assertEqual(
155+ ['NoCloud', 'IBMCloud', 'None'],
156+ ret.cfg.get('datasource_list'))
157+
158+ def test_ibmcloud_with_configdrive_seed(self):
159+ """ConfigDrive seed should be preferred over IBMCloud.
160+
161+ A ConfigDrive seed should be preferred over IBMCloud even if enabled.
162+ Ubuntu 16.04 images have a fstab entry that mounts the
163+ METADATA disk into <vlc>/seed/config_drive. LP: ##1766401."""
164+ data = copy.deepcopy(VALID_CFG['IBMCloud-config-2'])
165+ files = data.get('files', {})
166+ if not files:
167+ data['files'] = files
168+ files.update(VALID_CFG['ConfigDrive-seed']['files'])
169+ ret = self._check_via_dict(data, shell_true)
170+ self.assertEqual(
171+ ['ConfigDrive', 'IBMCloud', 'None'],
172+ ret.cfg.get('datasource_list'))
173+
174 def test_policy_disabled(self):
175 """A Builtin policy of 'disabled' should return not found.
176
177@@ -631,6 +690,12 @@
178 },
179 ],
180 },
181+ 'ConfigDrive-seed': {
182+ 'ds': 'ConfigDrive',
183+ 'files': {
184+ os.path.join(P_SEED_DIR, 'config_drive', 'openstack',
185+ 'latest', 'meta_data.json'): 'md\n'},
186+ },
187 'Hetzner': {
188 'ds': 'Hetzner',
189 'files': {P_SYS_VENDOR: 'Hetzner\n'},
190--- a/tools/ds-identify
191+++ b/tools/ds-identify
192@@ -600,7 +600,6 @@
193 *\ ds=nocloud*) return ${DS_FOUND};;
194 esac
195
196- is_ibm_cloud && return ${DS_NOT_FOUND}
197 for d in nocloud nocloud-net; do
198 check_seed_dir "$d" meta-data user-data && return ${DS_FOUND}
199 check_writable_seed_dir "$d" meta-data user-data && return ${DS_FOUND}
200@@ -611,11 +610,12 @@
201 return ${DS_NOT_FOUND}
202 }
203
204+is_ds_enabled() {
205+ local name="$1" pad=" ${DI_DSLIST} "
206+ [ "${pad#* $name }" != "${pad}" ]
207+}
208+
209 check_configdrive_v2() {
210- is_ibm_cloud && return ${DS_NOT_FOUND}
211- if has_fs_with_label CONFIG-2 config-2; then
212- return ${DS_FOUND}
213- fi
214 # look in /config-drive <vlc>/seed/config_drive for a directory
215 # openstack/YYYY-MM-DD format with a file meta_data.json
216 local d=""
217@@ -630,6 +630,13 @@
218 debug 1 "config drive seeded directory had only 'latest'"
219 return ${DS_FOUND}
220 fi
221+
222+ is_ds_enabled "IBMCloud"
223+ debug 1 "is_ds_enabled returned $?: $DI_DSLIST"
224+ is_ds_enabled "IBMCloud" && is_ibm_cloud && return ${DS_NOT_FOUND}
225+ if has_fs_with_label CONFIG-2 config-2; then
226+ return ${DS_FOUND}
227+ fi
228 return ${DS_NOT_FOUND}
229 }
230
diff --git a/debian/patches/cpick-6ef92c98-IBMCloud-recognize-provisioning-environment-during b/debian/patches/cpick-6ef92c98-IBMCloud-recognize-provisioning-environment-during
0new file mode 100644231new file mode 100644
index 0000000..b428120
--- /dev/null
+++ b/debian/patches/cpick-6ef92c98-IBMCloud-recognize-provisioning-environment-during
@@ -0,0 +1,389 @@
1From 6ef92c98c3d2b127b05d6708337efc8a81e00071 Mon Sep 17 00:00:00 2001
2From: Scott Moser <smoser@ubuntu.com>
3Date: Thu, 26 Apr 2018 16:24:24 -0500
4Subject: [PATCH] IBMCloud: recognize provisioning environment during debug
5 boots.
6
7When images are deployed from template in a production environment
8the artifacts of the provisioning stage (provisioningConfiguration.cfg)
9that cloud-init referenced are cleaned up. However, when provisioned
10in "debug" mode (internal to IBM) the artifacts are left.
11
12This changes the 'is_ibm_provisioning' implementations in both
13ds-identify and in the IBM datasource to identify the provisioning
14stage more correctly. The change is to consider provisioning only
15if the provisioing file existed and there was no log file or
16the log file was older than this boot.
17
18LP: #1767166
19---
20 cloudinit/sources/DataSourceIBMCloud.py | 42 +++++++++-----
21 cloudinit/tests/helpers.py | 13 ++++-
22 tests/unittests/test_datasource/test_ibmcloud.py | 50 ++++++++++++++++
23 tests/unittests/test_ds_identify.py | 72 +++++++++++++++++++++---
24 tools/ds-identify | 21 ++++++-
25 5 files changed, 175 insertions(+), 23 deletions(-)
26
27--- a/cloudinit/sources/DataSourceIBMCloud.py
28+++ b/cloudinit/sources/DataSourceIBMCloud.py
29@@ -8,17 +8,11 @@
30 * template: This is the legacy method of launching instances.
31 When booting from an image template, the system boots first into
32 a "provisioning" mode. There, host <-> guest mechanisms are utilized
33- to execute code in the guest and provision it.
34+ to execute code in the guest and configure it. The configuration
35+ includes configuring the system network and possibly installing
36+ packages and other software stack.
37
38- Cloud-init will disable itself when it detects that it is in the
39- provisioning mode. It detects this by the presence of
40- a file '/root/provisioningConfiguration.cfg'.
41-
42- When provided with user-data, the "first boot" will contain a
43- ConfigDrive-like disk labeled with 'METADATA'. If there is no user-data
44- provided, then there is no data-source.
45-
46- Cloud-init never does any network configuration in this mode.
47+ After the provisioning is finished, the system reboots.
48
49 * os_code: Essentially "launch by OS Code" (Operating System Code).
50 This is a more modern approach. There is no specific "provisioning" boot.
51@@ -138,8 +132,30 @@
52 return os.path.exists("/proc/xen")
53
54
55-def _is_ibm_provisioning():
56- return os.path.exists("/root/provisioningConfiguration.cfg")
57+def _is_ibm_provisioning(
58+ prov_cfg="/root/provisioningConfiguration.cfg",
59+ inst_log="/root/swinstall.log",
60+ boot_ref="/proc/1/environ"):
61+ """Return boolean indicating if this boot is ibm provisioning boot."""
62+ if os.path.exists(prov_cfg):
63+ msg = "config '%s' exists." % prov_cfg
64+ result = True
65+ if os.path.exists(inst_log):
66+ if os.path.exists(boot_ref):
67+ result = (os.stat(inst_log).st_mtime >
68+ os.stat(boot_ref).st_mtime)
69+ msg += (" log '%s' from %s boot." %
70+ (inst_log, "current" if result else "previous"))
71+ else:
72+ msg += (" log '%s' existed, but no reference file '%s'." %
73+ (inst_log, boot_ref))
74+ result = False
75+ else:
76+ msg += " log '%s' did not exist." % inst_log
77+ else:
78+ result, msg = (False, "config '%s' did not exist." % prov_cfg)
79+ LOG.debug("ibm_provisioning=%s: %s", result, msg)
80+ return result
81
82
83 def get_ibm_platform():
84@@ -189,7 +205,7 @@
85 else:
86 return (Platforms.TEMPLATE_LIVE_METADATA, metadata_path)
87 elif _is_ibm_provisioning():
88- return (Platforms.TEMPLATE_PROVISIONING_NODATA, None)
89+ return (Platforms.TEMPLATE_PROVISIONING_NODATA, None)
90 return not_found
91
92
93--- a/cloudinit/tests/helpers.py
94+++ b/cloudinit/tests/helpers.py
95@@ -8,6 +8,7 @@
96 import shutil
97 import sys
98 import tempfile
99+import time
100 import unittest
101
102 import mock
103@@ -285,7 +286,8 @@
104 os.path: [('isfile', 1), ('exists', 1),
105 ('islink', 1), ('isdir', 1), ('lexists', 1)],
106 os: [('listdir', 1), ('mkdir', 1),
107- ('lstat', 1), ('symlink', 2)]
108+ ('lstat', 1), ('symlink', 2),
109+ ('stat', 1)]
110 }
111
112 if hasattr(os, 'scandir'):
113@@ -354,6 +356,15 @@
114 return ret
115
116
117+def populate_dir_with_ts(path, data):
118+ """data is {'file': ('contents', mtime)}. mtime relative to now."""
119+ populate_dir(path, dict((k, v[0]) for k, v in data.items()))
120+ btime = time.time()
121+ for fpath, (_contents, mtime) in data.items():
122+ ts = btime + mtime if mtime else btime
123+ os.utime(os.path.sep.join((path, fpath)), (ts, ts))
124+
125+
126 def dir2dict(startdir, prefix=None):
127 flist = {}
128 if prefix is None:
129--- a/tests/unittests/test_datasource/test_ibmcloud.py
130+++ b/tests/unittests/test_datasource/test_ibmcloud.py
131@@ -259,4 +259,54 @@
132 ret['metadata'])
133
134
135+class TestIsIBMProvisioning(test_helpers.FilesystemMockingTestCase):
136+ """Test the _is_ibm_provisioning method."""
137+ inst_log = "/root/swinstall.log"
138+ prov_cfg = "/root/provisioningConfiguration.cfg"
139+ boot_ref = "/proc/1/environ"
140+ with_logs = True
141+
142+ def _call_with_root(self, rootd):
143+ self.reRoot(rootd)
144+ return ibm._is_ibm_provisioning()
145+
146+ def test_no_config(self):
147+ """No provisioning config means not provisioning."""
148+ self.assertFalse(self._call_with_root(self.tmp_dir()))
149+
150+ def test_config_only(self):
151+ """A provisioning config without a log means provisioning."""
152+ rootd = self.tmp_dir()
153+ test_helpers.populate_dir(rootd, {self.prov_cfg: "key=value"})
154+ self.assertTrue(self._call_with_root(rootd))
155+
156+ def test_config_with_old_log(self):
157+ """A config with a log from previous boot is not provisioning."""
158+ rootd = self.tmp_dir()
159+ data = {self.prov_cfg: ("key=value\nkey2=val2\n", -10),
160+ self.inst_log: ("log data\n", -30),
161+ self.boot_ref: ("PWD=/", 0)}
162+ test_helpers.populate_dir_with_ts(rootd, data)
163+ self.assertFalse(self._call_with_root(rootd=rootd))
164+ self.assertIn("from previous boot", self.logs.getvalue())
165+
166+ def test_config_with_new_log(self):
167+ """A config with a log from this boot is provisioning."""
168+ rootd = self.tmp_dir()
169+ data = {self.prov_cfg: ("key=value\nkey2=val2\n", -10),
170+ self.inst_log: ("log data\n", 30),
171+ self.boot_ref: ("PWD=/", 0)}
172+ test_helpers.populate_dir_with_ts(rootd, data)
173+ self.assertTrue(self._call_with_root(rootd=rootd))
174+ self.assertIn("from current boot", self.logs.getvalue())
175+
176+ def test_config_and_log_no_reference(self):
177+ """If the config and log existed, but no reference, assume not."""
178+ rootd = self.tmp_dir()
179+ test_helpers.populate_dir(
180+ rootd, {self.prov_cfg: "key=value", self.inst_log: "log data\n"})
181+ self.assertFalse(self._call_with_root(rootd=rootd))
182+ self.assertIn("no reference file", self.logs.getvalue())
183+
184+
185 # vi: ts=4 expandtab
186--- a/tests/unittests/test_ds_identify.py
187+++ b/tests/unittests/test_ds_identify.py
188@@ -1,5 +1,6 @@
189 # This file is part of cloud-init. See LICENSE file for license information.
190
191+from collections import namedtuple
192 import copy
193 import os
194 from uuid import uuid4
195@@ -7,7 +8,7 @@
196 from cloudinit import safeyaml
197 from cloudinit import util
198 from cloudinit.tests.helpers import (
199- CiTestCase, dir2dict, populate_dir)
200+ CiTestCase, dir2dict, populate_dir, populate_dir_with_ts)
201
202 from cloudinit.sources import DataSourceIBMCloud as dsibm
203
204@@ -66,7 +67,6 @@
205 P_SEED_DIR = "var/lib/cloud/seed"
206 P_DSID_CFG = "etc/cloud/ds-identify.cfg"
207
208-IBM_PROVISIONING_CHECK_PATH = "/root/provisioningConfiguration.cfg"
209 IBM_CONFIG_UUID = "9796-932E"
210
211 MOCK_VIRT_IS_KVM = {'name': 'detect_virt', 'RET': 'kvm', 'ret': 0}
212@@ -74,11 +74,17 @@
213 MOCK_VIRT_IS_XEN = {'name': 'detect_virt', 'RET': 'xen', 'ret': 0}
214 MOCK_UNAME_IS_PPC64 = {'name': 'uname', 'out': UNAME_PPC64EL, 'ret': 0}
215
216+shell_true = 0
217+shell_false = 1
218
219-class TestDsIdentify(CiTestCase):
220+CallReturn = namedtuple('CallReturn',
221+ ['rc', 'stdout', 'stderr', 'cfg', 'files'])
222+
223+
224+class DsIdentifyBase(CiTestCase):
225 dsid_path = os.path.realpath('tools/ds-identify')
226
227- def call(self, rootd=None, mocks=None, args=None, files=None,
228+ def call(self, rootd=None, mocks=None, func="main", args=None, files=None,
229 policy_dmi=DI_DEFAULT_POLICY,
230 policy_no_dmi=DI_DEFAULT_POLICY_NO_DMI,
231 ec2_strict_id=DI_EC2_STRICT_ID_DEFAULT):
232@@ -135,7 +141,7 @@
233 mocklines.append(write_mock(d))
234
235 endlines = [
236- 'main %s' % ' '.join(['"%s"' % s for s in args])
237+ func + ' ' + ' '.join(['"%s"' % s for s in args])
238 ]
239
240 with open(wrap, "w") as fp:
241@@ -159,7 +165,7 @@
242 cfg = {"_INVALID_YAML": contents,
243 "_EXCEPTION": str(e)}
244
245- return rc, out, err, cfg, dir2dict(rootd)
246+ return CallReturn(rc, out, err, cfg, dir2dict(rootd))
247
248 def _call_via_dict(self, data, rootd=None, **kwargs):
249 # return output of self.call with a dict input like VALID_CFG[item]
250@@ -191,6 +197,8 @@
251 ret.files)
252 return ret
253
254+
255+class TestDsIdentify(DsIdentifyBase):
256 def test_wb_print_variables(self):
257 """_print_info reports an array of discovered variables to stderr."""
258 data = VALID_CFG['Azure-dmi-detection']
259@@ -278,7 +286,10 @@
260 Template provisioning with user-data has METADATA disk,
261 datasource should return not found."""
262 data = copy.deepcopy(VALID_CFG['IBMCloud-metadata'])
263- data['files'] = {IBM_PROVISIONING_CHECK_PATH: 'xxx'}
264+ # change the 'is_ibm_provisioning' mock to return 1 (false)
265+ isprov_m = [m for m in data['mocks']
266+ if m["name"] == "is_ibm_provisioning"][0]
267+ isprov_m['ret'] = shell_true
268 return self._check_via_dict(data, RC_NOT_FOUND)
269
270 def test_ibmcloud_template_userdata(self):
271@@ -293,7 +304,8 @@
272
273 no disks attached. Datasource should return not found."""
274 data = copy.deepcopy(VALID_CFG['IBMCloud-nodisks'])
275- data['files'] = {IBM_PROVISIONING_CHECK_PATH: 'xxx'}
276+ data['mocks'].append(
277+ {'name': 'is_ibm_provisioning', 'ret': shell_true})
278 return self._check_via_dict(data, RC_NOT_FOUND)
279
280 def test_ibmcloud_template_no_userdata(self):
281@@ -505,6 +517,47 @@
282 self._test_ds_found('Hetzner')
283
284
285+class TestIsIBMProvisioning(DsIdentifyBase):
286+ """Test the is_ibm_provisioning method in ds-identify."""
287+
288+ inst_log = "/root/swinstall.log"
289+ prov_cfg = "/root/provisioningConfiguration.cfg"
290+ boot_ref = "/proc/1/environ"
291+ funcname = "is_ibm_provisioning"
292+
293+ def test_no_config(self):
294+ """No provisioning config means not provisioning."""
295+ ret = self.call(files={}, func=self.funcname)
296+ self.assertEqual(shell_false, ret.rc)
297+
298+ def test_config_only(self):
299+ """A provisioning config without a log means provisioning."""
300+ ret = self.call(files={self.prov_cfg: "key=value"}, func=self.funcname)
301+ self.assertEqual(shell_true, ret.rc)
302+
303+ def test_config_with_old_log(self):
304+ """A config with a log from previous boot is not provisioning."""
305+ rootd = self.tmp_dir()
306+ data = {self.prov_cfg: ("key=value\nkey2=val2\n", -10),
307+ self.inst_log: ("log data\n", -30),
308+ self.boot_ref: ("PWD=/", 0)}
309+ populate_dir_with_ts(rootd, data)
310+ ret = self.call(rootd=rootd, func=self.funcname)
311+ self.assertEqual(shell_false, ret.rc)
312+ self.assertIn("from previous boot", ret.stderr)
313+
314+ def test_config_with_new_log(self):
315+ """A config with a log from this boot is provisioning."""
316+ rootd = self.tmp_dir()
317+ data = {self.prov_cfg: ("key=value\nkey2=val2\n", -10),
318+ self.inst_log: ("log data\n", 30),
319+ self.boot_ref: ("PWD=/", 0)}
320+ populate_dir_with_ts(rootd, data)
321+ ret = self.call(rootd=rootd, func=self.funcname)
322+ self.assertEqual(shell_true, ret.rc)
323+ self.assertIn("from current boot", ret.stderr)
324+
325+
326 def blkid_out(disks=None):
327 """Convert a list of disk dictionaries into blkid content."""
328 if disks is None:
329@@ -704,6 +757,7 @@
330 'ds': 'IBMCloud',
331 'mocks': [
332 MOCK_VIRT_IS_XEN,
333+ {'name': 'is_ibm_provisioning', 'ret': shell_false},
334 {'name': 'blkid', 'ret': 0,
335 'out': blkid_out(
336 [{'DEVNAME': 'xvda1', 'TYPE': 'vfat', 'PARTUUID': uuid4()},
337@@ -717,6 +771,7 @@
338 'ds': 'IBMCloud',
339 'mocks': [
340 MOCK_VIRT_IS_XEN,
341+ {'name': 'is_ibm_provisioning', 'ret': shell_false},
342 {'name': 'blkid', 'ret': 0,
343 'out': blkid_out(
344 [{'DEVNAME': 'xvda1', 'TYPE': 'ext3', 'PARTUUID': uuid4(),
345@@ -734,6 +789,7 @@
346 'ds': 'IBMCloud',
347 'mocks': [
348 MOCK_VIRT_IS_XEN,
349+ {'name': 'is_ibm_provisioning', 'ret': shell_false},
350 {'name': 'blkid', 'ret': 0,
351 'out': blkid_out(
352 [{'DEVNAME': 'xvda1', 'TYPE': 'vfat', 'PARTUUID': uuid4()},
353--- a/tools/ds-identify
354+++ b/tools/ds-identify
355@@ -125,6 +125,7 @@
356 DI_EC2_STRICT_ID_DEFAULT="true"
357
358 _IS_IBM_CLOUD=""
359+_IS_IBM_PROVISIONING=""
360
361 error() {
362 set -- "ERROR:" "$@";
363@@ -1013,7 +1014,25 @@
364 }
365
366 is_ibm_provisioning() {
367- [ -f "${PATH_ROOT}/root/provisioningConfiguration.cfg" ]
368+ local pcfg="${PATH_ROOT}/root/provisioningConfiguration.cfg"
369+ local logf="${PATH_ROOT}/root/swinstall.log"
370+ local is_prov=false msg="config '$pcfg' did not exist."
371+ if [ -f "$pcfg" ]; then
372+ msg="config '$pcfg' exists."
373+ is_prov=true
374+ if [ -f "$logf" ]; then
375+ if [ "$logf" -nt "$PATH_PROC_1_ENVIRON" ]; then
376+ msg="$msg log '$logf' from current boot."
377+ else
378+ is_prov=false
379+ msg="$msg log '$logf' from previous boot."
380+ fi
381+ else
382+ msg="$msg log '$logf' did not exist."
383+ fi
384+ fi
385+ debug 2 "ibm_provisioning=$is_prov: $msg"
386+ [ "$is_prov" = "true" ]
387 }
388
389 is_ibm_cloud() {
diff --git a/debian/patches/series b/debian/patches/series
0new file mode 100644390new file mode 100644
index 0000000..2f90183
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,2 @@
1cpick-11172924-IBMCloud-Disable-config-drive-and-nocloud-only-if
2cpick-6ef92c98-IBMCloud-recognize-provisioning-environment-during

Subscribers

People subscribed via source and target branches