summaryrefslogtreecommitdiff
path: root/cloudinit/sources/DataSourceSmartOS.py
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/sources/DataSourceSmartOS.py')
-rw-r--r--cloudinit/sources/DataSourceSmartOS.py133
1 files changed, 113 insertions, 20 deletions
diff --git a/cloudinit/sources/DataSourceSmartOS.py b/cloudinit/sources/DataSourceSmartOS.py
index 1ce20c10..551b20c4 100644
--- a/cloudinit/sources/DataSourceSmartOS.py
+++ b/cloudinit/sources/DataSourceSmartOS.py
@@ -27,6 +27,7 @@
#
+import base64
from cloudinit import log as logging
from cloudinit import sources
from cloudinit import util
@@ -35,8 +36,6 @@ import os.path
import serial
-DEF_TTY_LOC = '/dev/ttyS1'
-DEF_TTY_TIMEOUT = 60
LOG = logging.getLogger(__name__)
SMARTOS_ATTRIB_MAP = {
@@ -47,17 +46,66 @@ SMARTOS_ATTRIB_MAP = {
'user-data': ('user-data', False),
'iptables_disable': ('iptables_disable', True),
'motd_sys_info': ('motd_sys_info', True),
+ 'availability_zone': ('datacenter_name', True),
+}
+
+DS_NAME = 'SmartOS'
+DS_CFG_PATH = ['datasource', DS_NAME]
+# BUILT-IN DATASOURCE CONFIGURATION
+# The following is the built-in configuration. If the values
+# are not set via the system configuration, then these default
+# will be used:
+# serial_device: which serial device to use for the meta-data
+# seed_timeout: how long to wait on the device
+# no_base64_decode: values which are not base64 encoded and
+# are fetched directly from SmartOS, not meta-data values
+# base64_keys: meta-data keys that are delivered in base64
+# base64_all: with the exclusion of no_base64_decode values,
+# treat all meta-data as base64 encoded
+# disk_setup: describes how to partition the ephemeral drive
+# fs_setup: describes how to format the ephemeral drive
+#
+BUILTIN_DS_CONFIG = {
+ 'serial_device': '/dev/ttyS1',
+ 'seed_timeout': 60,
+ 'no_base64_decode': ['root_authorized_keys',
+ 'motd_sys_info',
+ 'iptables_disable'],
+ 'base64_keys': [],
+ 'base64_all': False,
+ 'disk_aliases': {'ephemeral0': '/dev/vdb'},
+}
+
+BUILTIN_CLOUD_CONFIG = {
+ 'disk_setup': {
+ 'ephemeral0': {'table_type': 'mbr',
+ 'layout': False,
+ 'overwrite': False}
+ },
+ 'fs_setup': [{'label': 'ephemeral0',
+ 'filesystem': 'ext3',
+ 'device': 'ephemeral0'}],
}
class DataSourceSmartOS(sources.DataSource):
def __init__(self, sys_cfg, distro, paths):
sources.DataSource.__init__(self, sys_cfg, distro, paths)
- self.seed_dir = os.path.join(paths.seed_dir, 'sdc')
self.is_smartdc = None
- self.seed = self.sys_cfg.get("serial_device", DEF_TTY_LOC)
- self.seed_timeout = self.sys_cfg.get("serial_timeout",
- DEF_TTY_TIMEOUT)
+
+ self.ds_cfg = util.mergemanydict([
+ self.ds_cfg,
+ util.get_cfg_by_path(sys_cfg, DS_CFG_PATH, {}),
+ BUILTIN_DS_CONFIG])
+
+ self.metadata = {}
+ self.cfg = BUILTIN_CLOUD_CONFIG
+
+ self.seed = self.ds_cfg.get("serial_device")
+ self.seed_timeout = self.ds_cfg.get("serial_timeout")
+ self.smartos_no_base64 = self.ds_cfg.get('no_base64_decode')
+ self.b64_keys = self.ds_cfg.get('base64_keys')
+ self.b64_all = self.ds_cfg.get('base64_all')
def __str__(self):
root = sources.DataSource.__str__(self)
@@ -70,7 +118,6 @@ class DataSourceSmartOS(sources.DataSource):
if not os.path.exists(self.seed):
LOG.debug("Host does not appear to be on SmartOS")
return False
- self.seed = self.seed
dmi_info = dmi_data()
if dmi_info is False:
@@ -79,35 +126,60 @@ class DataSourceSmartOS(sources.DataSource):
system_uuid, system_type = dmi_info
if 'smartdc' not in system_type.lower():
- LOG.debug("Host is not on SmartOS")
+ LOG.debug("Host is not on SmartOS. system_type=%s", system_type)
return False
self.is_smartdc = True
md['instance-id'] = system_uuid
+ b64_keys = self.query('base64_keys', strip=True, b64=False)
+ if b64_keys is not None:
+ self.b64_keys = [k.strip() for k in str(b64_keys).split(',')]
+
+ b64_all = self.query('base64_all', strip=True, b64=False)
+ if b64_all is not None:
+ self.b64_all = util.is_true(b64_all)
+
for ci_noun, attribute in SMARTOS_ATTRIB_MAP.iteritems():
smartos_noun, strip = attribute
- md[ci_noun] = query_data(smartos_noun, self.seed,
- self.seed_timeout, strip=strip)
+ md[ci_noun] = self.query(smartos_noun, strip=strip)
if not md['local-hostname']:
md['local-hostname'] = system_uuid
+ ud = None
if md['user-data']:
ud = md['user-data']
- else:
+ elif md['user-script']:
ud = md['user-script']
- self.metadata = md
+ self.metadata = util.mergemanydict([md, self.metadata])
self.userdata_raw = ud
return True
+ def device_name_to_device(self, name):
+ return self.ds_cfg['disk_aliases'].get(name)
+
+ def get_config_obj(self):
+ return self.cfg
+
def get_instance_id(self):
return self.metadata['instance-id']
+ def query(self, noun, strip=False, default=None, b64=None):
+ if b64 is None:
+ if noun in self.smartos_no_base64:
+ b64 = False
+ elif self.b64_all or noun in self.b64_keys:
+ b64 = True
+
+ return query_data(noun=noun, strip=strip, seed_device=self.seed,
+ seed_timeout=self.seed_timeout, default=default,
+ b64=b64)
+
def get_serial(seed_device, seed_timeout):
"""This is replaced in unit testing, allowing us to replace
- serial.Serial with a mocked class
+ serial.Serial with a mocked class.
The timeout value of 60 seconds should never be hit. The value
is taken from SmartOS own provisioning tools. Since we are reading
@@ -124,12 +196,18 @@ def get_serial(seed_device, seed_timeout):
return ser
-def query_data(noun, seed_device, seed_timeout, strip=False):
+def query_data(noun, seed_device, seed_timeout, strip=False, default=None,
+ b64=None):
"""Makes a request to via the serial console via "GET <NOUN>"
In the response, the first line is the status, while subsequent lines
are is the value. A blank line with a "." is used to indicate end of
response.
+
+ If the response is expected to be base64 encoded, then set b64encoded
+ to true. Unfortantely, there is no way to know if something is 100%
+ encoded, so this method relies on being told if the data is base64 or
+ not.
"""
if not noun:
@@ -143,7 +221,7 @@ def query_data(noun, seed_device, seed_timeout, strip=False):
if 'SUCCESS' not in status:
ser.close()
- return None
+ return default
while not eom_found:
m = ser.readline()
@@ -153,12 +231,27 @@ def query_data(noun, seed_device, seed_timeout, strip=False):
response.append(m)
ser.close()
- if not strip:
- return "".join(response)
+
+ if b64 is None:
+ b64 = query_data('b64-%s' % noun, seed_device=seed_device,
+ seed_timeout=seed_timeout, b64=False,
+ default=False, strip=True)
+ b64 = util.is_true(b64)
+
+ resp = None
+ if b64 or strip:
+ resp = "".join(response).rstrip()
else:
- return "".join(response).rstrip()
+ resp = "".join(response)
+
+ if b64:
+ try:
+ return base64.b64decode(resp)
+ except TypeError:
+ LOG.warn("Failed base64 decoding key '%s'", noun)
+ return resp
- return None
+ return resp
def dmi_data():
@@ -181,7 +274,7 @@ def dmi_data():
except Exception as e:
util.logexc(LOG, "Failed to get system UUID", e)
- return sys_uuid.lower(), sys_type
+ return (sys_uuid.lower().strip(), sys_type.strip())
# Used to match classes to dependencies