From 0f8695323262e41c699588c7cd140f6b58c62017 Mon Sep 17 00:00:00 2001
From: Gonéri Le Bouder <goneri@lebouder.net>
Date: Fri, 24 May 2019 21:39:19 +0000
Subject: freebsd: NoCloud data source support

blkid is a Linux-only command. With this patch, cloud-init uses another
approach to find the data source on FreeBSD.

LP: #1645824
---
 cloudinit/sources/DataSourceNoCloud.py          | 40 ++++++++++++++-----------
 config/cloud.cfg.tmpl                           |  4 +--
 tests/unittests/test_datasource/test_nocloud.py | 18 +++++++++++
 3 files changed, 43 insertions(+), 19 deletions(-)

diff --git a/cloudinit/sources/DataSourceNoCloud.py b/cloudinit/sources/DataSourceNoCloud.py
index fcf5d589..8a9e5dd2 100644
--- a/cloudinit/sources/DataSourceNoCloud.py
+++ b/cloudinit/sources/DataSourceNoCloud.py
@@ -35,6 +35,26 @@ class DataSourceNoCloud(sources.DataSource):
         root = sources.DataSource.__str__(self)
         return "%s [seed=%s][dsmode=%s]" % (root, self.seed, self.dsmode)
 
+    def _get_devices(self, label):
+        if util.is_FreeBSD():
+            devlist = [
+                p for p in ['/dev/msdosfs/' + label, '/dev/iso9660/' + label]
+                if os.path.exists(p)]
+        else:
+            # Query optical drive to get it in blkid cache for 2.6 kernels
+            util.find_devs_with(path="/dev/sr0")
+            util.find_devs_with(path="/dev/sr1")
+
+            fslist = util.find_devs_with("TYPE=vfat")
+            fslist.extend(util.find_devs_with("TYPE=iso9660"))
+
+            label_list = util.find_devs_with("LABEL=%s" % label.upper())
+            label_list.extend(util.find_devs_with("LABEL=%s" % label.lower()))
+
+            devlist = list(set(fslist) & set(label_list))
+            devlist.sort(reverse=True)
+        return devlist
+
     def _get_data(self):
         defaults = {
             "instance-id": "nocloud",
@@ -99,20 +119,7 @@ class DataSourceNoCloud(sources.DataSource):
 
         label = self.ds_cfg.get('fs_label', "cidata")
         if label is not None:
-            # Query optical drive to get it in blkid cache for 2.6 kernels
-            util.find_devs_with(path="/dev/sr0")
-            util.find_devs_with(path="/dev/sr1")
-
-            fslist = util.find_devs_with("TYPE=vfat")
-            fslist.extend(util.find_devs_with("TYPE=iso9660"))
-
-            label_list = util.find_devs_with("LABEL=%s" % label.upper())
-            label_list.extend(util.find_devs_with("LABEL=%s" % label.lower()))
-
-            devlist = list(set(fslist) & set(label_list))
-            devlist.sort(reverse=True)
-
-            for dev in devlist:
+            for dev in self._get_devices(label):
                 try:
                     LOG.debug("Attempting to use data from %s", dev)
 
@@ -120,9 +127,8 @@ class DataSourceNoCloud(sources.DataSource):
                         seeded = util.mount_cb(dev, _pp2d_callback,
                                                pp2d_kwargs)
                     except ValueError:
-                        if dev in label_list:
-                            LOG.warning("device %s with label=%s not a"
-                                        "valid seed.", dev, label)
+                        LOG.warning("device %s with label=%s not a"
+                                    "valid seed.", dev, label)
                         continue
 
                     mydata = _merge_new_seed(mydata, seeded)
diff --git a/config/cloud.cfg.tmpl b/config/cloud.cfg.tmpl
index 25db43e0..684c7473 100644
--- a/config/cloud.cfg.tmpl
+++ b/config/cloud.cfg.tmpl
@@ -32,8 +32,8 @@ preserve_hostname: false
 
 {% if variant in ["freebsd"] %}
 # This should not be required, but leave it in place until the real cause of
-# not beeing able to find -any- datasources is resolved.
-datasource_list: ['ConfigDrive', 'Azure', 'OpenStack', 'Ec2']
+# not finding -any- datasources is resolved.
+datasource_list: ['NoCloud', 'ConfigDrive', 'Azure', 'OpenStack', 'Ec2']
 {% endif %}
 # Example datasource config
 # datasource:
diff --git a/tests/unittests/test_datasource/test_nocloud.py b/tests/unittests/test_datasource/test_nocloud.py
index b785362f..18bea0b9 100644
--- a/tests/unittests/test_datasource/test_nocloud.py
+++ b/tests/unittests/test_datasource/test_nocloud.py
@@ -278,6 +278,24 @@ class TestNoCloudDataSource(CiTestCase):
         self.assertEqual(netconf, dsrc.network_config)
         self.assertNotIn(gateway, str(dsrc.network_config))
 
+    @mock.patch("cloudinit.util.blkid")
+    def test_nocloud_get_devices_freebsd(self, m_is_lxd, fake_blkid):
+        populate_dir(os.path.join(self.paths.seed_dir, "nocloud"),
+                     {'user-data': b"ud", 'meta-data': "instance-id: IID\n"})
+
+        sys_cfg = {'datasource': {'NoCloud': {'fs_label': None}}}
+
+        self.mocks.enter_context(
+            mock.patch.object(util, 'is_FreeBSD', return_value=True))
+
+        self.mocks.enter_context(
+            mock.patch.object(os.path, 'exists', return_value=True))
+
+        dsrc = dsNoCloud(sys_cfg=sys_cfg, distro=None, paths=self.paths)
+        ret = dsrc._get_devices('foo')
+        self.assertEqual(['/dev/msdosfs/foo', '/dev/iso9660/foo'], ret)
+        fake_blkid.assert_not_called()
+
 
 class TestParseCommandLineData(CiTestCase):
 
-- 
cgit v1.2.3