sailesh1993 / rpms / cloud-init

Forked from rpms/cloud-init a year ago
Clone
18322d
From ff70ab92aa97b3d43dbde34ef9f70b9ee44e197e Mon Sep 17 00:00:00 2001
18322d
From: Eduardo Otubo <otubo@redhat.com>
18322d
Date: Wed, 19 Feb 2020 10:30:02 +0100
18322d
Subject: [PATCH 1/3] azure: avoid re-running cloud-init when instance-id is
18322d
 byte-swapped (#84)
18322d
18322d
RH-Author: Eduardo Otubo <otubo@redhat.com>
18322d
Message-id: <20200219103002.24735-1-otubo@redhat.com>
18322d
Patchwork-id: 93933
18322d
O-Subject: [RHEL-8.2.0/RHEL-7.9 cloud-init PATCH] azure: avoid re-running cloud-init when instance-id is byte-swapped (#84)
18322d
Bugzilla: 1788684
18322d
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
18322d
RH-Acked-by: Mohammed Gamal <mgamal@redhat.com>
18322d
18322d
BZ: 1788684
18322d
BRANCH: rhel820/master-18.5
18322d
UPSTREAM: 129b1c4e
18322d
BREW: 26606326
18322d
18322d
BZ: 1788340
18322d
BRANCH: rhel7/master-18.5
18322d
UPSTREAM: 129b1c4e
18322d
BREW: 26606322
18322d
18322d
commit 129b1c4ea250619bd7caed7aaffacc796b0139f2
18322d
Author: AOhassan <37305877+AOhassan@users.noreply.github.com>
18322d
Date:   Thu Dec 12 13:51:42 2019 -0800
18322d
18322d
    azure: avoid re-running cloud-init when instance-id is byte-swapped (#84)
18322d
18322d
    Azure stores the instance ID with an incorrect byte ordering for the
18322d
    first three hyphen delimited parts. This results in invalid
18322d
    is_new_instance checks forcing Azure datasource to recrawl the metadata
18322d
    service.
18322d
18322d
    When persisting instance-id from the metadata service, swap the
18322d
    instance-id string byte order such that it is consistent with
18322d
    that returned by dmi information. Check whether the instance-id
18322d
    string is a byte-swapped match when determining correctly whether
18322d
    the Azure platform instance-id has actually changed.
18322d
18322d
Signed-off-by: Eduardo Otubo <otubo@redhat.com>
18322d
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
18322d
---
18322d
 cloudinit/sources/DataSourceAzure.py               | 16 ++++++++++---
18322d
 cloudinit/sources/helpers/azure.py                 | 27 ++++++++++++++++++++++
18322d
 tests/unittests/test_datasource/test_azure.py      | 24 ++++++++++++++++---
18322d
 .../unittests/test_datasource/test_azure_helper.py | 19 +++++++++++++++
18322d
 4 files changed, 80 insertions(+), 6 deletions(-)
18322d
18322d
diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py
18322d
index 5baf8da..66bbe5e 100755
18322d
--- a/cloudinit/sources/DataSourceAzure.py
18322d
+++ b/cloudinit/sources/DataSourceAzure.py
18322d
@@ -28,7 +28,8 @@ from cloudinit.reporting import events
18322d
 
18322d
 from cloudinit.sources.helpers.azure import (azure_ds_reporter,
18322d
                                              azure_ds_telemetry_reporter,
18322d
-                                             get_metadata_from_fabric)
18322d
+                                             get_metadata_from_fabric,
18322d
+                                             is_byte_swapped)
18322d
 
18322d
 LOG = logging.getLogger(__name__)
18322d
 
18322d
@@ -458,8 +459,7 @@ class DataSourceAzure(sources.DataSource):
18322d
         seed = _get_random_seed()
18322d
         if seed:
18322d
             crawled_data['metadata']['random_seed'] = seed
18322d
-        crawled_data['metadata']['instance-id'] = util.read_dmi_data(
18322d
-            'system-uuid')
18322d
+        crawled_data['metadata']['instance-id'] = self._iid()
18322d
 
18322d
         if perform_reprovision:
18322d
             LOG.info("Reporting ready to Azure after getting ReprovisionData")
18322d
@@ -530,6 +530,16 @@ class DataSourceAzure(sources.DataSource):
18322d
         # quickly (local check only) if self.instance_id is still valid
18322d
         return sources.instance_id_matches_system_uuid(self.get_instance_id())
18322d
 
18322d
+    def _iid(self, previous=None):
18322d
+        prev_iid_path = os.path.join(
18322d
+            self.paths.get_cpath('data'), 'instance-id')
18322d
+        iid = util.read_dmi_data('system-uuid')
18322d
+        if os.path.exists(prev_iid_path):
18322d
+            previous = util.load_file(prev_iid_path).strip()
18322d
+            if is_byte_swapped(previous, iid):
18322d
+                return previous
18322d
+        return iid
18322d
+
18322d
     @azure_ds_telemetry_reporter
18322d
     def setup(self, is_new_instance):
18322d
         if self._negotiated is False:
18322d
diff --git a/cloudinit/sources/helpers/azure.py b/cloudinit/sources/helpers/azure.py
18322d
index 82c4c8c..c2a57cc 100755
18322d
--- a/cloudinit/sources/helpers/azure.py
18322d
+++ b/cloudinit/sources/helpers/azure.py
18322d
@@ -7,6 +7,7 @@ import re
18322d
 import socket
18322d
 import struct
18322d
 import time
18322d
+import textwrap
18322d
 
18322d
 from cloudinit.net import dhcp
18322d
 from cloudinit import stages
18322d
@@ -40,6 +41,32 @@ def azure_ds_telemetry_reporter(func):
18322d
     return impl
18322d
 
18322d
 
18322d
+def is_byte_swapped(previous_id, current_id):
18322d
+    """
18322d
+    Azure stores the instance ID with an incorrect byte ordering for the
18322d
+    first parts. This corrects the byte order such that it is consistent with
18322d
+    that returned by the metadata service.
18322d
+    """
18322d
+    if previous_id == current_id:
18322d
+        return False
18322d
+
18322d
+    def swap_bytestring(s, width=2):
18322d
+        dd = [byte for byte in textwrap.wrap(s, 2)]
18322d
+        dd.reverse()
18322d
+        return ''.join(dd)
18322d
+
18322d
+    parts = current_id.split('-')
18322d
+    swapped_id = '-'.join([
18322d
+            swap_bytestring(parts[0]),
18322d
+            swap_bytestring(parts[1]),
18322d
+            swap_bytestring(parts[2]),
18322d
+            parts[3],
18322d
+            parts[4]
18322d
+        ])
18322d
+
18322d
+    return previous_id == swapped_id
18322d
+
18322d
+
18322d
 @contextmanager
18322d
 def cd(newdir):
18322d
     prevdir = os.getcwd()
18322d
diff --git a/tests/unittests/test_datasource/test_azure.py b/tests/unittests/test_datasource/test_azure.py
18322d
index bc8b42c..1fb0565 100644
18322d
--- a/tests/unittests/test_datasource/test_azure.py
18322d
+++ b/tests/unittests/test_datasource/test_azure.py
18322d
@@ -314,7 +314,7 @@ scbus-1 on xpt0 bus 0
18322d
             'public-keys': [],
18322d
         })
18322d
 
18322d
-        self.instance_id = 'test-instance-id'
18322d
+        self.instance_id = 'D0DF4C54-4ECB-4A4B-9954-5BDF3ED5C3B8'
18322d
 
18322d
         def _dmi_mocks(key):
18322d
             if key == 'system-uuid':
18322d
@@ -511,7 +511,7 @@ fdescfs            /dev/fd          fdescfs rw              0 0
18322d
                       'subnet': [{'address': '10.0.0.0', 'prefix': '24'}]},
18322d
                 'ipv6': {'ipAddress': []},
18322d
                 'macAddress': '000D3A047598'}]}},
18322d
-            'instance-id': 'test-instance-id',
18322d
+            'instance-id': 'D0DF4C54-4ECB-4A4B-9954-5BDF3ED5C3B8',
18322d
             'local-hostname': u'myhost',
18322d
             'random_seed': 'wild'}
18322d
 
18322d
@@ -881,6 +881,24 @@ fdescfs            /dev/fd          fdescfs rw              0 0
18322d
         self.assertTrue(ret)
18322d
         self.assertEqual('value', dsrc.metadata['test'])
18322d
 
18322d
+    def test_instance_id_endianness(self):
18322d
+        """Return the previous iid when dmi uuid is the byteswapped iid."""
18322d
+        ds = self._get_ds({'ovfcontent': construct_valid_ovf_env()})
18322d
+        # byte-swapped previous
18322d
+        write_file(
18322d
+            os.path.join(self.paths.cloud_dir, 'data', 'instance-id'),
18322d
+            '544CDFD0-CB4E-4B4A-9954-5BDF3ED5C3B8')
18322d
+        ds.get_data()
18322d
+        self.assertEqual(
18322d
+            '544CDFD0-CB4E-4B4A-9954-5BDF3ED5C3B8', ds.metadata['instance-id'])
18322d
+        # not byte-swapped previous
18322d
+        write_file(
18322d
+            os.path.join(self.paths.cloud_dir, 'data', 'instance-id'),
18322d
+            '644CDFD0-CB4E-4B4A-9954-5BDF3ED5C3B8')
18322d
+        ds.get_data()
18322d
+        self.assertEqual(
18322d
+            'D0DF4C54-4ECB-4A4B-9954-5BDF3ED5C3B8', ds.metadata['instance-id'])
18322d
+
18322d
     def test_instance_id_from_dmidecode_used(self):
18322d
         ds = self._get_ds({'ovfcontent': construct_valid_ovf_env()})
18322d
         ds.get_data()
18322d
@@ -1080,7 +1098,7 @@ class TestAzureBounce(CiTestCase):
18322d
 
18322d
         def _dmi_mocks(key):
18322d
             if key == 'system-uuid':
18322d
-                return 'test-instance-id'
18322d
+                return 'D0DF4C54-4ECB-4A4B-9954-5BDF3ED5C3B8'
18322d
             elif key == 'chassis-asset-tag':
18322d
                 return '7783-7084-3265-9085-8269-3286-77'
18322d
             raise RuntimeError('should not get here')
18322d
diff --git a/tests/unittests/test_datasource/test_azure_helper.py b/tests/unittests/test_datasource/test_azure_helper.py
18322d
index bd006ab..7ad5cc1 100644
18322d
--- a/tests/unittests/test_datasource/test_azure_helper.py
18322d
+++ b/tests/unittests/test_datasource/test_azure_helper.py
18322d
@@ -170,6 +170,25 @@ class TestGoalStateParsing(CiTestCase):
18322d
         goal_state = self._get_goal_state(instance_id=instance_id)
18322d
         self.assertEqual(instance_id, goal_state.instance_id)
18322d
 
18322d
+    def test_instance_id_byte_swap(self):
18322d
+        """Return true when previous_iid is byteswapped current_iid"""
18322d
+        previous_iid = "D0DF4C54-4ECB-4A4B-9954-5BDF3ED5C3B8"
18322d
+        current_iid = "544CDFD0-CB4E-4B4A-9954-5BDF3ED5C3B8"
18322d
+        self.assertTrue(
18322d
+            azure_helper.is_byte_swapped(previous_iid, current_iid))
18322d
+
18322d
+    def test_instance_id_no_byte_swap_same_instance_id(self):
18322d
+        previous_iid = "D0DF4C54-4ECB-4A4B-9954-5BDF3ED5C3B8"
18322d
+        current_iid = "D0DF4C54-4ECB-4A4B-9954-5BDF3ED5C3B8"
18322d
+        self.assertFalse(
18322d
+            azure_helper.is_byte_swapped(previous_iid, current_iid))
18322d
+
18322d
+    def test_instance_id_no_byte_swap_diff_instance_id(self):
18322d
+        previous_iid = "D0DF4C54-4ECB-4A4B-9954-5BDF3ED5C3B8"
18322d
+        current_iid = "G0DF4C54-4ECB-4A4B-9954-5BDF3ED5C3B8"
18322d
+        self.assertFalse(
18322d
+            azure_helper.is_byte_swapped(previous_iid, current_iid))
18322d
+
18322d
     def test_certificates_xml_parsed_and_fetched_correctly(self):
18322d
         http_client = mock.MagicMock()
18322d
         certificates_url = 'TestCertificatesUrl'
18322d
-- 
18322d
1.8.3.1
18322d