← Back to team overview

cloud-init-dev team mailing list archive

[Merge] ~ruansx/cloud-init:add-zstack-datasource1 into cloud-init:master

 

Steve Ruan has proposed merging ~ruansx/cloud-init:add-zstack-datasource1 into cloud-init:master.

Commit message:
Add datastore for ZStack platfrom

AWS EC2 datasource is used by lots of cloud operator, ZStack platform will provide the cloud-init service same as AWS.
ZStack will identify their platform to the guest by setting the 'dmidecode -s chassis-asset-tag' to a string that ends with '.zstack.io'.

LP: #1841181

Requested reviews:
  cloud-init Commiters (cloud-init-dev)
Related bugs:
  Bug #1841181 in cloud-init: "add datasource for ZStack"
  https://bugs.launchpad.net/cloud-init/+bug/1841181

For more details, see:
https://code.launchpad.net/~ruansx/cloud-init/+git/cloud-init/+merge/372830
-- 
Your team cloud-init Commiters is requested to review the proposed merge of ~ruansx/cloud-init:add-zstack-datasource1 into cloud-init:master.
diff --git a/cloudinit/apport.py b/cloudinit/apport.py
index 003ff1f..fde1f75 100644
--- a/cloudinit/apport.py
+++ b/cloudinit/apport.py
@@ -37,6 +37,7 @@ KNOWN_CLOUD_NAMES = [
     'Scaleway',
     'SmartOS',
     'VMware',
+    'ZStack',
     'Other']
 
 # Potentially clear text collected logs
diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py
index 1010745..6c72ace 100644
--- a/cloudinit/sources/DataSourceEc2.py
+++ b/cloudinit/sources/DataSourceEc2.py
@@ -33,6 +33,7 @@ class CloudNames(object):
     ALIYUN = "aliyun"
     AWS = "aws"
     BRIGHTBOX = "brightbox"
+    ZSTACK = "zstack"
     # UNKNOWN indicates no positive id.  If strict_id is 'warn' or 'false',
     # then an attempt at the Ec2 Metadata service will be made.
     UNKNOWN = "unknown"
@@ -477,10 +478,16 @@ def identify_brightbox(data):
         return CloudNames.BRIGHTBOX
 
 
+def identify_zstack(data):
+    if data['asset_tag'].endswith('.zstack.io'):
+        return CloudNames.ZSTACK
+
+
 def identify_platform():
     # identify the platform and return an entry in CloudNames.
     data = _collect_platform_data()
-    checks = (identify_aws, identify_brightbox, lambda x: CloudNames.UNKNOWN)
+    checks = (identify_aws, identify_brightbox, identify_zstack,
+              lambda x: CloudNames.UNKNOWN)
     for checker in checks:
         try:
             result = checker(data)
@@ -498,6 +505,7 @@ def _collect_platform_data():
        uuid: system-uuid from dmi or /sys/hypervisor
        uuid_source: 'hypervisor' (/sys/hypervisor/uuid) or 'dmi'
        serial: dmi 'system-serial-number' (/sys/.../product_serial)
+       asset_tag: 'dmidecode -s chassis-asset-tag'
 
     On Ec2 instances experimentation is that product_serial is upper case,
     and product_uuid is lower case.  This returns lower case values for both.
@@ -520,6 +528,12 @@ def _collect_platform_data():
 
     data['serial'] = serial.lower()
 
+    asset_tag = util.read_dmi_data('chassis-asset-tag')
+    if asset_tag is None:
+        asset_tag = ''
+
+    data['asset_tag'] = asset_tag.lower()
+
     return data
 
 
diff --git a/doc/rtd/topics/datasources.rst b/doc/rtd/topics/datasources.rst
index 8e58be9..0872c5b 100644
--- a/doc/rtd/topics/datasources.rst
+++ b/doc/rtd/topics/datasources.rst
@@ -112,6 +112,7 @@ API
 
 The current interface that a datasource object must provide is the following:
 
+<<<<<<< doc/rtd/topics/datasources.rst
 .. sourcecode:: python
 
     # returns a mime multipart message that contains
@@ -163,5 +164,26 @@ The current interface that a datasource object must provide is the following:
     def get_hostname(self, fqdn=False)
 
     def get_package_mirror_info(self)
+=======
+   datasources/aliyun.rst
+   datasources/altcloud.rst
+   datasources/azure.rst
+   datasources/cloudsigma.rst
+   datasources/cloudstack.rst
+   datasources/configdrive.rst
+   datasources/digitalocean.rst
+   datasources/ec2.rst
+   datasources/exoscale.rst
+   datasources/maas.rst
+   datasources/nocloud.rst
+   datasources/opennebula.rst
+   datasources/openstack.rst
+   datasources/oracle.rst
+   datasources/ovf.rst
+   datasources/smartos.rst
+   datasources/fallback.rst
+   datasources/gce.rst
+   datasources/zstack.rst
+>>>>>>> doc/rtd/topics/datasources.rst
 
 .. vi: textwidth=79
diff --git a/doc/rtd/topics/datasources/zstack.rst b/doc/rtd/topics/datasources/zstack.rst
new file mode 100644
index 0000000..36e60ff
--- /dev/null
+++ b/doc/rtd/topics/datasources/zstack.rst
@@ -0,0 +1,36 @@
+.. _datasource_zstack:
+
+ZStack
+======
+ZStack platform provides a AWS Ec2 metadata service, but with different datasource identity.
+More information about ZStack can be found at `ZStack <https://www.zstack.io>`__.
+
+Discovery
+---------
+To determine whether a vm running on ZStack platform, cloud-init checks DMI information
+by 'dmidecode -s chassis-asset-tag', if the output ends with '.zstack.io', it's running
+on ZStack platform:
+
+
+Metadata
+^^^^^^^^
+Same as EC2, instance metadata can be queried at
+
+::
+
+    GET http://169.254.169.254/2009-04-04/meta-data/
+    instance-id
+    local-hostname
+
+Userdata
+^^^^^^^^
+Same as EC2, instance userdata can be queried at
+
+::
+
+    GET http://169.254.169.254/2009-04-04/user-data/
+    meta_data.json
+    user_data
+    password
+
+.. vi: textwidth=78
diff --git a/tests/unittests/test_datasource/test_ec2.py b/tests/unittests/test_datasource/test_ec2.py
index 1ec8e00..daa4532 100644
--- a/tests/unittests/test_datasource/test_ec2.py
+++ b/tests/unittests/test_datasource/test_ec2.py
@@ -662,4 +662,29 @@ class TestConvertEc2MetadataNetworkConfig(test_helpers.CiTestCase):
                 expected,
                 ec2.convert_ec2_metadata_network_config(self.network_metadata))
 
+class TesIdentifyPlatform(test_helpers.CiTestCase):
+
+    def collmock(self, **kwargs):
+        """return non-special _collect_platform_data updated with changes."""
+        unspecial = {
+            'asset_tag': '3857-0037-2746-7462-1818-3997-77',
+            'serial': 'H23-C4J3JV-R6',
+            'uuid': '81c7e555-6471-4833-9551-1ab366c4cfd2',
+            'uuid_source': 'dmi',
+        }
+        unspecial.update(**kwargs)
+        return unspecial
+
+    @mock.patch('cloudinit.sources.DataSourceEc2._collect_platform_data')
+    def test_identify_zstack(self, m_collect):
+        """zstack should be identified if cassis-asset-tag ends in .zstack.io."""
+        m_collect.return_value = self.collmock(asset_tag='123456.zstack.io')
+        self.assertEqual(ec2.CloudNames.ZSTACK, ec2.identify_platform())
+
+    @mock.patch('cloudinit.sources.DataSourceEc2._collect_platform_data')
+    def test_identify_zstack_full_domain_only(self, m_collect):
+        """zstack asset-tag matching should match only on full domain boundary."""
+        m_collect.return_value = self.collmock(asset_tag='123456.buzzstack.io')
+        self.assertEqual(ec2.CloudNames.UNKNOWN, ec2.identify_platform())
+
 # vi: ts=4 expandtab
diff --git a/tests/unittests/test_ds_identify.py b/tests/unittests/test_ds_identify.py
index de87be2..7aeeb91 100644
--- a/tests/unittests/test_ds_identify.py
+++ b/tests/unittests/test_ds_identify.py
@@ -609,6 +609,10 @@ class TestDsIdentify(DsIdentifyBase):
         self.assertEqual(expected, [p for p in expected if p in toks],
                          "path did not have expected tokens")
 
+    def test_zstack_is_ec2(self):
+        """EC2: chassis asset tag ends with 'zstack.io'"""
+        self._test_ds_found('Ec2-ZStack')
+
 
 class TestIsIBMProvisioning(DsIdentifyBase):
     """Test the is_ibm_provisioning method in ds-identify."""
@@ -971,8 +975,11 @@ VALID_CFG = {
             {'name': 'blkid', 'ret': 2, 'out': ''},
         ],
         'files': {ds_smartos.METADATA_SOCKFILE: 'would be a socket\n'},
+    },
+    'Ec2-ZStack': {
+        'ds': 'Ec2',
+        'files': {P_CHASSIS_ASSET_TAG: '123456.zstack.io\n'},
     }
-
 }
 
 # vi: ts=4 expandtab
diff --git a/tools/ds-identify b/tools/ds-identify
index 2447d14..f76f2a6 100755
--- a/tools/ds-identify
+++ b/tools/ds-identify
@@ -895,6 +895,11 @@ ec2_identify_platform() {
         *.brightbox.com) _RET="Brightbox"; return 0;;
     esac
 
+    local asset_tag="${DI_DMI_CHASSIS_ASSET_TAG}"
+    case "$asset_tag" in
+        *.zstack.io) _RET="ZStack"; return 0;;
+    esac
+
     # AWS http://docs.aws.amazon.com/AWSEC2/
     #     latest/UserGuide/identify_ec2_instances.html
     local uuid="" hvuuid="${PATH_SYS_HYPERVISOR}/uuid"