summaryrefslogtreecommitdiff
path: root/cloudinit
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit')
-rw-r--r--cloudinit/settings.py1
-rw-r--r--cloudinit/sources/DataSourceAltCloud.py299
2 files changed, 300 insertions, 0 deletions
diff --git a/cloudinit/settings.py b/cloudinit/settings.py
index 2083cf60..cdfc31ae 100644
--- a/cloudinit/settings.py
+++ b/cloudinit/settings.py
@@ -31,6 +31,7 @@ CFG_BUILTIN = {
'datasource_list': [
'NoCloud',
'ConfigDrive',
+ 'AltCloud',
'OVF',
'MAAS',
'Ec2',
diff --git a/cloudinit/sources/DataSourceAltCloud.py b/cloudinit/sources/DataSourceAltCloud.py
new file mode 100644
index 00000000..69c376a5
--- /dev/null
+++ b/cloudinit/sources/DataSourceAltCloud.py
@@ -0,0 +1,299 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2009-2010 Canonical Ltd.
+# Copyright (C) 2012 Hewlett-Packard Development Company, L.P.
+# Copyright (C) 2012 Yahoo! Inc.
+#
+# Author: Joe VLcek <JVLcek@RedHat.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+'''
+This file contains code used to gather the user data passed to an
+instance on RHEVm and vSphere.
+'''
+
+import errno
+import os
+import os.path
+
+from cloudinit import log as logging
+from cloudinit import sources
+from cloudinit import util
+from cloudinit.util import ProcessExecutionError
+
+LOG = logging.getLogger(__name__)
+
+# Needed file paths
+CLOUD_INFO_FILE = '/etc/sysconfig/cloud-info'
+
+# Shell command lists
+CMD_DMI_SYSTEM = ['/usr/sbin/dmidecode', '--string', 'system-product-name']
+CMD_PROBE_FLOPPY = ['/sbin/modprobe', 'floppy']
+CMD_UDEVADM_SETTLE = ['/sbin/udevadm', 'settle', '--quiet', '--timeout=5']
+
+META_DATA_NOT_SUPPORTED = {
+ 'block-device-mapping': {},
+ 'instance-id': 455,
+ 'local-hostname': 'localhost',
+ 'placement': {},
+ }
+
+
+def read_user_data_callback(mount_dir):
+ '''
+ Description:
+ This callback will be applied by util.mount_cb() on the mounted
+ file.
+
+ Deltacloud file name contains deltacloud. Those not using
+ Deltacloud but instead instrumenting the injection, could
+ drop deltacloud from the file name.
+
+ Input:
+ mount_dir - Mount directory
+
+ Returns:
+ User Data
+
+ '''
+
+ deltacloud_user_data_file = mount_dir + '/deltacloud-user-data.txt'
+ user_data_file = mount_dir + '/user-data.txt'
+
+ # First try deltacloud_user_data_file. On failure try user_data_file.
+ try:
+ with open(deltacloud_user_data_file, 'r') as user_data_f:
+ user_data = user_data_f.read().strip()
+ except:
+ try:
+ with open(user_data_file, 'r') as user_data_f:
+ user_data = user_data_f.read().strip()
+ except:
+ util.logexc(LOG, ('Failed accessing user data file.'))
+ return None
+
+ return user_data
+
+
+class DataSourceAltCloud(sources.DataSource):
+ def __init__(self, sys_cfg, distro, paths):
+ sources.DataSource.__init__(self, sys_cfg, distro, paths)
+ self.seed = None
+ self.supported_seed_starts = ("/", "file://")
+
+ def __str__(self):
+ mstr = "%s [seed=%s]" % (util.obj_name(self), self.seed)
+ return mstr
+
+ def get_cloud_type(self):
+ '''
+ Description:
+ Get the type for the cloud back end this instance is running on
+ by examining the string returned by:
+ dmidecode --string system-product-name
+
+ On VMWare/vSphere dmidecode returns: RHEV Hypervisor
+ On VMWare/vSphere dmidecode returns: VMware Virtual Platform
+
+ Input:
+ None
+
+ Returns:
+ One of the following strings:
+ 'RHEV', 'VSPHERE' or 'UNKNOWN'
+
+ '''
+
+ cmd = CMD_DMI_SYSTEM
+ try:
+ (cmd_out, _err) = util.subp(cmd)
+ except ProcessExecutionError, _err:
+ LOG.debug(('Failed command: %s\n%s') % \
+ (' '.join(cmd), _err.message))
+ return 'UNKNOWN'
+ except OSError, _err:
+ LOG.debug(('Failed command: %s\n%s') % \
+ (' '.join(cmd), _err.message))
+ return 'UNKNOWN'
+
+ if cmd_out.upper().startswith('RHEV'):
+ return 'RHEV'
+
+ if cmd_out.upper().startswith('VMWARE'):
+ return 'VSPHERE'
+
+ return 'UNKNOWN'
+
+ def get_data(self):
+ '''
+ Description:
+ User Data is passed to the launching instance which
+ is used to perform instance configuration.
+
+ Cloud providers expose the user data differently.
+ It is necessary to determine which cloud provider
+ the current instance is running on to determine
+ how to access the user data. Images built with
+ image factory will contain a CLOUD_INFO_FILE which
+ contains a string identifying the cloud provider.
+
+ Images not built with Imagefactory will try to
+ determine what the cloud provider is based on system
+ information.
+ '''
+
+ LOG.debug('Invoked get_data()')
+
+ if os.path.exists(CLOUD_INFO_FILE):
+ try:
+ cloud_info = open(CLOUD_INFO_FILE)
+ cloud_type = cloud_info.read().strip().upper()
+ cloud_info.close()
+ except:
+ util.logexc(LOG, 'Unable to access cloud info file.')
+ return False
+ else:
+ cloud_type = self.get_cloud_type()
+
+ LOG.debug('cloud_type: ' + str(cloud_type))
+
+ if 'RHEV' in cloud_type:
+ if self.user_data_rhevm():
+ return True
+ elif 'VSPHERE' in cloud_type:
+ if self.user_data_vsphere():
+ return True
+ else:
+ # there was no recognized alternate cloud type
+ # indicating this handler should not be used.
+ return False
+
+ # No user data found
+ util.logexc(LOG, ('Failed accessing user data.'))
+ return False
+
+ def user_data_rhevm(self):
+ '''
+ RHEVM specific userdata read
+
+ If on RHEV-M the user data will be contained on the
+ floppy device in file <user_data_file>
+ To access it:
+ modprobe floppy
+
+ Leverage util.mount_cb to:
+ mkdir <tmp mount dir>
+ mount /dev/fd0 <tmp mount dir>
+ The call back passed to util.mount_cb will do:
+ read <tmp mount dir>/<user_data_file>
+ '''
+
+ return_str = None
+
+ # modprobe floppy
+ try:
+ cmd = CMD_PROBE_FLOPPY
+ (cmd_out, _err) = util.subp(cmd)
+ LOG.debug(('Command: %s\nOutput%s') % (' '.join(cmd), cmd_out))
+ except ProcessExecutionError, _err:
+ util.logexc(LOG, (('Failed command: %s\n%s') % \
+ (' '.join(cmd), _err.message)))
+ return False
+ except OSError, _err:
+ util.logexc(LOG, (('Failed command: %s\n%s') % \
+ (' '.join(cmd), _err.message)))
+ return False
+
+ floppy_dev = '/dev/fd0'
+
+ # udevadm settle for floppy device
+ try:
+ cmd = CMD_UDEVADM_SETTLE
+ cmd.append('--exit-if-exists=' + floppy_dev)
+ (cmd_out, _err) = util.subp(cmd)
+ LOG.debug(('Command: %s\nOutput%s') % (' '.join(cmd), cmd_out))
+ except ProcessExecutionError, _err:
+ util.logexc(LOG, (('Failed command: %s\n%s') % \
+ (' '.join(cmd), _err.message)))
+ return False
+ except OSError, _err:
+ util.logexc(LOG, (('Failed command: %s\n%s') % \
+ (' '.join(cmd), _err.message)))
+ return False
+
+ try:
+ return_str = util.mount_cb(floppy_dev, read_user_data_callback)
+ except OSError as err:
+ if err.errno != errno.ENOENT:
+ raise
+ except util.MountFailedError:
+ util.logexc(LOG, ("Failed to mount %s"
+ " when looking for user data"), floppy_dev)
+
+ self.userdata_raw = return_str
+ self.metadata = META_DATA_NOT_SUPPORTED
+
+ if return_str:
+ return True
+ else:
+ return False
+
+ def user_data_vsphere(self):
+ '''
+ vSphere specific userdata read
+
+ If on vSphere the user data will be contained on the
+ cdrom device in file <user_data_file>
+ To access it:
+ Leverage util.mount_cb to:
+ mkdir <tmp mount dir>
+ mount /dev/fd0 <tmp mount dir>
+ The call back passed to util.mount_cb will do:
+ read <tmp mount dir>/<user_data_file>
+ '''
+
+ return_str = None
+ cdrom_list = util.find_devs_with('LABEL=CDROM')
+ for cdrom_dev in cdrom_list:
+ try:
+ return_str = util.mount_cb(cdrom_dev, read_user_data_callback)
+ if return_str:
+ break
+ except OSError as err:
+ if err.errno != errno.ENOENT:
+ raise
+ except util.MountFailedError:
+ util.logexc(LOG, ("Failed to mount %s"
+ " when looking for user data"), cdrom_dev)
+
+ self.userdata_raw = return_str
+ self.metadata = META_DATA_NOT_SUPPORTED
+
+ if return_str:
+ return True
+ else:
+ return False
+
+# Used to match classes to dependencies
+# Source DataSourceAltCloud does not really depend on networking.
+# In the future 'dsmode' like behavior can be added to offer user
+# the ability to run before networking.
+datasources = [
+ (DataSourceAltCloud, (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)