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