Merge ~chad.smith/cloud-init:datasource-ibmcloud into cloud-init:master

Proposed by Chad Smith
Status: Merged
Approved by: Chad Smith
Approved revision: 332275089657698b78826e8adbdc5ed62069ced8
Merge reported by: Chad Smith
Merged at revision: e0f644b7c8c76bd63d242558685722cc70d9c51d
Proposed branch: ~chad.smith/cloud-init:datasource-ibmcloud
Merge into: cloud-init:master
Diff against target: 1049 lines (+857/-12)
7 files modified
cloudinit/sources/DataSourceConfigDrive.py (+10/-0)
cloudinit/sources/DataSourceIBMCloud.py (+325/-0)
cloudinit/tests/test_util.py (+72/-0)
cloudinit/util.py (+31/-0)
tests/unittests/test_datasource/test_ibmcloud.py (+262/-0)
tests/unittests/test_ds_identify.py (+101/-3)
tools/ds-identify (+56/-9)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Chad Smith Approve
Review via email: mp+342009@code.launchpad.net

Commit message

IBMCloud: Initial IBM Cloud datasource.

This adds a specific IBM Cloud datasource.
IBM Cloud is identified by:
 a.) running on xen
 b.) one of a LABEL=METADATA disk or a LABEL=config-2 disk with
     UUID=9796-932E

The datasource contains its own config-drive reader that reads
only the currently supported portion of config-drive needed for
ibm cloud.

During the provisioning boot, cloud-init is disabled.

See the docstring in DataSourceIBMCloud.py for more more information.

Description of the change

see commit message. Shepherd smoser's branch into tip

To post a comment you must log in.
Revision history for this message
Chad Smith (chad.smith) wrote :

Addressed minor review comments to land this branch.

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

FAILED: Continuous integration, rev:4641cef07c60683296f596d395d3a4d2430b177b
https://jenkins.ubuntu.com/server/job/cloud-init-ci/921/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    FAILED: Ubuntu LTS: Integration

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

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

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

review: Approve (continuous-integration)
Revision history for this message
Chad Smith (chad.smith) wrote :

An upstream commit landed for this bug.

To view that commit see the following URL:
https://git.launchpad.net/cloud-init/commit/?id=e0f644b7

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/cloudinit/sources/DataSourceConfigDrive.py b/cloudinit/sources/DataSourceConfigDrive.py
index b8db626..c7b5fe5 100644
--- a/cloudinit/sources/DataSourceConfigDrive.py
+++ b/cloudinit/sources/DataSourceConfigDrive.py
@@ -14,6 +14,7 @@ from cloudinit import util
1414
15from cloudinit.net import eni15from cloudinit.net import eni
1616
17from cloudinit.sources.DataSourceIBMCloud import get_ibm_platform
17from cloudinit.sources.helpers import openstack18from cloudinit.sources.helpers import openstack
1819
19LOG = logging.getLogger(__name__)20LOG = logging.getLogger(__name__)
@@ -255,6 +256,15 @@ def find_candidate_devs(probe_optical=True):
255 # an unpartitioned block device (ex sda, not sda1)256 # an unpartitioned block device (ex sda, not sda1)
256 devices = [d for d in candidates257 devices = [d for d in candidates
257 if d in by_label or not util.is_partition(d)]258 if d in by_label or not util.is_partition(d)]
259
260 if devices:
261 # IBMCloud uses config-2 label, but limited to a single UUID.
262 ibm_platform, ibm_path = get_ibm_platform()
263 if ibm_path in devices:
264 devices.remove(ibm_path)
265 LOG.debug("IBMCloud device '%s' (%s) removed from candidate list",
266 ibm_path, ibm_platform)
267
258 return devices268 return devices
259269
260270
diff --git a/cloudinit/sources/DataSourceIBMCloud.py b/cloudinit/sources/DataSourceIBMCloud.py
261new file mode 100644271new file mode 100644
index 0000000..02b3d56
--- /dev/null
+++ b/cloudinit/sources/DataSourceIBMCloud.py
@@ -0,0 +1,325 @@
1# This file is part of cloud-init. See LICENSE file for license information.
2"""Datasource for IBMCloud.
3
4IBMCloud is also know as SoftLayer or BlueMix.
5IBMCloud hypervisor is xen (2018-03-10).
6
7There are 2 different api exposed launch methods.
8 * template: This is the legacy method of launching instances.
9 When booting from an image template, the system boots first into
10 a "provisioning" mode. There, host <-> guest mechanisms are utilized
11 to execute code in the guest and provision it.
12
13 Cloud-init will disable itself when it detects that it is in the
14 provisioning mode. It detects this by the presence of
15 a file '/root/provisioningConfiguration.cfg'.
16
17 When provided with user-data, the "first boot" will contain a
18 ConfigDrive-like disk labeled with 'METADATA'. If there is no user-data
19 provided, then there is no data-source.
20
21 Cloud-init never does any network configuration in this mode.
22
23 * os_code: Essentially "launch by OS Code" (Operating System Code).
24 This is a more modern approach. There is no specific "provisioning" boot.
25 Instead, cloud-init does all the customization. With or without
26 user-data provided, an OpenStack ConfigDrive like disk is attached.
27
28 Only disks with label 'config-2' and UUID '9796-932E' are considered.
29 This is to avoid this datasource claiming ConfigDrive. This does
30 mean that 1 in 8^16 (~4 billion) Xen ConfigDrive systems will be
31 incorrectly identified as IBMCloud.
32
33TODO:
34 * is uuid (/sys/hypervisor/uuid) stable for life of an instance?
35 it seems it is not the same as data's uuid in the os_code case
36 but is in the template case.
37
38"""
39import base64
40import json
41import os
42
43from cloudinit import log as logging
44from cloudinit import sources
45from cloudinit.sources.helpers import openstack
46from cloudinit import util
47
48LOG = logging.getLogger(__name__)
49
50IBM_CONFIG_UUID = "9796-932E"
51
52
53class Platforms(object):
54 TEMPLATE_LIVE_METADATA = "Template/Live/Metadata"
55 TEMPLATE_LIVE_NODATA = "UNABLE TO BE IDENTIFIED."
56 TEMPLATE_PROVISIONING_METADATA = "Template/Provisioning/Metadata"
57 TEMPLATE_PROVISIONING_NODATA = "Template/Provisioning/No-Metadata"
58 OS_CODE = "OS-Code/Live"
59
60
61PROVISIONING = (
62 Platforms.TEMPLATE_PROVISIONING_METADATA,
63 Platforms.TEMPLATE_PROVISIONING_NODATA)
64
65
66class DataSourceIBMCloud(sources.DataSource):
67
68 dsname = 'IBMCloud'
69 system_uuid = None
70
71 def __init__(self, sys_cfg, distro, paths):
72 super(DataSourceIBMCloud, self).__init__(sys_cfg, distro, paths)
73 self.source = None
74 self._network_config = None
75 self.network_json = None
76 self.platform = None
77
78 def __str__(self):
79 root = super(DataSourceIBMCloud, self).__str__()
80 mstr = "%s [%s %s]" % (root, self.platform, self.source)
81 return mstr
82
83 def _get_data(self):
84 results = read_md()
85 if results is None:
86 return False
87
88 self.source = results['source']
89 self.platform = results['platform']
90 self.metadata = results['metadata']
91 self.userdata_raw = results.get('userdata')
92 self.network_json = results.get('networkdata')
93 vd = results.get('vendordata')
94 self.vendordata_pure = vd
95 self.system_uuid = results['system-uuid']
96 try:
97 self.vendordata_raw = sources.convert_vendordata(vd)
98 except ValueError as e:
99 LOG.warning("Invalid content in vendor-data: %s", e)
100 self.vendordata_raw = None
101
102 return True
103
104 def check_instance_id(self, sys_cfg):
105 """quickly (local check only) if self.instance_id is still valid
106
107 in Template mode, the system uuid (/sys/hypervisor/uuid) is the
108 same as found in the METADATA disk. But that is not true in OS_CODE
109 mode. So we read the system_uuid and keep that for later compare."""
110 if self.system_uuid is None:
111 return False
112 return self.system_uuid == _read_system_uuid()
113
114 @property
115 def network_config(self):
116 if self.platform != Platforms.OS_CODE:
117 # If deployed from template, an agent in the provisioning
118 # environment handles networking configuration. Not cloud-init.
119 return {'config': 'disabled', 'version': 1}
120 if self._network_config is None:
121 if self.network_json is not None:
122 LOG.debug("network config provided via network_json")
123 self._network_config = openstack.convert_net_json(
124 self.network_json, known_macs=None)
125 else:
126 LOG.debug("no network configuration available.")
127 return self._network_config
128
129
130def _read_system_uuid():
131 uuid_path = "/sys/hypervisor/uuid"
132 if not os.path.isfile(uuid_path):
133 return None
134 return util.load_file(uuid_path).strip().lower()
135
136
137def _is_xen():
138 return os.path.exists("/proc/xen")
139
140
141def _is_ibm_provisioning():
142 return os.path.exists("/root/provisioningConfiguration.cfg")
143
144
145def get_ibm_platform():
146 """Return a tuple (Platform, path)
147
148 If this is Not IBM cloud, then the return value is (None, None).
149 An instance in provisioning mode is considered running on IBM cloud."""
150 label_mdata = "METADATA"
151 label_cfg2 = "CONFIG-2"
152 not_found = (None, None)
153
154 if not _is_xen():
155 return not_found
156
157 # fslabels contains only the first entry with a given label.
158 fslabels = {}
159 try:
160 devs = util.blkid()
161 except util.ProcessExecutionError as e:
162 LOG.warning("Failed to run blkid: %s", e)
163 return (None, None)
164
165 for dev in sorted(devs.keys()):
166 data = devs[dev]
167 label = data.get("LABEL", "").upper()
168 uuid = data.get("UUID", "").upper()
169 if label not in (label_mdata, label_cfg2):
170 continue
171 if label in fslabels:
172 LOG.warning("Duplicate fslabel '%s'. existing=%s current=%s",
173 label, fslabels[label], data)
174 continue
175 if label == label_cfg2 and uuid != IBM_CONFIG_UUID:
176 LOG.debug("Skipping %s with LABEL=%s due to uuid != %s: %s",
177 dev, label, uuid, data)
178 continue
179 fslabels[label] = data
180
181 metadata_path = fslabels.get(label_mdata, {}).get('DEVNAME')
182 cfg2_path = fslabels.get(label_cfg2, {}).get('DEVNAME')
183
184 if cfg2_path:
185 return (Platforms.OS_CODE, cfg2_path)
186 elif metadata_path:
187 if _is_ibm_provisioning():
188 return (Platforms.TEMPLATE_PROVISIONING_METADATA, metadata_path)
189 else:
190 return (Platforms.TEMPLATE_LIVE_METADATA, metadata_path)
191 elif _is_ibm_provisioning():
192 return (Platforms.TEMPLATE_PROVISIONING_NODATA, None)
193 return not_found
194
195
196def read_md():
197 """Read data from IBM Cloud.
198
199 @return: None if not running on IBM Cloud.
200 dictionary with guaranteed fields: metadata, version
201 and optional fields: userdata, vendordata, networkdata.
202 Also includes the system uuid from /sys/hypervisor/uuid."""
203 platform, path = get_ibm_platform()
204 if platform is None:
205 LOG.debug("This is not an IBMCloud platform.")
206 return None
207 elif platform in PROVISIONING:
208 LOG.debug("Cloud-init is disabled during provisioning: %s.",
209 platform)
210 return None
211
212 ret = {'platform': platform, 'source': path,
213 'system-uuid': _read_system_uuid()}
214
215 try:
216 if os.path.isdir(path):
217 results = metadata_from_dir(path)
218 else:
219 results = util.mount_cb(path, metadata_from_dir)
220 except BrokenMetadata as e:
221 raise RuntimeError(
222 "Failed reading IBM config disk (platform=%s path=%s): %s" %
223 (platform, path, e))
224
225 ret.update(results)
226 return ret
227
228
229class BrokenMetadata(IOError):
230 pass
231
232
233def metadata_from_dir(source_dir):
234 """Walk source_dir extracting standardized metadata.
235
236 Certain metadata keys are renamed to present a standardized set of metadata
237 keys.
238
239 This function has a lot in common with ConfigDriveReader.read_v2 but
240 there are a number of inconsistencies, such key renames and as only
241 presenting a 'latest' version which make it an unlikely candidate to share
242 code.
243
244 @return: Dict containing translated metadata, userdata, vendordata,
245 networkdata as present.
246 """
247
248 def opath(fname):
249 return os.path.join("openstack", "latest", fname)
250
251 def load_json_bytes(blob):
252 return json.loads(blob.decode('utf-8'))
253
254 files = [
255 # tuples of (results_name, path, translator)
256 ('metadata_raw', opath('meta_data.json'), load_json_bytes),
257 ('userdata', opath('user_data'), None),
258 ('vendordata', opath('vendor_data.json'), load_json_bytes),
259 ('networkdata', opath('network_data.json'), load_json_bytes),
260 ]
261
262 results = {}
263 for (name, path, transl) in files:
264 fpath = os.path.join(source_dir, path)
265 raw = None
266 try:
267 raw = util.load_file(fpath, decode=False)
268 except IOError as e:
269 LOG.debug("Failed reading path '%s': %s", fpath, e)
270
271 if raw is None or transl is None:
272 data = raw
273 else:
274 try:
275 data = transl(raw)
276 except Exception as e:
277 raise BrokenMetadata("Failed decoding %s: %s" % (path, e))
278
279 results[name] = data
280
281 if results.get('metadata_raw') is None:
282 raise BrokenMetadata(
283 "%s missing required file 'meta_data.json'" % source_dir)
284
285 results['metadata'] = {}
286
287 md_raw = results['metadata_raw']
288 md = results['metadata']
289 if 'random_seed' in md_raw:
290 try:
291 md['random_seed'] = base64.b64decode(md_raw['random_seed'])
292 except (ValueError, TypeError) as e:
293 raise BrokenMetadata(
294 "Badly formatted metadata random_seed entry: %s" % e)
295
296 renames = (
297 ('public_keys', 'public-keys'), ('hostname', 'local-hostname'),
298 ('uuid', 'instance-id'))
299 for mdname, newname in renames:
300 if mdname in md_raw:
301 md[newname] = md_raw[mdname]
302
303 return results
304
305
306# Used to match classes to dependencies
307datasources = [
308 (DataSourceIBMCloud, (sources.DEP_FILESYSTEM,)),
309]
310
311
312# Return a list of data sources that match this set of dependencies
313def get_datasource_list(depends):
314 return sources.list_from_depends(depends, datasources)
315
316
317if __name__ == "__main__":
318 import argparse
319
320 parser = argparse.ArgumentParser(description='Query IBM Cloud Metadata')
321 args = parser.parse_args()
322 data = read_md()
323 print(util.json_dumps(data))
324
325# vi: ts=4 expandtab
diff --git a/cloudinit/tests/test_util.py b/cloudinit/tests/test_util.py
index d30643d..3f37dbb 100644
--- a/cloudinit/tests/test_util.py
+++ b/cloudinit/tests/test_util.py
@@ -3,6 +3,7 @@
3"""Tests for cloudinit.util"""3"""Tests for cloudinit.util"""
44
5import logging5import logging
6from textwrap import dedent
67
7import cloudinit.util as util8import cloudinit.util as util
89
@@ -140,4 +141,75 @@ class TestGetHostnameFqdn(CiTestCase):
140 [{'fqdn': True, 'metadata_only': True},141 [{'fqdn': True, 'metadata_only': True},
141 {'metadata_only': True}], mycloud.calls)142 {'metadata_only': True}], mycloud.calls)
142143
144
145class TestBlkid(CiTestCase):
146 ids = {
147 "id01": "1111-1111",
148 "id02": "22222222-2222",
149 "id03": "33333333-3333",
150 "id04": "44444444-4444",
151 "id05": "55555555-5555-5555-5555-555555555555",
152 "id06": "66666666-6666-6666-6666-666666666666",
153 "id07": "52894610484658920398",
154 "id08": "86753098675309867530",
155 "id09": "99999999-9999-9999-9999-999999999999",
156 }
157
158 blkid_out = dedent("""\
159 /dev/loop0: TYPE="squashfs"
160 /dev/loop1: TYPE="squashfs"
161 /dev/loop2: TYPE="squashfs"
162 /dev/loop3: TYPE="squashfs"
163 /dev/sda1: UUID="{id01}" TYPE="vfat" PARTUUID="{id02}"
164 /dev/sda2: UUID="{id03}" TYPE="ext4" PARTUUID="{id04}"
165 /dev/sda3: UUID="{id05}" TYPE="ext4" PARTUUID="{id06}"
166 /dev/sda4: LABEL="default" UUID="{id07}" UUID_SUB="{id08}" """
167 """TYPE="zfs_member" PARTUUID="{id09}"
168 /dev/loop4: TYPE="squashfs"
169 """)
170
171 maxDiff = None
172
173 def _get_expected(self):
174 return ({
175 "/dev/loop0": {"DEVNAME": "/dev/loop0", "TYPE": "squashfs"},
176 "/dev/loop1": {"DEVNAME": "/dev/loop1", "TYPE": "squashfs"},
177 "/dev/loop2": {"DEVNAME": "/dev/loop2", "TYPE": "squashfs"},
178 "/dev/loop3": {"DEVNAME": "/dev/loop3", "TYPE": "squashfs"},
179 "/dev/loop4": {"DEVNAME": "/dev/loop4", "TYPE": "squashfs"},
180 "/dev/sda1": {"DEVNAME": "/dev/sda1", "TYPE": "vfat",
181 "UUID": self.ids["id01"],
182 "PARTUUID": self.ids["id02"]},
183 "/dev/sda2": {"DEVNAME": "/dev/sda2", "TYPE": "ext4",
184 "UUID": self.ids["id03"],
185 "PARTUUID": self.ids["id04"]},
186 "/dev/sda3": {"DEVNAME": "/dev/sda3", "TYPE": "ext4",
187 "UUID": self.ids["id05"],
188 "PARTUUID": self.ids["id06"]},
189 "/dev/sda4": {"DEVNAME": "/dev/sda4", "TYPE": "zfs_member",
190 "LABEL": "default",
191 "UUID": self.ids["id07"],
192 "UUID_SUB": self.ids["id08"],
193 "PARTUUID": self.ids["id09"]},
194 })
195
196 @mock.patch("cloudinit.util.subp")
197 def test_functional_blkid(self, m_subp):
198 m_subp.return_value = (
199 self.blkid_out.format(**self.ids), "")
200 self.assertEqual(self._get_expected(), util.blkid())
201 m_subp.assert_called_with(["blkid", "-o", "full"], capture=True,
202 decode="replace")
203
204 @mock.patch("cloudinit.util.subp")
205 def test_blkid_no_cache_uses_no_cache(self, m_subp):
206 """blkid should turn off cache if disable_cache is true."""
207 m_subp.return_value = (
208 self.blkid_out.format(**self.ids), "")
209 self.assertEqual(self._get_expected(),
210 util.blkid(disable_cache=True))
211 m_subp.assert_called_with(["blkid", "-o", "full", "-c", "/dev/null"],
212 capture=True, decode="replace")
213
214
143# vi: ts=4 expandtab215# vi: ts=4 expandtab
diff --git a/cloudinit/util.py b/cloudinit/util.py
index cae8b19..fb4ee5f 100644
--- a/cloudinit/util.py
+++ b/cloudinit/util.py
@@ -1237,6 +1237,37 @@ def find_devs_with(criteria=None, oformat='device',
1237 return entries1237 return entries
12381238
12391239
1240def blkid(devs=None, disable_cache=False):
1241 """Get all device tags details from blkid.
1242
1243 @param devs: Optional list of device paths you wish to query.
1244 @param disable_cache: Bool, set True to start with clean cache.
1245
1246 @return: Dict of key value pairs of info for the device.
1247 """
1248 if devs is None:
1249 devs = []
1250 else:
1251 devs = list(devs)
1252
1253 cmd = ['blkid', '-o', 'full']
1254 if disable_cache:
1255 cmd.extend(['-c', '/dev/null'])
1256 cmd.extend(devs)
1257
1258 # we have to decode with 'replace' as shelx.split (called by
1259 # load_shell_content) can't take bytes. So this is potentially
1260 # lossy of non-utf-8 chars in blkid output.
1261 out, _ = subp(cmd, capture=True, decode="replace")
1262 ret = {}
1263 for line in out.splitlines():
1264 dev, _, data = line.partition(":")
1265 ret[dev] = load_shell_content(data)
1266 ret[dev]["DEVNAME"] = dev
1267
1268 return ret
1269
1270
1240def peek_file(fname, max_bytes):1271def peek_file(fname, max_bytes):
1241 LOG.debug("Peeking at %s (max_bytes=%s)", fname, max_bytes)1272 LOG.debug("Peeking at %s (max_bytes=%s)", fname, max_bytes)
1242 with open(fname, 'rb') as ifh:1273 with open(fname, 'rb') as ifh:
diff --git a/tests/unittests/test_datasource/test_ibmcloud.py b/tests/unittests/test_datasource/test_ibmcloud.py
1243new file mode 1006441274new file mode 100644
index 0000000..621cfe4
--- /dev/null
+++ b/tests/unittests/test_datasource/test_ibmcloud.py
@@ -0,0 +1,262 @@
1# This file is part of cloud-init. See LICENSE file for license information.
2
3from cloudinit.sources import DataSourceIBMCloud as ibm
4from cloudinit.tests import helpers as test_helpers
5
6import base64
7import copy
8import json
9import mock
10from textwrap import dedent
11
12D_PATH = "cloudinit.sources.DataSourceIBMCloud."
13
14
15class TestIBMCloud(test_helpers.CiTestCase):
16 """Test the datasource."""
17 def setUp(self):
18 super(TestIBMCloud, self).setUp()
19 pass
20
21
22@mock.patch(D_PATH + "_is_xen", return_value=True)
23@mock.patch(D_PATH + "_is_ibm_provisioning")
24@mock.patch(D_PATH + "util.blkid")
25class TestGetIBMPlatform(test_helpers.CiTestCase):
26 """Test the get_ibm_platform helper."""
27
28 blkid_base = {
29 "/dev/xvda1": {
30 "DEVNAME": "/dev/xvda1", "LABEL": "cloudimg-bootfs",
31 "TYPE": "ext3"},
32 "/dev/xvda2": {
33 "DEVNAME": "/dev/xvda2", "LABEL": "cloudimg-rootfs",
34 "TYPE": "ext4"},
35 }
36
37 blkid_metadata_disk = {
38 "/dev/xvdh1": {
39 "DEVNAME": "/dev/xvdh1", "LABEL": "METADATA", "TYPE": "vfat",
40 "SEC_TYPE": "msdos", "UUID": "681B-8C5D",
41 "PARTUUID": "3d631e09-01"},
42 }
43
44 blkid_oscode_disk = {
45 "/dev/xvdh": {
46 "DEVNAME": "/dev/xvdh", "LABEL": "config-2", "TYPE": "vfat",
47 "SEC_TYPE": "msdos", "UUID": ibm.IBM_CONFIG_UUID}
48 }
49
50 def setUp(self):
51 self.blkid_metadata = copy.deepcopy(self.blkid_base)
52 self.blkid_metadata.update(copy.deepcopy(self.blkid_metadata_disk))
53
54 self.blkid_oscode = copy.deepcopy(self.blkid_base)
55 self.blkid_oscode.update(copy.deepcopy(self.blkid_oscode_disk))
56
57 def test_id_template_live_metadata(self, m_blkid, m_is_prov, _m_xen):
58 """identify TEMPLATE_LIVE_METADATA."""
59 m_blkid.return_value = self.blkid_metadata
60 m_is_prov.return_value = False
61 self.assertEqual(
62 (ibm.Platforms.TEMPLATE_LIVE_METADATA, "/dev/xvdh1"),
63 ibm.get_ibm_platform())
64
65 def test_id_template_prov_metadata(self, m_blkid, m_is_prov, _m_xen):
66 """identify TEMPLATE_PROVISIONING_METADATA."""
67 m_blkid.return_value = self.blkid_metadata
68 m_is_prov.return_value = True
69 self.assertEqual(
70 (ibm.Platforms.TEMPLATE_PROVISIONING_METADATA, "/dev/xvdh1"),
71 ibm.get_ibm_platform())
72
73 def test_id_template_prov_nodata(self, m_blkid, m_is_prov, _m_xen):
74 """identify TEMPLATE_PROVISIONING_NODATA."""
75 m_blkid.return_value = self.blkid_base
76 m_is_prov.return_value = True
77 self.assertEqual(
78 (ibm.Platforms.TEMPLATE_PROVISIONING_NODATA, None),
79 ibm.get_ibm_platform())
80
81 def test_id_os_code(self, m_blkid, m_is_prov, _m_xen):
82 """Identify OS_CODE."""
83 m_blkid.return_value = self.blkid_oscode
84 m_is_prov.return_value = False
85 self.assertEqual((ibm.Platforms.OS_CODE, "/dev/xvdh"),
86 ibm.get_ibm_platform())
87
88 def test_id_os_code_must_match_uuid(self, m_blkid, m_is_prov, _m_xen):
89 """Test against false positive on openstack with non-ibm UUID."""
90 blkid = self.blkid_oscode
91 blkid["/dev/xvdh"]["UUID"] = "9999-9999"
92 m_blkid.return_value = blkid
93 m_is_prov.return_value = False
94 self.assertEqual((None, None), ibm.get_ibm_platform())
95
96
97@mock.patch(D_PATH + "_read_system_uuid", return_value=None)
98@mock.patch(D_PATH + "get_ibm_platform")
99class TestReadMD(test_helpers.CiTestCase):
100 """Test the read_datasource helper."""
101
102 template_md = {
103 "files": [],
104 "network_config": {"content_path": "/content/interfaces"},
105 "hostname": "ci-fond-ram",
106 "name": "ci-fond-ram",
107 "domain": "testing.ci.cloud-init.org",
108 "meta": {"dsmode": "net"},
109 "uuid": "8e636730-9f5d-c4a5-327c-d7123c46e82f",
110 "public_keys": {"1091307": "ssh-rsa AAAAB3NzaC1...Hw== ci-pubkey"},
111 }
112
113 oscode_md = {
114 "hostname": "ci-grand-gannet.testing.ci.cloud-init.org",
115 "name": "ci-grand-gannet",
116 "uuid": "2f266908-8e6c-4818-9b5c-42e9cc66a785",
117 "random_seed": "bm90LXJhbmRvbQo=",
118 "crypt_key": "ssh-rsa AAAAB3NzaC1yc2..n6z/",
119 "configuration_token": "eyJhbGciOi..M3ZA",
120 "public_keys": {"1091307": "ssh-rsa AAAAB3N..Hw== ci-pubkey"},
121 }
122
123 content_interfaces = dedent("""\
124 auto lo
125 iface lo inet loopback
126
127 auto eth0
128 allow-hotplug eth0
129 iface eth0 inet static
130 address 10.82.43.5
131 netmask 255.255.255.192
132 """)
133
134 userdata = b"#!/bin/sh\necho hi mom\n"
135 # meta.js file gets json encoded userdata as a list.
136 meta_js = '["#!/bin/sh\necho hi mom\n"]'
137 vendor_data = {
138 "cloud-init": "#!/bin/bash\necho 'root:$6$5ab01p1m1' | chpasswd -e"}
139
140 network_data = {
141 "links": [
142 {"id": "interface_29402281", "name": "eth0", "mtu": None,
143 "type": "phy", "ethernet_mac_address": "06:00:f1:bd:da:25"},
144 {"id": "interface_29402279", "name": "eth1", "mtu": None,
145 "type": "phy", "ethernet_mac_address": "06:98:5e:d0:7f:86"}
146 ],
147 "networks": [
148 {"id": "network_109887563", "link": "interface_29402281",
149 "type": "ipv4", "ip_address": "10.82.43.2",
150 "netmask": "255.255.255.192",
151 "routes": [
152 {"network": "10.0.0.0", "netmask": "255.0.0.0",
153 "gateway": "10.82.43.1"},
154 {"network": "161.26.0.0", "netmask": "255.255.0.0",
155 "gateway": "10.82.43.1"}]},
156 {"id": "network_109887551", "link": "interface_29402279",
157 "type": "ipv4", "ip_address": "108.168.194.252",
158 "netmask": "255.255.255.248",
159 "routes": [
160 {"network": "0.0.0.0", "netmask": "0.0.0.0",
161 "gateway": "108.168.194.249"}]}
162 ],
163 "services": [
164 {"type": "dns", "address": "10.0.80.11"},
165 {"type": "dns", "address": "10.0.80.12"}
166 ],
167 }
168
169 sysuuid = '7f79ebf5-d791-43c3-a723-854e8389d59f'
170
171 def _get_expected_metadata(self, os_md):
172 """return expected 'metadata' for data loaded from meta_data.json."""
173 os_md = copy.deepcopy(os_md)
174 renames = (
175 ('hostname', 'local-hostname'),
176 ('uuid', 'instance-id'),
177 ('public_keys', 'public-keys'))
178 ret = {}
179 for osname, mdname in renames:
180 if osname in os_md:
181 ret[mdname] = os_md[osname]
182 if 'random_seed' in os_md:
183 ret['random_seed'] = base64.b64decode(os_md['random_seed'])
184
185 return ret
186
187 def test_provisioning_md(self, m_platform, m_sysuuid):
188 """Provisioning env with a metadata disk should return None."""
189 m_platform.return_value = (
190 ibm.Platforms.TEMPLATE_PROVISIONING_METADATA, "/dev/xvdh")
191 self.assertIsNone(ibm.read_md())
192
193 def test_provisioning_no_metadata(self, m_platform, m_sysuuid):
194 """Provisioning env with no metadata disk should return None."""
195 m_platform.return_value = (
196 ibm.Platforms.TEMPLATE_PROVISIONING_NODATA, None)
197 self.assertIsNone(ibm.read_md())
198
199 def test_provisioning_not_ibm(self, m_platform, m_sysuuid):
200 """Provisioning env but not identified as IBM should return None."""
201 m_platform.return_value = (None, None)
202 self.assertIsNone(ibm.read_md())
203
204 def test_template_live(self, m_platform, m_sysuuid):
205 """Template live environment should be identified."""
206 tmpdir = self.tmp_dir()
207 m_platform.return_value = (
208 ibm.Platforms.TEMPLATE_LIVE_METADATA, tmpdir)
209 m_sysuuid.return_value = self.sysuuid
210
211 test_helpers.populate_dir(tmpdir, {
212 'openstack/latest/meta_data.json': json.dumps(self.template_md),
213 'openstack/latest/user_data': self.userdata,
214 'openstack/content/interfaces': self.content_interfaces,
215 'meta.js': self.meta_js})
216
217 ret = ibm.read_md()
218 self.assertEqual(ibm.Platforms.TEMPLATE_LIVE_METADATA,
219 ret['platform'])
220 self.assertEqual(tmpdir, ret['source'])
221 self.assertEqual(self.userdata, ret['userdata'])
222 self.assertEqual(self._get_expected_metadata(self.template_md),
223 ret['metadata'])
224 self.assertEqual(self.sysuuid, ret['system-uuid'])
225
226 def test_os_code_live(self, m_platform, m_sysuuid):
227 """Verify an os_code metadata path."""
228 tmpdir = self.tmp_dir()
229 m_platform.return_value = (ibm.Platforms.OS_CODE, tmpdir)
230 netdata = json.dumps(self.network_data)
231 test_helpers.populate_dir(tmpdir, {
232 'openstack/latest/meta_data.json': json.dumps(self.oscode_md),
233 'openstack/latest/user_data': self.userdata,
234 'openstack/latest/vendor_data.json': json.dumps(self.vendor_data),
235 'openstack/latest/network_data.json': netdata,
236 })
237
238 ret = ibm.read_md()
239 self.assertEqual(ibm.Platforms.OS_CODE, ret['platform'])
240 self.assertEqual(tmpdir, ret['source'])
241 self.assertEqual(self.userdata, ret['userdata'])
242 self.assertEqual(self._get_expected_metadata(self.oscode_md),
243 ret['metadata'])
244
245 def test_os_code_live_no_userdata(self, m_platform, m_sysuuid):
246 """Verify os_code without user-data."""
247 tmpdir = self.tmp_dir()
248 m_platform.return_value = (ibm.Platforms.OS_CODE, tmpdir)
249 test_helpers.populate_dir(tmpdir, {
250 'openstack/latest/meta_data.json': json.dumps(self.oscode_md),
251 'openstack/latest/vendor_data.json': json.dumps(self.vendor_data),
252 })
253
254 ret = ibm.read_md()
255 self.assertEqual(ibm.Platforms.OS_CODE, ret['platform'])
256 self.assertEqual(tmpdir, ret['source'])
257 self.assertIsNone(ret['userdata'])
258 self.assertEqual(self._get_expected_metadata(self.oscode_md),
259 ret['metadata'])
260
261
262# vi: ts=4 expandtab
diff --git a/tests/unittests/test_ds_identify.py b/tests/unittests/test_ds_identify.py
index 85999b7..5364398 100644
--- a/tests/unittests/test_ds_identify.py
+++ b/tests/unittests/test_ds_identify.py
@@ -9,6 +9,8 @@ from cloudinit import util
9from cloudinit.tests.helpers import (9from cloudinit.tests.helpers import (
10 CiTestCase, dir2dict, populate_dir)10 CiTestCase, dir2dict, populate_dir)
1111
12from cloudinit.sources import DataSourceIBMCloud as dsibm
13
12UNAME_MYSYS = ("Linux bart 4.4.0-62-generic #83-Ubuntu "14UNAME_MYSYS = ("Linux bart 4.4.0-62-generic #83-Ubuntu "
13 "SMP Wed Jan 18 14:10:15 UTC 2017 x86_64 GNU/Linux")15 "SMP Wed Jan 18 14:10:15 UTC 2017 x86_64 GNU/Linux")
14UNAME_PPC64EL = ("Linux diamond 4.4.0-83-generic #106-Ubuntu SMP "16UNAME_PPC64EL = ("Linux diamond 4.4.0-83-generic #106-Ubuntu SMP "
@@ -37,8 +39,8 @@ BLKID_UEFI_UBUNTU = [
3739
38POLICY_FOUND_ONLY = "search,found=all,maybe=none,notfound=disabled"40POLICY_FOUND_ONLY = "search,found=all,maybe=none,notfound=disabled"
39POLICY_FOUND_OR_MAYBE = "search,found=all,maybe=all,notfound=disabled"41POLICY_FOUND_OR_MAYBE = "search,found=all,maybe=all,notfound=disabled"
40DI_DEFAULT_POLICY = "search,found=all,maybe=all,notfound=enabled"42DI_DEFAULT_POLICY = "search,found=all,maybe=all,notfound=disabled"
41DI_DEFAULT_POLICY_NO_DMI = "search,found=all,maybe=all,notfound=disabled"43DI_DEFAULT_POLICY_NO_DMI = "search,found=all,maybe=all,notfound=enabled"
42DI_EC2_STRICT_ID_DEFAULT = "true"44DI_EC2_STRICT_ID_DEFAULT = "true"
43OVF_MATCH_STRING = 'http://schemas.dmtf.org/ovf/environment/1'45OVF_MATCH_STRING = 'http://schemas.dmtf.org/ovf/environment/1'
4446
@@ -64,6 +66,9 @@ P_SYS_VENDOR = "sys/class/dmi/id/sys_vendor"
64P_SEED_DIR = "var/lib/cloud/seed"66P_SEED_DIR = "var/lib/cloud/seed"
65P_DSID_CFG = "etc/cloud/ds-identify.cfg"67P_DSID_CFG = "etc/cloud/ds-identify.cfg"
6668
69IBM_PROVISIONING_CHECK_PATH = "/root/provisioningConfiguration.cfg"
70IBM_CONFIG_UUID = "9796-932E"
71
67MOCK_VIRT_IS_KVM = {'name': 'detect_virt', 'RET': 'kvm', 'ret': 0}72MOCK_VIRT_IS_KVM = {'name': 'detect_virt', 'RET': 'kvm', 'ret': 0}
68MOCK_VIRT_IS_VMWARE = {'name': 'detect_virt', 'RET': 'vmware', 'ret': 0}73MOCK_VIRT_IS_VMWARE = {'name': 'detect_virt', 'RET': 'vmware', 'ret': 0}
69MOCK_VIRT_IS_XEN = {'name': 'detect_virt', 'RET': 'xen', 'ret': 0}74MOCK_VIRT_IS_XEN = {'name': 'detect_virt', 'RET': 'xen', 'ret': 0}
@@ -239,6 +244,57 @@ class TestDsIdentify(CiTestCase):
239 self._test_ds_found('ConfigDriveUpper')244 self._test_ds_found('ConfigDriveUpper')
240 return245 return
241246
247 def test_ibmcloud_template_userdata_in_provisioning(self):
248 """Template provisioned with user-data during provisioning stage.
249
250 Template provisioning with user-data has METADATA disk,
251 datasource should return not found."""
252 data = copy.deepcopy(VALID_CFG['IBMCloud-metadata'])
253 data['files'] = {IBM_PROVISIONING_CHECK_PATH: 'xxx'}
254 return self._check_via_dict(data, RC_NOT_FOUND)
255
256 def test_ibmcloud_template_userdata(self):
257 """Template provisioned with user-data first boot.
258
259 Template provisioning with user-data has METADATA disk.
260 datasource should return found."""
261 self._test_ds_found('IBMCloud-metadata')
262
263 def test_ibmcloud_template_no_userdata_in_provisioning(self):
264 """Template provisioned with no user-data during provisioning.
265
266 no disks attached. Datasource should return not found."""
267 data = copy.deepcopy(VALID_CFG['IBMCloud-nodisks'])
268 data['files'] = {IBM_PROVISIONING_CHECK_PATH: 'xxx'}
269 return self._check_via_dict(data, RC_NOT_FOUND)
270
271 def test_ibmcloud_template_no_userdata(self):
272 """Template provisioned with no user-data first boot.
273
274 no disks attached. Datasource should return found."""
275 self._check_via_dict(VALID_CFG['IBMCloud-nodisks'], RC_NOT_FOUND)
276
277 def test_ibmcloud_os_code(self):
278 """Launched by os code always has config-2 disk."""
279 self._test_ds_found('IBMCloud-config-2')
280
281 def test_ibmcloud_os_code_different_uuid(self):
282 """IBM cloud config-2 disks must be explicit match on UUID.
283
284 If the UUID is not 9796-932E then we actually expect ConfigDrive."""
285 data = copy.deepcopy(VALID_CFG['IBMCloud-config-2'])
286 offset = None
287 for m, d in enumerate(data['mocks']):
288 if d.get('name') == "blkid":
289 offset = m
290 break
291 if not offset:
292 raise ValueError("Expected to find 'blkid' mock, but did not.")
293 data['mocks'][offset]['out'] = d['out'].replace(dsibm.IBM_CONFIG_UUID,
294 "DEAD-BEEF")
295 self._check_via_dict(
296 data, rc=RC_FOUND, dslist=['ConfigDrive', DS_NONE])
297
242 def test_policy_disabled(self):298 def test_policy_disabled(self):
243 """A Builtin policy of 'disabled' should return not found.299 """A Builtin policy of 'disabled' should return not found.
244300
@@ -452,7 +508,7 @@ VALID_CFG = {
452 },508 },
453 'Ec2-xen': {509 'Ec2-xen': {
454 'ds': 'Ec2',510 'ds': 'Ec2',
455 'mocks': [{'name': 'detect_virt', 'RET': 'xen', 'ret': 0}],511 'mocks': [MOCK_VIRT_IS_XEN],
456 'files': {512 'files': {
457 'sys/hypervisor/uuid': 'ec2c6e2f-5fac-4fc7-9c82-74127ec14bbb\n'513 'sys/hypervisor/uuid': 'ec2c6e2f-5fac-4fc7-9c82-74127ec14bbb\n'
458 },514 },
@@ -579,6 +635,48 @@ VALID_CFG = {
579 'ds': 'Hetzner',635 'ds': 'Hetzner',
580 'files': {P_SYS_VENDOR: 'Hetzner\n'},636 'files': {P_SYS_VENDOR: 'Hetzner\n'},
581 },637 },
638 'IBMCloud-metadata': {
639 'ds': 'IBMCloud',
640 'mocks': [
641 MOCK_VIRT_IS_XEN,
642 {'name': 'blkid', 'ret': 0,
643 'out': blkid_out(
644 [{'DEVNAME': 'xvda1', 'TYPE': 'vfat', 'PARTUUID': uuid4()},
645 {'DEVNAME': 'xvda2', 'TYPE': 'ext4',
646 'LABEL': 'cloudimg-rootfs', 'PARTUUID': uuid4()},
647 {'DEVNAME': 'xvdb', 'TYPE': 'vfat', 'LABEL': 'METADATA'}]),
648 },
649 ],
650 },
651 'IBMCloud-config-2': {
652 'ds': 'IBMCloud',
653 'mocks': [
654 MOCK_VIRT_IS_XEN,
655 {'name': 'blkid', 'ret': 0,
656 'out': blkid_out(
657 [{'DEVNAME': 'xvda1', 'TYPE': 'ext3', 'PARTUUID': uuid4(),
658 'UUID': uuid4(), 'LABEL': 'cloudimg-bootfs'},
659 {'DEVNAME': 'xvdb', 'TYPE': 'vfat', 'LABEL': 'config-2',
660 'UUID': dsibm.IBM_CONFIG_UUID},
661 {'DEVNAME': 'xvda2', 'TYPE': 'ext4',
662 'LABEL': 'cloudimg-rootfs', 'PARTUUID': uuid4(),
663 'UUID': uuid4()},
664 ]),
665 },
666 ],
667 },
668 'IBMCloud-nodisks': {
669 'ds': 'IBMCloud',
670 'mocks': [
671 MOCK_VIRT_IS_XEN,
672 {'name': 'blkid', 'ret': 0,
673 'out': blkid_out(
674 [{'DEVNAME': 'xvda1', 'TYPE': 'vfat', 'PARTUUID': uuid4()},
675 {'DEVNAME': 'xvda2', 'TYPE': 'ext4',
676 'LABEL': 'cloudimg-rootfs', 'PARTUUID': uuid4()}]),
677 },
678 ],
679 },
582}680}
583681
584# vi: ts=4 expandtab682# vi: ts=4 expandtab
diff --git a/tools/ds-identify b/tools/ds-identify
index e2552c8..9a2db5c 100755
--- a/tools/ds-identify
+++ b/tools/ds-identify
@@ -92,6 +92,7 @@ DI_DMI_SYS_VENDOR=""
92DI_DMI_PRODUCT_SERIAL=""92DI_DMI_PRODUCT_SERIAL=""
93DI_DMI_PRODUCT_UUID=""93DI_DMI_PRODUCT_UUID=""
94DI_FS_LABELS=""94DI_FS_LABELS=""
95DI_FS_UUIDS=""
95DI_ISO9660_DEVS=""96DI_ISO9660_DEVS=""
96DI_KERNEL_CMDLINE=""97DI_KERNEL_CMDLINE=""
97DI_VIRT=""98DI_VIRT=""
@@ -114,7 +115,7 @@ DI_DSNAME=""
114# be searched if there is no setting found in config.115# be searched if there is no setting found in config.
115DI_DSLIST_DEFAULT="MAAS ConfigDrive NoCloud AltCloud Azure Bigstep \116DI_DSLIST_DEFAULT="MAAS ConfigDrive NoCloud AltCloud Azure Bigstep \
116CloudSigma CloudStack DigitalOcean AliYun Ec2 GCE OpenNebula OpenStack \117CloudSigma CloudStack DigitalOcean AliYun Ec2 GCE OpenNebula OpenStack \
117OVF SmartOS Scaleway Hetzner"118OVF SmartOS Scaleway Hetzner IBMCloud"
118DI_DSLIST=""119DI_DSLIST=""
119DI_MODE=""120DI_MODE=""
120DI_ON_FOUND=""121DI_ON_FOUND=""
@@ -123,6 +124,8 @@ DI_ON_NOTFOUND=""
123124
124DI_EC2_STRICT_ID_DEFAULT="true"125DI_EC2_STRICT_ID_DEFAULT="true"
125126
127_IS_IBM_CLOUD=""
128
126error() {129error() {
127 set -- "ERROR:" "$@";130 set -- "ERROR:" "$@";
128 debug 0 "$@"131 debug 0 "$@"
@@ -196,7 +199,7 @@ read_fs_info() {
196 return199 return
197 fi200 fi
198 local oifs="$IFS" line="" delim=","201 local oifs="$IFS" line="" delim=","
199 local ret=0 out="" labels="" dev="" label="" ftype="" isodevs=""202 local ret=0 out="" labels="" dev="" label="" ftype="" isodevs="" uuids=""
200 out=$(blkid -c /dev/null -o export) || {203 out=$(blkid -c /dev/null -o export) || {
201 ret=$?204 ret=$?
202 error "failed running [$ret]: blkid -c /dev/null -o export"205 error "failed running [$ret]: blkid -c /dev/null -o export"
@@ -219,12 +222,14 @@ read_fs_info() {
219 LABEL=*) label="${line#LABEL=}";222 LABEL=*) label="${line#LABEL=}";
220 labels="${labels}${line#LABEL=}${delim}";;223 labels="${labels}${line#LABEL=}${delim}";;
221 TYPE=*) ftype=${line#TYPE=};;224 TYPE=*) ftype=${line#TYPE=};;
225 UUID=*) uuids="${uuids}${line#UUID=}$delim";;
222 esac226 esac
223 done227 done
224 [ -n "$dev" -a "$ftype" = "iso9660" ] &&228 [ -n "$dev" -a "$ftype" = "iso9660" ] &&
225 isodevs="${isodevs} ${dev}=$label"229 isodevs="${isodevs} ${dev}=$label"
226230
227 DI_FS_LABELS="${labels%${delim}}"231 DI_FS_LABELS="${labels%${delim}}"
232 DI_FS_UUIDS="${uuids%${delim}}"
228 DI_ISO9660_DEVS="${isodevs# }"233 DI_ISO9660_DEVS="${isodevs# }"
229}234}
230235
@@ -437,14 +442,25 @@ dmi_sys_vendor_is() {
437 [ "${DI_DMI_SYS_VENDOR}" = "$1" ]442 [ "${DI_DMI_SYS_VENDOR}" = "$1" ]
438}443}
439444
440has_fs_with_label() {445has_fs_with_uuid() {
441 local label="$1"446 case ",${DI_FS_UUIDS}," in
442 case ",${DI_FS_LABELS}," in447 *,$1,*) return 0;;
443 *,$label,*) return 0;;
444 esac448 esac
445 return 1449 return 1
446}450}
447451
452has_fs_with_label() {
453 # has_fs_with_label(label1[ ,label2 ..])
454 # return 0 if a there is a filesystem that matches any of the labels.
455 local label=""
456 for label in "$@"; do
457 case ",${DI_FS_LABELS}," in
458 *,$label,*) return 0;;
459 esac
460 done
461 return 1
462}
463
448nocase_equal() {464nocase_equal() {
449 # nocase_equal(a, b)465 # nocase_equal(a, b)
450 # return 0 if case insenstive comparision a.lower() == b.lower()466 # return 0 if case insenstive comparision a.lower() == b.lower()
@@ -583,6 +599,8 @@ dscheck_NoCloud() {
583 case " ${DI_DMI_PRODUCT_SERIAL} " in599 case " ${DI_DMI_PRODUCT_SERIAL} " in
584 *\ ds=nocloud*) return ${DS_FOUND};;600 *\ ds=nocloud*) return ${DS_FOUND};;
585 esac601 esac
602
603 is_ibm_cloud && return ${DS_NOT_FOUND}
586 for d in nocloud nocloud-net; do604 for d in nocloud nocloud-net; do
587 check_seed_dir "$d" meta-data user-data && return ${DS_FOUND}605 check_seed_dir "$d" meta-data user-data && return ${DS_FOUND}
588 check_writable_seed_dir "$d" meta-data user-data && return ${DS_FOUND}606 check_writable_seed_dir "$d" meta-data user-data && return ${DS_FOUND}
@@ -594,9 +612,8 @@ dscheck_NoCloud() {
594}612}
595613
596check_configdrive_v2() {614check_configdrive_v2() {
597 if has_fs_with_label "config-2"; then615 is_ibm_cloud && return ${DS_NOT_FOUND}
598 return ${DS_FOUND}616 if has_fs_with_label CONFIG-2 config-2; then
599 elif has_fs_with_label "CONFIG-2"; then
600 return ${DS_FOUND}617 return ${DS_FOUND}
601 fi618 fi
602 # look in /config-drive <vlc>/seed/config_drive for a directory619 # look in /config-drive <vlc>/seed/config_drive for a directory
@@ -988,6 +1005,36 @@ dscheck_Hetzner() {
988 return ${DS_NOT_FOUND}1005 return ${DS_NOT_FOUND}
989}1006}
9901007
1008is_ibm_provisioning() {
1009 [ -f "${PATH_ROOT}/root/provisioningConfiguration.cfg" ]
1010}
1011
1012is_ibm_cloud() {
1013 cached "${_IS_IBM_CLOUD}" && return ${_IS_IBM_CLOUD}
1014 local ret=1
1015 if [ "$DI_VIRT" = "xen" ]; then
1016 if is_ibm_provisioning; then
1017 ret=0
1018 elif has_fs_with_label METADATA metadata; then
1019 ret=0
1020 elif has_fs_with_uuid 9796-932E &&
1021 has_fs_with_label CONFIG-2 config-2; then
1022 ret=0
1023 fi
1024 fi
1025 _IS_IBM_CLOUD=$ret
1026 return $ret
1027}
1028
1029dscheck_IBMCloud() {
1030 if is_ibm_provisioning; then
1031 debug 1 "cloud-init disabled during provisioning on IBMCloud"
1032 return ${DS_NOT_FOUND}
1033 fi
1034 is_ibm_cloud && return ${DS_FOUND}
1035 return ${DS_NOT_FOUND}
1036}
1037
991collect_info() {1038collect_info() {
992 read_virt1039 read_virt
993 read_pid1_product_name1040 read_pid1_product_name

Subscribers

People subscribed via source and target branches