Merge ~smoser/cloud-init:feature/datasource-ibmcloud into cloud-init:master

Proposed by Scott Moser
Status: Rejected
Rejected by: Chad Smith
Proposed branch: ~smoser/cloud-init:feature/datasource-ibmcloud
Merge into: cloud-init:master
Diff against target: 1044 lines (+838/-14)
8 files modified
cloudinit/sources/DataSourceConfigDrive.py (+10/-0)
cloudinit/sources/DataSourceIBMCloud.py (+311/-0)
cloudinit/sources/helpers/openstack.py (+2/-2)
cloudinit/tests/test_util.py (+72/-0)
cloudinit/util.py (+24/-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
Chad Smith Approve
Server Team CI bot continuous-integration Approve
Review via email: mp+341774@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

TODO:
* we could further limit the search for ibmcloud datasource to devices
  that have a LABEL=SWAP-xvdb1.
  /dev/xvdb1: LABEL="SWAP-xvdb1" UUID="d51fcca0-6b10-4934-a572-f3898dfd8840" TYPE="swap" PARTUUID="00025cdb-01"

Here is some information collected from 16.04 images I've launched.
OS_CODE (user-data) http://paste.ubuntu.com/p/BHsczrj8qq/
TEMPLATE_PROVISIONING_METADATA : http://paste.ubuntu.com/p/tQgw9mzpjX/
TEMPLATE_PROVISIONING_LIVE : http://paste.ubuntu.com/p/Wr4VHd6Wrt/

Here are some hacky scripts in a gist that i put together while doing this:
https://gist.github.com/smoser/94195d335b5af32efe1be056e413e130

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

FAILED: Continuous integration, rev:1ebcb6a85ca6865f1aa6657ce32e10b5436702a4
https://jenkins.ubuntu.com/server/job/cloud-init-ci/893/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

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

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

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

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

FAILED: Continuous integration, rev:466ad1cda9392ccdc4813641c34d40e82589a675
https://jenkins.ubuntu.com/server/job/cloud-init-ci/899/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

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

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

FAILED: Continuous integration, rev:56f4558535f46b66356c181361408ebbd8b8f710
https://jenkins.ubuntu.com/server/job/cloud-init-ci/900/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

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

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

FAILED: Continuous integration, rev:b046fdac982523ee76e1e02036f49f390cf82742
https://jenkins.ubuntu.com/server/job/cloud-init-ci/901/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

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

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

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

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

some inline comments, questions.

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

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

review: Approve (continuous-integration)
Revision history for this message
Scott Moser (smoser) wrote :

addressed feedbacks

Revision history for this message
Scott Moser (smoser) wrote :

addressed feedbacks.

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

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

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

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

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

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

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

Minor nits and suggestions, looks great. Tested ds-id & datasource against openstack cloud making sure we didn't get false positives etc.

review: Approve
Revision history for this message
Scott Moser (smoser) wrote :

I'll get to your suggestions later.
thanks for the review.

Revision history for this message
Chad Smith (chad.smith) wrote :

There was an error fetching revisions from git servers. Please try again in a few minutes. If the problem persists, contact Launchpad support.

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..e687539
--- /dev/null
+++ b/cloudinit/sources/DataSourceIBMCloud.py
@@ -0,0 +1,311 @@
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 def opath(fname):
235 return os.path.join("openstack", "latest", fname)
236
237 def load_json_bytes(blob):
238 return json.loads(blob.decode('utf-8'))
239
240 files = [
241 # tuples of (results_name, path, translator)
242 ('metadata_raw', opath('meta_data.json'), load_json_bytes),
243 ('userdata', opath('user_data'), None),
244 ('vendordata', opath('vendor_data.json'), load_json_bytes),
245 ('networkdata', opath('network_data.json'), load_json_bytes),
246 ]
247
248 results = {}
249 for (name, path, transl) in files:
250 fpath = os.path.join(source_dir, path)
251 raw = None
252 try:
253 raw = util.load_file(fpath, decode=False)
254 except IOError as e:
255 LOG.debug("Failed reading path '%s': %s", fpath, e)
256
257 if raw is None or transl is None:
258 data = raw
259 else:
260 try:
261 data = transl(raw)
262 except Exception as e:
263 raise BrokenMetadata("Failed decoding %s: %s" % (path, e))
264
265 results[name] = data
266
267 if results.get('metadata_raw') is None:
268 raise BrokenMetadata(
269 "%s missing required file 'meta_data.json'" % source_dir)
270
271 results['metadata'] = {}
272
273 md_raw = results['metadata_raw']
274 md = results['metadata']
275 if 'random_seed' in md_raw:
276 try:
277 md['random_seed'] = base64.b64decode(md_raw['random_seed'])
278 except (ValueError, TypeError) as e:
279 raise BrokenMetadata(
280 "Badly formatted metadata random_seed entry: %s" % e)
281
282 renames = (
283 ('public_keys', 'public-keys'), ('hostname', 'local-hostname'),
284 ('uuid', 'instance-id'))
285 for mdname, newname in renames:
286 if mdname in md_raw:
287 md[newname] = md_raw[mdname]
288
289 return results
290
291
292# Used to match classes to dependencies
293datasources = [
294 (DataSourceIBMCloud, (sources.DEP_FILESYSTEM,)),
295]
296
297
298# Return a list of data sources that match this set of dependencies
299def get_datasource_list(depends):
300 return sources.list_from_depends(depends, datasources)
301
302
303if __name__ == "__main__":
304 import argparse
305
306 parser = argparse.ArgumentParser(description='Query IBM Cloud Metadata')
307 args = parser.parse_args()
308 data = read_md()
309 print(util.json_dumps(data))
310
311# vi: ts=4 expandtab
diff --git a/cloudinit/sources/helpers/openstack.py b/cloudinit/sources/helpers/openstack.py
index 26f3168..82821a2 100644
--- a/cloudinit/sources/helpers/openstack.py
+++ b/cloudinit/sources/helpers/openstack.py
@@ -329,9 +329,9 @@ class BaseReader(object):
329329
330330
331class ConfigDriveReader(BaseReader):331class ConfigDriveReader(BaseReader):
332 def __init__(self, base_path):332 def __init__(self, base_path, versions=None):
333 super(ConfigDriveReader, self).__init__(base_path)333 super(ConfigDriveReader, self).__init__(base_path)
334 self._versions = None334 self._versions = versions
335335
336 def _path_join(self, base, *add_ons):336 def _path_join(self, base, *add_ons):
337 components = [base] + list(add_ons)337 components = [base] + list(add_ons)
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..7ae62cc 100644
--- a/cloudinit/util.py
+++ b/cloudinit/util.py
@@ -1237,6 +1237,30 @@ def find_devs_with(criteria=None, oformat='device',
1237 return entries1237 return entries
12381238
12391239
1240def blkid(devs=None, disable_cache=False):
1241 if devs is None:
1242 devs = []
1243 else:
1244 devs = list(devs)
1245
1246 cmd = ['blkid', '-o', 'full']
1247 if disable_cache:
1248 cmd.extend(['-c', '/dev/null'])
1249 cmd.extend(devs)
1250
1251 # we have to decode with 'replace' as shelx.split (called by
1252 # load_shell_content) can't take bytes. So this is potentially
1253 # lossy of non-utf-8 chars in blkid output.
1254 out, _ = subp(cmd, capture=True, decode="replace")
1255 ret = {}
1256 for line in out.splitlines():
1257 dev, _, data = line.partition(":")
1258 ret[dev] = load_shell_content(data)
1259 ret[dev]["DEVNAME"] = dev
1260
1261 return ret
1262
1263
1240def peek_file(fname, max_bytes):1264def peek_file(fname, max_bytes):
1241 LOG.debug("Peeking at %s (max_bytes=%s)", fname, max_bytes)1265 LOG.debug("Peeking at %s (max_bytes=%s)", fname, max_bytes)
1242 with open(fname, 'rb') as ifh:1266 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 1006441267new file mode 100644
index 0000000..894bfb5
--- /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 fro 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