← Back to team overview

cloud-init-dev team mailing list archive

[Merge] ~xnox/cloud-init:networkd into cloud-init:master

 

Dimitri John Ledkov has proposed merging ~xnox/cloud-init:networkd into cloud-init:master.

Commit message:
azure: discover datasource from networkd lease files

LP: #1718029

Requested reviews:
  cloud-init commiters (cloud-init-dev)
Related bugs:
  Bug #1718029 in cloud-init (Ubuntu): "cloudstack and azure datasources broken when using netplan/systemd-networkd"
  https://bugs.launchpad.net/ubuntu/+source/cloud-init/+bug/1718029

For more details, see:
https://code.launchpad.net/~xnox/cloud-init/+git/cloud-init/+merge/331642

azure: discover datasource from networkd lease files

LP: #1718029
-- 
Your team cloud-init commiters is requested to review the proposed merge of ~xnox/cloud-init:networkd into cloud-init:master.
diff --git a/cloudinit/sources/helpers/azure.py b/cloudinit/sources/helpers/azure.py
index 28ed0ae..f4028a7 100644
--- a/cloudinit/sources/helpers/azure.py
+++ b/cloudinit/sources/helpers/azure.py
@@ -1,5 +1,6 @@
 # This file is part of cloud-init. See LICENSE file for license information.
 
+import glob
 import json
 import logging
 import os
@@ -15,6 +16,8 @@ from xml.etree import ElementTree
 
 from cloudinit import util
 
+from six.moves.configparser import ConfigParser
+from six.moves import cStringIO as StringIO
 
 LOG = logging.getLogger(__name__)
 
@@ -239,6 +242,20 @@ class WALinuxAgentShim(object):
         return socket.inet_ntoa(packed_bytes)
 
     @staticmethod
+    def _get_value_from_networkd_leases(_leases='/run/systemd/netif/leases/*'):
+        for lease in glob.iglob(_leases):
+            cp = ConfigParser()
+            try:
+                if hasattr(cp, 'read_string'):
+                    cp.read_string('[LEASE]\n' + util.load_file(lease))
+                else:
+                    cp.readfp(StringIO('[LEASE]\n' + util.load_file(lease))) # noqa pylint: disable=deprecated-method
+                return cp.get('LEASE', 'OPTION_245')
+            except Exception:
+                pass
+        return None
+
+    @staticmethod
     def _get_value_from_leases_file(fallback_lease_file):
         leases = []
         content = util.load_file(fallback_lease_file)
@@ -287,12 +304,15 @@ class WALinuxAgentShim(object):
 
     @staticmethod
     def find_endpoint(fallback_lease_file=None):
-        LOG.debug('Finding Azure endpoint...')
         value = None
-        # Option-245 stored in /run/cloud-init/dhclient.hooks/<ifc>.json
-        # a dhclient exit hook that calls cloud-init-dhclient-hook
-        dhcp_options = WALinuxAgentShim._load_dhclient_json()
-        value = WALinuxAgentShim._get_value_from_dhcpoptions(dhcp_options)
+        LOG.debug('Finding Azure endpoint from networkd...')
+        value = WALinuxAgentShim._get_value_from_networkd_leases()
+        if value is None:
+            # Option-245 stored in /run/cloud-init/dhclient.hooks/<ifc>.json
+            # a dhclient exit hook that calls cloud-init-dhclient-hook
+            LOG.debug('Finding Azure endpoint from hook json...')
+            dhcp_options = WALinuxAgentShim._load_dhclient_json()
+            value = WALinuxAgentShim._get_value_from_dhcpoptions(dhcp_options)
         if value is None:
             # Fallback and check the leases file if unsuccessful
             LOG.debug("Unable to find endpoint in dhclient logs. "
diff --git a/tests/unittests/test_datasource/test_azure_helper.py b/tests/unittests/test_datasource/test_azure_helper.py
index 44b99ec..10cd7f4 100644
--- a/tests/unittests/test_datasource/test_azure_helper.py
+++ b/tests/unittests/test_datasource/test_azure_helper.py
@@ -1,6 +1,8 @@
 # This file is part of cloud-init. See LICENSE file for license information.
 
 import os
+import shutil
+import tempfile
 
 from cloudinit.sources.helpers import azure as azure_helper
 from cloudinit.tests.helpers import ExitStack, mock, TestCase
@@ -135,6 +137,45 @@ class TestExtractIpAddressFromLeaseValue(TestCase):
             ))
 
 
+class TestExtractIpAddressFromNetworkd(TestCase):
+
+    def setUp(self):
+        super(TestExtractIpAddressFromNetworkd, self).setUp()
+        lease_dir = tempfile.mkdtemp()
+        self.leases = lease_dir + '/*'
+        ifindex = '2'
+        self.lease_file = os.path.join(lease_dir, ifindex)
+        self.addCleanup(shutil.rmtree, lease_dir, ignore_errors=True)
+
+    def test_none(self):
+        value = None
+        self.assertEqual(
+            value,
+            azure_helper.WALinuxAgentShim._get_value_from_networkd_leases(
+                self.leases
+            ))
+
+    def test_typical(self):
+        value = '624c3620'
+        with open(self.lease_file, 'w') as f:
+            f.write('OPTION_245=%s\n' % value)
+        self.assertEqual(
+            value,
+            azure_helper.WALinuxAgentShim._get_value_from_networkd_leases(
+                self.leases
+            ))
+
+    def test_malformed(self):
+        value = None
+        with open(self.lease_file, 'w') as f:
+            f.write('OPTION_246=624c3620\n')
+        self.assertEqual(
+            value,
+            azure_helper.WALinuxAgentShim._get_value_from_networkd_leases(
+                self.leases
+            ))
+
+
 class TestGoalStateParsing(TestCase):
 
     default_parameters = {

References