PSARC 2017/039 cloudbase-init: Portable cloud image initialization with ConfigDrive
25615046 cloudbase-init should support configdrive on solaris
25027429 Enable cloudbase-init debug in config file
--- a/components/openstack/cloudbase-init/Makefile Thu Mar 23 23:22:55 2017 -0700
+++ b/components/openstack/cloudbase-init/Makefile Fri Mar 24 14:28:46 2017 -0600
@@ -65,6 +65,12 @@
COMPONENT_PRE_BUILD_ACTION = \
$(CP) $(COMPONENT_DIR)/PKG-INFO $(SOURCE_DIR)
+COMPONENT_POST_INSTALL_ACTION += \
+ $(CP) \
+ files/solaris.py \
+ $(PROTO_DIR)$(PYTHON_LIB)/cloudbaseinit/metadata/services/osconfigdrive; \
+ $(PYTHON) -m compileall $(PROTO_DIR)/$(PYTHON_VENDOR_PACKAGES)
+
# common targets
build: $(BUILD_NO_ARCH)
--- a/components/openstack/cloudbase-init/cloudbase-init.p5m Thu Mar 23 23:22:55 2017 -0700
+++ b/components/openstack/cloudbase-init/cloudbase-init.p5m Fri Mar 24 14:28:46 2017 -0600
@@ -20,7 +20,7 @@
#
#
-# Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
#
set name=pkg.fmri \
@@ -39,9 +39,9 @@
set name=org.opensolaris.arc-caseid value=PSARC/2016/010
set name=org.opensolaris.consolidation value=$(CONSOLIDATION)
#
-file cloudbase-init.conf path=etc/cloudbase-init.conf
file cloudbase-init.xml path=lib/svc/manifest/application/cloudbase-init.xml
file cloudbase-init path=lib/svc/method/cloudbase-init
+file cloudbase-init.stencil path=lib/svc/stencils/cloudbase-init.stencil
file path=usr/bin/cloudbase-init
file path=usr/lib/python$(PYVER)/vendor-packages/cloudbase_init-$(COMPONENT_VERSION)-py$(PYVER).egg-info/PKG-INFO
file path=usr/lib/python$(PYVER)/vendor-packages/cloudbase_init-$(COMPONENT_VERSION)-py$(PYVER).egg-info/SOURCES.txt
@@ -67,6 +67,8 @@
file path=usr/lib/python$(PYVER)/vendor-packages/cloudbaseinit/metadata/services/osconfigdrive/__init__.py
file path=usr/lib/python$(PYVER)/vendor-packages/cloudbaseinit/metadata/services/osconfigdrive/base.py
file path=usr/lib/python$(PYVER)/vendor-packages/cloudbaseinit/metadata/services/osconfigdrive/factory.py
+file files/solaris.py \
+ path=usr/lib/python$(PYVER)/vendor-packages/cloudbaseinit/metadata/services/osconfigdrive/solaris.py
file path=usr/lib/python$(PYVER)/vendor-packages/cloudbaseinit/metadata/services/osconfigdrive/windows.py
file path=usr/lib/python$(PYVER)/vendor-packages/cloudbaseinit/osutils/__init__.py
file path=usr/lib/python$(PYVER)/vendor-packages/cloudbaseinit/osutils/base.py
--- a/components/openstack/cloudbase-init/files/cloudbase-init Thu Mar 23 23:22:55 2017 -0700
+++ b/components/openstack/cloudbase-init/files/cloudbase-init Fri Mar 24 14:28:46 2017 -0600
@@ -1,6 +1,6 @@
#!/usr/bin/python2.7
-# Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@@ -16,6 +16,7 @@
import ConfigParser
import os
+import subprocess
import sys
import urllib2
@@ -25,21 +26,22 @@
def start():
# verify metadata service is reachable
- try:
- parser = ConfigParser.ConfigParser()
- parser.read('/etc/cloudbase-init.conf')
- if parser.has_option('DEFAULT', 'metadata_base_url'):
- url = parser.get('DEFAULT', 'metadata_base_url')
- else:
- url = 'http://169.254.169.254'
- open_url = urllib2.urlopen(url, timeout=20)
- except Exception as err:
- print >> sys.stderr, 'No response from %s: %s' % (url, err)
- return smf_include.SMF_EXIT_ERR_FATAL
+ parser = ConfigParser.ConfigParser()
+ parser.read('/etc/cloudbase-init.conf')
+ if not parser.has_option('DEFAULT', 'config_drive'):
+ try:
+ if parser.has_option('DEFAULT', 'metadata_base_url'):
+ url = parser.get('DEFAULT', 'metadata_base_url')
+ else:
+ url = 'http://169.254.169.254'
+ open_url = urllib2.urlopen(url, timeout=20)
+ except Exception as err:
+ print >> sys.stderr, 'No response from %s: %s' % (url, err)
+ return smf_include.SMF_EXIT_ERR_FATAL
# Initiate cloudbase-init service
- cmd_str = "/usr/bin/cloudbase-init --debug"
- smf_include.smf_subprocess(cmd_str)
+ cmd_str = "/usr/bin/cloudbase-init"
+ subprocess.call(cmd_str, stdout=sys.stdout, stderr=sys.stderr)
if __name__ == "__main__":
os.putenv("LC_ALL", "C")
--- a/components/openstack/cloudbase-init/files/cloudbase-init.conf Thu Mar 23 23:22:55 2017 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-[DEFAULT]
-plugins=cloudbaseinit.plugins.common.userdata.UserDataPlugin
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/cloudbase-init/files/cloudbase-init.stencil Fri Mar 24 14:28:46 2017 -0600
@@ -0,0 +1,14 @@
+# This file is generated via SMF, and configuration should be changed by
+# setting or updating properties in the cloudbase-init:default service:instance.
+# Any modifications to this file will be overwritten on the next start of the
+# cloudbase-init.
+#
+# For example to turn on debugging do the following :
+# svccfg -s cloudbase-init:default setprop config/debug = boolean: true
+# svcadm refresh cloudbase-init
+
+[DEFAULT]
+; add any properties and their values that are in the config property group.
+$%/config/(.*)/ {
+$%1 = $%{config/$%1}
+}
--- a/components/openstack/cloudbase-init/files/cloudbase-init.xml Thu Mar 23 23:22:55 2017 -0700
+++ b/components/openstack/cloudbase-init/files/cloudbase-init.xml Fri Mar 24 14:28:46 2017 -0600
@@ -1,7 +1,7 @@
<?xml version="1.0" ?>
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
<!--
- Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
@@ -30,19 +30,21 @@
<service_fmri value='svc:/milestone/network:default' />
</dependency>
- <logfile_attributes permissions='600'/>
+ <instance name='default' enabled='true'>
+ <logfile_attributes permissions='600'/>
+
+ <exec_method timeout_seconds="0" type="method" name="start"
+ exec="/lib/svc/method/cloudbase-init %m">
+ </exec_method>
- <exec_method timeout_seconds="0" type="method" name="start"
- exec="/lib/svc/method/cloudbase-init %m">
- </exec_method>
- <exec_method timeout_seconds="30" type="method" name="stop"
- exec=":kill"/>
+ <!-- This is a transient service, so nothing to stop -->
+ <exec_method timeout_seconds="0" type="method" name="stop"
+ exec=":true"/>
- <property_group name='startd' type='framework'>
- <propval name='duration' type='astring' value='transient' />
- </property_group>
+ <property_group name='startd' type='framework'>
+ <propval name='duration' type='astring' value='transient' />
+ </property_group>
- <instance name='default' enabled='true'>
<!-- to start/stop/refresh the service -->
<property_group name='general' type='framework'>
<propval name='action_authorization' type='astring'
@@ -50,6 +52,22 @@
<propval name='value_authorization' type='astring'
value='solaris.smf.value.cloudbase-init' />
</property_group>
+
+ <property_group name='cloudbase_stencil' type='configfile'>
+ <propval name='path' type='astring'
+ value='/etc/cloudbase-init.conf' />
+ <propval name='stencil' type='astring'
+ value='cloudbase-init.stencil' />
+ <propval name='mode' type='astring' value='0444' />
+ <propval name='owner' type='astring' value='root' />
+ <propval name='group' type='astring' value='bin' />
+ </property_group>
+
+ <property_group name='config' type='application' >
+ <propval name='plugins' type='astring'
+ value='cloudbaseinit.plugins.common.userdata.UserDataPlugin' />
+ <propval name='debug' type='boolean' value='false' />
+ </property_group>
</instance>
<template>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/cloudbase-init/files/solaris.py Fri Mar 24 14:28:46 2017 -0600
@@ -0,0 +1,138 @@
+# Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import itertools
+import os
+import shutil
+import subprocess
+
+from cloudbaseinit.metadata.services.osconfigdrive import base
+from oslo_config import cfg
+from oslo_log import log as oslo_logging
+
+opts = [
+ cfg.StrOpt('tar', default='/usr/bin/tar',
+ help='path to "tar", used to extract ISO ConfigDrive '
+ 'files'),
+]
+CONF = cfg.CONF
+CONF.register_opts(opts)
+
+LOG = oslo_logging.getLogger(__name__)
+
+
+class SolarisConfigDriveManager(base.BaseConfigDriveManager):
+
+ def __init__(self):
+ super(SolarisConfigDriveManager, self).__init__()
+
+ def _config_drive_iso_cdrom(self):
+ """ Currently we support iso_cdrom because this is the closest "set" to
+ what we can currently get into the zone. We cannot push a file into
+ the zone without several complicated steps that would be prone to
+ breakage. So we are bringing in the ISO as a device in the zone. This
+ device can then be mounted. The way we present the device to the zone
+ is a read only cdrom like device so we will mount it as a "cdrom".
+ """
+ LOG.debug("self.target_path = %s" % self.target_path)
+ mountdir = '/root/cbi_cdrom'
+ try:
+ # The device is currently hard coded to c1d1p0
+ mounted = False
+ if not os.path.exists(mountdir):
+ os.mkdir(mountdir, 0500)
+
+ subprocess.check_call(['/usr/sbin/mount', '-F', 'hsfs',
+ '/dev/dsk/c1d1p0', mountdir])
+
+ mounted = True
+ chkfile = os.path.join(mountdir, 'openstack/latest/meta_data.json')
+ if os.path.exists(chkfile):
+ os.rmdir(self.target_path)
+ os.mkdir(self.target_path, 0500)
+ copycmd = ['/usr/bin/cp', '-rp']
+ for d in os.listdir(mountdir):
+ copycmd.append(mountdir + '/' + d)
+
+ copycmd.append(self.target_path)
+ subprocess.check_call(copycmd)
+ else:
+ return False
+ except Exception as ex:
+ LOG.error("Get config drive data failed: %s" % ex)
+ return False
+ finally:
+ if mounted:
+ subprocess.call(['/usr/sbin/umount', mountdir])
+ os.rmdir(mountdir)
+ # Set the property to complete the mount/copy/umount process and
+ # release the BUILD status in the controller.
+ subprocess.call(['/usr/sbin/svccfg', '-s',
+ 'cloudbase-init:default', 'setprop',
+ 'configdrive/copydone', '=', 'true'])
+
+ return True
+
+ def _config_drive_iso_hdd(self):
+ """ Currently not supported
+ """
+ LOG.error("iso/hdd is currently not supported")
+ return False
+
+ def _config_drive_iso_partition(self):
+ """ Currently not supported
+ """
+ LOG.error("iso/partition is currently not supported")
+ return False
+
+ def _config_drive_vfat_cdrom(self):
+ """ Currently not supported
+ """
+ LOG.error("vfat/cdrom is currently not supported")
+ return False
+
+ def _config_drive_vfat_hdd(self):
+ """ Currently not supported
+ """
+ LOG.error("vfat/hdd is currently not supported")
+ return False
+
+ def _config_drive_vfat_partition(self):
+ """ Currently not supported
+ """
+ LOG.error("vfat/partition is currently not supported")
+ return False
+
+ def get_config_drive_files(self, searched_types=None,
+ searched_locations=None):
+ # The problem we face is that the type and locations are not really
+ # relevant because we do not literally inject the config drive file
+ # into the zone. We provide an iso as a device to the zone which can
+ # then be mounted as a cdrom (readonly device) and then the data
+ # extracted from there. So looping through like this isn't really
+ # going to buy much, but we get simply write those methods as not
+ # supported yet.
+ #
+ # A lot of this will change once we can truly inject files into the
+ # zone.
+ for cd_type, cd_location in itertools.product(searched_types,
+ searched_locations):
+ LOG.info('Looking for Config Drive %(type)s in %(location)s',
+ {"type": cd_type, "location": cd_location})
+ find_drive = getattr(self, "_config_drive_" + cd_type + '_' +
+ cd_location)
+ if find_drive():
+ return True
+
+ return False
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openstack/cloudbase-init/patches/osconfigdrive.patch Fri Mar 24 14:28:46 2017 -0600
@@ -0,0 +1,17 @@
+In house patch to add the sunos5 platform to the confidrive portion of
+cloudbase-init.
+
+This patch is suitable for upstream submission.
+
+--- cloudbase-init-0.9.9/cloudbaseinit/metadata/services/osconfigdrive/factory.py.~1~ 2016-11-08 10:35:08.131876634 +0000
++++ cloudbase-init-0.9.9/cloudbaseinit/metadata/services/osconfigdrive/factory.py 2016-11-08 10:35:41.632217784 +0000
+@@ -21,6 +21,8 @@ def get_config_drive_manager():
+ class_paths = {
+ 'win32': 'cloudbaseinit.metadata.services.osconfigdrive.windows.'
+ 'WindowsConfigDriveManager',
++ 'sunos5': 'cloudbaseinit.metadata.services.osconfigdrive.solaris.'
++ 'SolarisConfigDriveManager',
+ }
+
+ class_path = class_paths.get(sys.platform)
+
--- a/components/openstack/nova/Makefile Thu Mar 23 23:22:55 2017 -0700
+++ b/components/openstack/nova/Makefile Fri Mar 24 14:28:46 2017 -0600
@@ -20,7 +20,7 @@
#
#
-# Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
#
include ../../../make-rules/shared-macros.mk
@@ -114,6 +114,7 @@
REQUIRED_PACKAGES += library/python/sqlalchemy-27
REQUIRED_PACKAGES += library/python/sqlalchemy-migrate-27
REQUIRED_PACKAGES += library/python/webob-27
+REQUIRED_PACKAGES += media/cdrtools
REQUIRED_PACKAGES += system/core-os
REQUIRED_PACKAGES += system/file-system/zfs
REQUIRED_PACKAGES += system/library/storage/suri
--- a/components/openstack/nova/files/nova-compute Thu Mar 23 23:22:55 2017 -0700
+++ b/components/openstack/nova/files/nova-compute Fri Mar 24 14:28:46 2017 -0600
@@ -1,6 +1,6 @@
#!/usr/bin/python2.7
-# Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@@ -86,6 +86,15 @@
check_call(['/usr/bin/pfexec', '/usr/bin/chown', 'nova:nova',
imagecache_path])
+ configdrive_path = '/var/share/nova/configdrives'
+ if not os.path.exists(configdrive_path):
+ ret = _create_dataset(configdrive_path, "VARSHARE/nova/configdrives")
+ if ret != smf_include.SMF_EXIT_OK:
+ return ret
+
+ check_call(['/usr/bin/pfexec', '/usr/bin/chown', 'nova:nova',
+ configdrive_path])
+
smf_include.smf_subprocess("/usr/bin/pfexec /usr/lib/nova/nova-compute")
if __name__ == "__main__":
--- a/components/openstack/nova/files/nova.conf Thu Mar 23 23:22:55 2017 -0700
+++ b/components/openstack/nova/files/nova.conf Fri Mar 24 14:28:46 2017 -0600
@@ -1925,7 +1925,7 @@
# Name and optionally path of the tool used for ISO image creation (string
# value)
-#mkisofs_cmd = genisoimage
+mkisofs_cmd = /usr/bin/mkisofs
# Number of seconds to wait between runs of the image cache manager. Set to -1
# to disable. Setting this to 0 will run at the default rate. (integer value)
--- a/components/openstack/nova/files/solariszones/driver.py Thu Mar 23 23:22:55 2017 -0700
+++ b/components/openstack/nova/files/solariszones/driver.py Fri Mar 24 14:28:46 2017 -0600
@@ -51,6 +51,7 @@
from oslo_utils import versionutils
from passlib.hash import sha256_crypt
+from nova.api.metadata import base as instance_metadata
from nova.api.metadata import password
from nova.compute import arch
from nova.compute import hv_type
@@ -72,6 +73,7 @@
from nova.objects import flavor as flavor_obj
from nova.objects import migrate_data as migrate_data_obj
from nova import utils
+from nova.virt import configdrive
from nova.virt import driver
from nova.virt import event as virtevent
from nova.virt import hardware
@@ -1478,6 +1480,46 @@
[zonemgr.Property("storage", listvalue=[suri])],
ignore_exists=True)
+ def _get_configdrive_path(self, instance):
+ cd_name = "config_drive-" + instance['name']
+
+ return os.path.join("/var/share/nova/configdrives", cd_name)
+
+ def _set_configdrive(self, name, instance, sc_dir):
+ """Set the configdrive device"""
+ zone = self._get_zone_by_name(name)
+ if zone is None:
+ raise exception.InstanceNotFound(instance_id=name)
+
+ cd_path = self._get_configdrive_path(instance)
+
+ with ZoneConfig(zone) as zc:
+ storagepath = "file://root:root@" + cd_path
+ zc.addresource("device", [zonemgr.Property("storage", storagepath),
+ zonemgr.Property("id", "1")])
+
+ fp = os.path.join(sc_dir, "config_drive.xml")
+ tree = sysconfig.create_config_drive()
+ sysconfig.create_sc_profile(fp, tree)
+
+ def _unset_configdrive(self, name, instance):
+ """ Remove the configdrive device from the zone"""
+ zone = self._get_zone_by_name(name)
+ if zone is None:
+ raise exception.InstanceNotFound(instance_id=name)
+
+ cd_path = self._get_configdrive_path(instance)
+
+ with ZoneConfig(zone) as zc:
+ storagepath = "file://root:root@" + cd_path
+ zc.removeresources("device",
+ [zonemgr.Property("storage", storagepath)])
+
+ if zone.state == ZONE_STATE_RUNNING:
+ zone.apply()
+
+ os.remove(cd_path)
+
def _set_num_cpu(self, name, vcpus, brand):
"""Set number of VCPUs in a Solaris Zone configuration."""
zone = self._get_zone_by_name(name)
@@ -1895,6 +1937,8 @@
self._set_memory_cap(name, instance['memory_mb'], brand)
self._set_network(context, name, instance, network_info, brand,
sc_dir)
+ if configdrive.required_by(instance):
+ self._set_configdrive(name, instance, sc_dir)
except Exception as ex:
reason = zonemgr_strerror(ex)
LOG.exception(_("Unable to create configuration for instance '%s' "
@@ -2199,6 +2243,47 @@
"via zonemgr(3RAD): %s") % (name, reason))
raise
+ def _waitfor_copydone(self, name):
+ failcount = 0
+ cbi_service = 'svc:/application/cloudbase-init:default'
+ cbi_state = None
+ end_states = ['online', 'degraded', 'maintenance', 'disabled']
+ while cbi_state not in end_states:
+ try:
+ cbi_state, err = utils.execute('/usr/sbin/zlogin', '-S', name,
+ '/usr/bin/svcs', '-H', '-o',
+ 'state', cbi_service)
+ cbi_state = cbi_state.strip()
+ except processutils.ProcessExecutionError:
+ # If it has been two minutes and the zone is still not able to
+ # return any kind of state, and the zlogin is failing, then
+ # simply get out of the process of protecting the config-drive
+ # but leave it attached to the zone.
+ if failcount > 120:
+ return False
+
+ failcount = failcount + 1
+ greenthread.sleep(1)
+ continue
+
+ if cbi_state == "disabled" and cbi_state in end_states:
+ out, err = utils.execute('/usr/sbin/zlogin', '-S', name,
+ '/usr/bin/svcprop', '-p',
+ 'general/enabled', cbi_service)
+ out = out.strip()
+ if out == "true":
+ end_states.remove('disabled')
+
+ if cbi_state == "offline*":
+ out, err = utils.execute('/usr/sbin/zlogin', '-S', name,
+ '/usr/bin/svcprop', '-C', '-p',
+ 'configdrive/copydone', cbi_service)
+ out = out.strip()
+ if out == "true":
+ break
+
+ return True
+
def spawn(self, context, instance, image_meta, injected_files,
admin_password, network_info=None, block_device_info=None):
"""Create a new instance/VM/domain on the virtualization platform.
@@ -2270,6 +2355,18 @@
dir=CONF.state_path)
os.chmod(sc_dir, 0755)
+ # Create the configdrive if required.
+ if configdrive.required_by(instance):
+ instance_md = instance_metadata.InstanceMetadata(
+ instance,
+ content=injected_files)
+ with configdrive.ConfigDriveBuilder(instance_md=instance_md) as cd:
+ cd_path = self._get_configdrive_path(instance)
+ try:
+ cd.make_drive(cd_path)
+ except Exception as e:
+ LOG.info(_("Failed to create config drive '%s'" % cd_path))
+
try:
self._create_config(context, instance, network_info,
connection_info, sc_dir, admin_password)
@@ -2281,6 +2378,10 @@
instance, entry['mount_device'])
self._power_on(instance, network_info)
+ if configdrive.required_by(instance):
+ unset = self._waitfor_copydone(name)
+ if unset:
+ self._unset_configdrive(name, instance)
except Exception as ex:
reason = zonemgr_strerror(ex)
LOG.exception(_("Unable to spawn instance '%s' via zonemgr(3RAD): "
@@ -2439,6 +2540,14 @@
self._uninstall(instance)
if self._get_state(zone) == power_state.NOSTATE:
self._delete_config(instance)
+ if configdrive.required_by(instance):
+ # Make sure that we don't leave any dirt around.
+ cd_path = self._get_configdrive_path(instance)
+ try:
+ os.remove(cd_path)
+ except OSError:
+ pass
+
except Exception as ex:
reason = zonemgr_strerror(ex)
LOG.warning(_("Unable to destroy instance '%s' via zonemgr(3RAD): "
--- a/components/openstack/nova/files/solariszones/sysconfig.py Thu Mar 23 23:22:55 2017 -0700
+++ b/components/openstack/nova/files/solariszones/sysconfig.py Fri Mar 24 14:28:46 2017 -0600
@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
-# Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@@ -225,6 +225,41 @@
return svcbundle
+def create_config_drive():
+ """ return an etree object representing the configdrive options for
+ cloudbase-init
+ """
+ svcbundle = etree.Element("service_bundle", type="profile",
+ name="cloudbase-init")
+ service = etree.SubElement(svcbundle, "service", version="1",
+ type="service",
+ name="application/cloudbase-init")
+ instance = etree.SubElement(service, "instance", name="default",
+ enabled="true")
+ pg = etree.SubElement(instance, "property_group", type="application",
+ name="config")
+ etree.SubElement(pg, "propval", type="astring", name="config_drive_types",
+ value="iso")
+
+ etree.SubElement(pg, "propval", type="boolean", name="config_drive",
+ value="true")
+
+ etree.SubElement(pg, "propval", type="astring",
+ name="config_drive_locations", value="cdrom, hdd")
+
+ v = "cloudbaseinit.metadata.services.configdrive.ConfigDriveService"
+ etree.SubElement(pg, "propval", type="astring", name="metadata_services",
+ value=v)
+
+ mntpg = etree.SubElement(instance, "property_group", type="application",
+ name="configdrive")
+
+ etree.SubElement(mntpg, "propval", type="boolean", name="copydone",
+ value="false")
+
+ return svcbundle
+
+
def create_sc_profile(path, tree):
""" create a file containing the proper XML headers and encoding for a
given etree object
--- a/components/openstack/nova/nova.p5m Thu Mar 23 23:22:55 2017 -0700
+++ b/components/openstack/nova/nova.p5m Fri Mar 24 14:28:46 2017 -0600
@@ -20,7 +20,7 @@
#
#
-# Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
#
set name=pkg.fmri \
@@ -1007,6 +1007,9 @@
# force a dependency on package delivering chown(1)
depend type=require fmri=__TBD pkg.debug.depend.file=usr/bin/chown
+# force a dependency on package delivering mkisofs(8)
+depend type=require fmri=__TBD pkg.debug.depend.file=usr/bin/mkisofs
+
# force a dependency on package delivering archiveadm(1M)
depend type=require fmri=__TBD pkg.debug.depend.file=usr/sbin/archiveadm \
variant.debug.container=false