← Back to team overview

cloud-init-dev team mailing list archive

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

 

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

Commit message:
add cloud-init to detech ZStack cloud platfrom.


Requested reviews:
  cloud-init Commiters (cloud-init-dev)

For more details, see:
https://code.launchpad.net/~ruansx/cloud-init/+git/cloud-init/+merge/372445
-- 
Your team cloud-init Commiters is requested to review the proposed merge of ~ruansx/cloud-init:add-zstack-datasource 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/settings.py b/cloudinit/settings.py
index 2060d81..7e1fa06 100644
--- a/cloudinit/settings.py
+++ b/cloudinit/settings.py
@@ -40,6 +40,7 @@ CFG_BUILTIN = {
         'IBMCloud',
         'Oracle',
         'Exoscale',
+        'ZStack',
         # At the end to act as a 'catch' when none of the above work...
         'None',
     ],
diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py
index 5c017bf..c75bfc8 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"
diff --git a/cloudinit/sources/DataSourceZStack.py b/cloudinit/sources/DataSourceZStack.py
new file mode 100644
index 0000000..1c22706
--- /dev/null
+++ b/cloudinit/sources/DataSourceZStack.py
@@ -0,0 +1,58 @@
+# This file is part of cloud-init. See LICENSE file for license information.
+
+from cloudinit import sources
+from cloudinit.sources import DataSourceEc2 as EC2
+from cloudinit import util
+
+CHASSIS_ASSET_TAG = "ZStack Vm"
+
+
+class DataSourceZStack(EC2.DataSourceEc2):
+
+    dsname = 'ZStack'
+
+    def get_hostname(self, fqdn=False, resolve_ip=False, metadata_only=False):
+        return self.metadata.get('hostname', 'localhost.localdomain')
+
+    def get_public_ssh_keys(self):
+        return parse_public_keys(self.metadata.get('public-keys', {}))
+
+    def _get_cloud_name(self):
+        if _is_zstack():
+            return EC2.CloudNames.ZStack
+        else:
+            return EC2.CloudNames.NO_EC2_METADATA
+
+
+def _is_zstack():
+    asset_tag = util.read_dmi_data('chassis-asset-tag')
+    return asset_tag == CHASSIS_ASSET_TAG
+
+
+def parse_public_keys(public_keys):
+    keys = []
+    for _key_id, key_body in public_keys.items():
+        if isinstance(key_body, str):
+            keys.append(key_body.strip())
+        elif isinstance(key_body, list):
+            keys.extend(key_body)
+        elif isinstance(key_body, dict):
+            key = key_body.get('openssh-key', [])
+            if isinstance(key, str):
+                keys.append(key.strip())
+            elif isinstance(key, list):
+                keys.extend(key)
+    return keys
+
+
+# Used to match classes to dependencies
+datasources = [
+    (DataSourceZStack, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)),
+]
+
+
+# Return a list of data sources that match this set of dependencies
+def get_datasource_list(depends):
+    return sources.list_from_depends(depends, datasources)
+
+# vi: ts=4 expandtab
diff --git a/doc/rtd/topics/datasources.rst b/doc/rtd/topics/datasources.rst
index 2148cd5..6e71cce 100644
--- a/doc/rtd/topics/datasources.rst
+++ b/doc/rtd/topics/datasources.rst
@@ -165,5 +165,6 @@ Follow for more information.
    datasources/smartos.rst
    datasources/fallback.rst
    datasources/gce.rst
+   datasources/zstack.rst
 
 .. vi: textwidth=78
diff --git a/doc/rtd/topics/datasources/zstack.rst b/doc/rtd/topics/datasources/zstack.rst
new file mode 100644
index 0000000..996e13a
--- /dev/null
+++ b/doc/rtd/topics/datasources/zstack.rst
@@ -0,0 +1,15 @@
+.. _datasource_zstack:
+
+ZStack
+======
+The ``ZStack`` datasource get data from ZStack platform cloud-init service.
+More information is here `ZStack <www.zstack.io>`_.
+
+Discovery
+---------
+To determine whether a platform is ZStack, cloud-init checks the following environment
+ attributes as a potential OpenStack platform:
+
+ * **DMI chassis_asset_tag** is *"ZStack Vm"*
+
+.. vi: textwidth=78
diff --git a/tests/unittests/test_datasource/test_common.py b/tests/unittests/test_datasource/test_common.py
index 61a7a76..8d78d5a 100644
--- a/tests/unittests/test_datasource/test_common.py
+++ b/tests/unittests/test_datasource/test_common.py
@@ -25,6 +25,7 @@ from cloudinit.sources import (
     DataSourceOVF as OVF,
     DataSourceScaleway as Scaleway,
     DataSourceSmartOS as SmartOS,
+    DataSourceZStack as ZStack,
 )
 from cloudinit.sources import DataSourceNone as DSNone
 
@@ -60,6 +61,7 @@ DEFAULT_NETWORK = [
     NoCloud.DataSourceNoCloudNet,
     OpenStack.DataSourceOpenStack,
     OVF.DataSourceOVFNet,
+    ZStack.DataSourceZStack
 ]
 
 
diff --git a/tests/unittests/test_ds_identify.py b/tests/unittests/test_ds_identify.py
index 587e699..b46f986 100644
--- a/tests/unittests/test_ds_identify.py
+++ b/tests/unittests/test_ds_identify.py
@@ -13,6 +13,7 @@ from cloudinit.tests.helpers import (
 from cloudinit.sources import DataSourceIBMCloud as ds_ibm
 from cloudinit.sources import DataSourceSmartOS as ds_smartos
 from cloudinit.sources import DataSourceOracle as ds_oracle
+from cloudinit.sources import DataSourceZStack as ds_zstack
 
 UNAME_MYSYS = ("Linux bart 4.4.0-62-generic #83-Ubuntu "
                "SMP Wed Jan 18 14:10:15 UTC 2017 x86_64 GNU/Linux")
@@ -655,6 +656,18 @@ class TestOracle(DsIdentifyBase):
         self._check_via_dict(mycfg, rc=RC_NOT_FOUND)
 
 
+class TestZStack(DsIdentifyBase):
+    def test_found_by_chassis(self):
+        """Simple positive test of ZStack by chassis id."""
+        self._test_ds_found('ZStack')
+
+    def test_not_found(self):
+        """Simple negative test of ZStack."""
+        mycfg = copy.deepcopy(VALID_CFG['ZStack'])
+        mycfg['files'][P_CHASSIS_ASSET_TAG] = "Not ZStack"
+        self._check_via_dict(mycfg, rc=RC_NOT_FOUND)
+
+
 def blkid_out(disks=None):
     """Convert a list of disk dictionaries into blkid content."""
     if disks is None:
@@ -959,6 +972,12 @@ VALID_CFG = {
             {'name': 'blkid', 'ret': 2, 'out': ''},
         ],
         'files': {ds_smartos.METADATA_SOCKFILE: 'would be a socket\n'},
+    },
+    'ZStack': {
+        'ds': 'ZStack',
+        'files': {
+            P_CHASSIS_ASSET_TAG: ds_zstack.CHASSIS_ASSET_TAG + '\n',
+        }
     }
 
 }
diff --git a/tools/ds-identify b/tools/ds-identify
index e0d4865..daaa17b 100755
--- a/tools/ds-identify
+++ b/tools/ds-identify
@@ -124,7 +124,7 @@ DI_DSNAME=""
 # be searched if there is no setting found in config.
 DI_DSLIST_DEFAULT="MAAS ConfigDrive NoCloud AltCloud Azure Bigstep \
 CloudSigma CloudStack DigitalOcean AliYun Ec2 GCE OpenNebula OpenStack \
-OVF SmartOS Scaleway Hetzner IBMCloud Oracle Exoscale"
+OVF SmartOS Scaleway Hetzner IBMCloud Oracle Exoscale ZStack"
 DI_DSLIST=""
 DI_MODE=""
 DI_ON_FOUND=""
@@ -1103,6 +1103,12 @@ dscheck_Oracle() {
     return ${DS_NOT_FOUND}
 }
 
+dscheck_ZStack() {
+    local asset_tag="ZStack Vm"
+    dmi_chassis_asset_tag_matches "${asset_tag}" && return ${DS_FOUND}
+    return ${DS_NOT_FOUND}
+}
+
 is_ibm_provisioning() {
     local pcfg="${PATH_ROOT}/root/provisioningConfiguration.cfg"
     local logf="${PATH_ROOT}/root/swinstall.log"

Follow ups