Simone Tiraboschi has uploaded a new change for review. Change subject: packaging: setup: saving the configuration on the shared domain ......................................................................
packaging: setup: saving the configuration on the shared domain Creating an additional volume. Packing the configuration files in a tar archive and copying it to the additional volume. Change-Id: I1096aa6eabf97ac9cf9b032d0ac57fd8f63c2924 Signed-off-by: Simone Tiraboschi <stira...@redhat.com> --- M src/ovirt_hosted_engine_setup/constants.py M src/ovirt_hosted_engine_setup/util.py M src/plugins/ovirt-hosted-engine-setup/core/answerfile.py M src/plugins/ovirt-hosted-engine-setup/core/conf.py M src/plugins/ovirt-hosted-engine-setup/ha/ha_notifications.py M src/plugins/ovirt-hosted-engine-setup/storage/__init__.py A src/plugins/ovirt-hosted-engine-setup/storage/heconf.py M src/plugins/ovirt-hosted-engine-setup/vm/boot_disk.py M src/plugins/ovirt-hosted-engine-setup/vm/image.py 9 files changed, 357 insertions(+), 51 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-hosted-engine-setup refs/changes/52/42052/1 diff --git a/src/ovirt_hosted_engine_setup/constants.py b/src/ovirt_hosted_engine_setup/constants.py index 091afb9..f0d9c11 100644 --- a/src/ovirt_hosted_engine_setup/constants.py +++ b/src/ovirt_hosted_engine_setup/constants.py @@ -290,6 +290,11 @@ ENGINE_HA_CONFDIR, 'broker.conf' ) + HECONFD_VERSION = 'version' + HECONFD_ANSWERFILE = 'fhanswers.conf' + HECONFD_HECONF = 'hosted-engine.conf' + HECONFD_BROKER_CONF = 'broker.conf' + HECONFD_VM_CONF = 'vm.conf' @util.export @@ -646,6 +651,11 @@ FORCE_CREATEVG = 'OVEHOSTED_ENGINE/forceCreateVG' + ANSWERFILE_CONTENT = 'OVEHOSTED_VM/storageAnswerFileContent' + HECONF_CONTENT = 'OVEHOSTED_VM/storageHEConfContent' + BROKER_CONF_CONTENT = 'OVEHOSTED_NOTIF/brokerConfContent' + VM_CONF_CONTENT = 'OVEHOSTED_NOTIF/vmConfContent' + @util.export @util.codegen @@ -925,6 +935,9 @@ VDSM_LIBVIRT_CONFIGURED = 'ohosted.vdsm.libvirt.configured' NODE_FILES_PERSIST_S = 'ohosted.node.files.persist.start' NODE_FILES_PERSIST_E = 'ohosted.node.files.persist.end' + CONF_VOLUME_AVAILABLE = 'ohosted.conf.volume.available' + BROKER_CONF_AVAIABLE = 'ohosted.notifications.broker.conf.available' + ANSWER_FILE_AVAIABLE = 'ohosted.notifications.answerfile.available' DIALOG_TITLES_S_VM = 'ohosted.dialog.titles.vm.start' DIALOG_TITLES_E_VM = 'ohosted.dialog.titles.vm.end' @@ -948,8 +961,10 @@ DEFAULT_SANLOCK_SERVICE = 'sanlock' DEFAULT_LOCKSPACE_NAME = 'hosted-engine' DEFAULT_IMAGE_DESC = 'Hosted Engine Image' + DEFAULT_CONF_IMAGE_DESC = 'Hosted Engine Configuration Image' DEFAULT_IMAGE_SIZE_GB = 25 # based on minimum requirements. DEFAULT_MEM_SIZE_MB = 4096 # based on minimum requirements. + DEFAULT_CONF_IMAGE_SIZE_GB = 1 DEFAULT_BOOT = 'disk' # boot device - drive C or cdrom or pxe DEFAULT_CDROM = '/dev/null' DEFAULT_BRIDGE_IF = 'em1' diff --git a/src/ovirt_hosted_engine_setup/util.py b/src/ovirt_hosted_engine_setup/util.py index f4b4301..b4b1715 100644 --- a/src/ovirt_hosted_engine_setup/util.py +++ b/src/ovirt_hosted_engine_setup/util.py @@ -21,6 +21,7 @@ """Utils.""" +import glob import os import random import re @@ -98,6 +99,35 @@ ) +def get_volume_path(type, sd_uuid, img_uuid, vol_uuid): + """ + Return path of the volume file inside the domain + """ + volume_path = ohostedcons.FileLocations.SD_MOUNT_PARENT_DIR + if type == 'glusterfs': + volume_path = os.path.join( + volume_path, + 'glusterSD', + ) + volume_path = os.path.join( + volume_path, + '*', + sd_uuid, + 'images', + img_uuid, + vol_uuid, + ) + volumes = glob.glob(volume_path) + if not volumes: + raise RuntimeError( + 'Path to volume {vol_uuid} not found in {root}'.format( + vol_uuid=vol_uuid, + root=ohostedcons.FileLocations.SD_MOUNT_PARENT_DIR, + ) + ) + return volumes[0] + + class VirtUserContext(object): """ Switch to vdsm:kvm user with provided umask diff --git a/src/plugins/ovirt-hosted-engine-setup/core/answerfile.py b/src/plugins/ovirt-hosted-engine-setup/core/answerfile.py index c28fcbe..4aa24c2 100644 --- a/src/plugins/ovirt-hosted-engine-setup/core/answerfile.py +++ b/src/plugins/ovirt-hosted-engine-setup/core/answerfile.py @@ -26,6 +26,9 @@ import os +from io import StringIO + + from otopi import common from otopi import constants as otopicons from otopi import plugin @@ -47,6 +50,24 @@ def __init__(self, context): super(Plugin, self).__init__(context=context) + def _generate_answers(self, f): + f.write('[environment:default]\n') + for c in ohostedcons.__dict__['__hosted_attrs__']: + for k in c.__dict__.values(): + if hasattr(k, '__hosted_attrs__'): + if k.__hosted_attrs__['answerfile']: + k = k.fget(None) + if k in self.environment: + v = self.environment[k] + f.write( + '%s=%s:%s\n' % ( + k, + common.typeName(v), + '\n'.join(v) if isinstance(v, list) + else v, + ) + ) + def _save_answers(self, name): self.logger.info( _("Generating answer file '{name}'").format( @@ -55,22 +76,7 @@ ) path = self.resolveFile(name) with open(path, 'w') as f: - f.write('[environment:default]\n') - for c in ohostedcons.__dict__['__hosted_attrs__']: - for k in c.__dict__.values(): - if hasattr(k, '__hosted_attrs__'): - if k.__hosted_attrs__['answerfile']: - k = k.fget(None) - if k in self.environment: - v = self.environment[k] - f.write( - '%s=%s:%s\n' % ( - k, - common.typeName(v), - '\n'.join(v) if isinstance(v, list) - else v, - ) - ) + self._generate_answers(f) if self.environment[ohostedcons.CoreEnv.NODE_SETUP]: try: ohostedutil.persist(path) @@ -98,6 +104,23 @@ self._answers = [] @plugin.event( + stage=plugin.Stages.STAGE_CLOSEUP, + after=ohostedcons.Stages.CONF_VOLUME_AVAILABLE, + name=ohostedcons.Stages.ANSWER_FILE_AVAIABLE, + ) + def _closeup(self): + # TODO: ensure to generate after latest env value modification + # otherwise that value will not be in the file copied to the engine VM + f = StringIO() + try: + self._generate_answers(f) + self.environment[ + ohostedcons.NotificationsEnv.ANSWERFILE_CONTENT + ] = f.getvalue() + finally: + f.close() + + @plugin.event( stage=plugin.Stages.STAGE_CLEANUP, priority=plugin.Stages.PRIORITY_LAST, ) diff --git a/src/plugins/ovirt-hosted-engine-setup/core/conf.py b/src/plugins/ovirt-hosted-engine-setup/core/conf.py index 1abd940..00df005 100644 --- a/src/plugins/ovirt-hosted-engine-setup/core/conf.py +++ b/src/plugins/ovirt-hosted-engine-setup/core/conf.py @@ -140,6 +140,9 @@ template=ohostedcons.FileLocations.OVIRT_HOSTED_ENGINE_TEMPLATE, subst=subst ) + self.environment[ + ohostedcons.StorageEnv.HECONF_CONTENT + ] = content with transaction.Transaction() as localtransaction: localtransaction.append( filetransaction.FileTransaction( diff --git a/src/plugins/ovirt-hosted-engine-setup/ha/ha_notifications.py b/src/plugins/ovirt-hosted-engine-setup/ha/ha_notifications.py index eaa89f7..d9c1a10 100644 --- a/src/plugins/ovirt-hosted-engine-setup/ha/ha_notifications.py +++ b/src/plugins/ovirt-hosted-engine-setup/ha/ha_notifications.py @@ -216,15 +216,21 @@ @plugin.event( stage=plugin.Stages.STAGE_MISC, condition=lambda self: self._enabled, + name=ohostedcons.Stages.BROKER_CONF_AVAIABLE, ) def _misc(self): f = StringIO.StringIO() try: self._cfg.write(f) + self.environment[ + ohostedcons.NotificationsEnv.BROKER_CONF_CONTENT + ] = f.getvalue() self.environment[otopicons.CoreEnv.MAIN_TRANSACTION].append( filetransaction.FileTransaction( name=self._conffile, - content=f.getvalue(), + content=self.environment[ + ohostedcons.NotificationsEnv.BROKER_CONF_CONTENT + ], modifiedList=self.environment[ otopicons.CoreEnv.MODIFIED_FILES ], diff --git a/src/plugins/ovirt-hosted-engine-setup/storage/__init__.py b/src/plugins/ovirt-hosted-engine-setup/storage/__init__.py index bc4a0b2..328650d 100644 --- a/src/plugins/ovirt-hosted-engine-setup/storage/__init__.py +++ b/src/plugins/ovirt-hosted-engine-setup/storage/__init__.py @@ -26,6 +26,7 @@ from . import blockd from . import glusterfs +from . import heconf from . import nfs from . import storage @@ -34,6 +35,7 @@ def createPlugins(context): blockd.Plugin(context=context) glusterfs.Plugin(context=context) + heconf.Plugin(context=context) nfs.Plugin(context=context) storage.Plugin(context=context) diff --git a/src/plugins/ovirt-hosted-engine-setup/storage/heconf.py b/src/plugins/ovirt-hosted-engine-setup/storage/heconf.py new file mode 100644 index 0000000..64ed1f8 --- /dev/null +++ b/src/plugins/ovirt-hosted-engine-setup/storage/heconf.py @@ -0,0 +1,248 @@ +# +# ovirt-hosted-engine-setup -- ovirt hosted engine setup +# Copyright (C) 2015 Red Hat, Inc. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# + + +""" +HEConf storage domain plugin. +""" + +import gettext +import os +import uuid +import tarfile +import tempfile + + +from io import StringIO + + +from otopi import plugin +from otopi import util + + +from ovirt_hosted_engine_setup import constants as ohostedcons +from ovirt_hosted_engine_setup import config as ohostedconfig +from ovirt_hosted_engine_setup import tasks +from ovirt_hosted_engine_setup import util as ohostedutil + + +def _(m): + return gettext.dgettext(message=m, domain='ovirt-hosted-engine-setup') + + +@util.export +class Plugin(plugin.PluginBase): + """ + Local storage plugin. + """ + + def __init__(self, context): + super(Plugin, self).__init__(context=context) + self.cli = None + self.waiter = None + + def _add_to_tar(self, tar, fname, content): + string = StringIO(content) + info = tarfile.TarInfo(name=fname) + info.size = len(string.buf) + tar.addfile(tarinfo=info, fileobj=string) + + @plugin.event( + stage=plugin.Stages.STAGE_INIT, + ) + def _init(self): + self.environment.setdefault( + ohostedcons.StorageEnv.CONF_IMAGE_SIZE_GB, + ohostedcons.Defaults.DEFAULT_CONF_IMAGE_SIZE_GB + ) + self.environment.setdefault( + ohostedcons.StorageEnv.CONF_IMG_UUID, + str(uuid.uuid4()) + ) + self.environment.setdefault( + ohostedcons.StorageEnv.CONF_VOL_UUID, + str(uuid.uuid4()) + ) + self.environment.setdefault( + ohostedcons.StorageEnv.CONF_IMAGE_DESC, + ohostedcons.Defaults.DEFAULT_CONF_IMAGE_DESC + ) + self.environment.setdefault( + ohostedcons.StorageEnv.ANSWERFILE_CONTENT, + None + ) + self.environment.setdefault( + ohostedcons.StorageEnv.HECONF_CONTENT, + None + ) + self.environment.setdefault( + ohostedcons.StorageEnv.BROKER_CONF_CONTENT, + None + ) + self.environment.setdefault( + ohostedcons.StorageEnv.VM_CONF_CONTENT, + None + ) + + @plugin.event( + stage=plugin.Stages.STAGE_MISC, + name=ohostedcons.Stages.CONF_VOLUME_AVAILABLE, + after=ohostedcons.Stages.VM_IMAGE_AVAILABLE, + condition=lambda self: not self.environment[ + ohostedcons.CoreEnv.IS_ADDITIONAL_HOST + ], + ) + def _misc_create_volume(self): + cli = self.environment[ohostedcons.VDSMEnv.VDS_CLI] + volFormat = ohostedcons.VolumeFormat.RAW_FORMAT + preallocate = ohostedcons.VolumeTypes.PREALLOCATED_VOL + + sdUUID = self.environment[ohostedcons.StorageEnv.SD_UUID] + spUUID = self.environment[ohostedcons.StorageEnv.SP_UUID] + imgUUID = self.environment[ohostedcons.StorageEnv.CONF_IMG_UUID] + volUUID = self.environment[ohostedcons.StorageEnv.CONF_VOL_UUID] + diskType = 2 + + # creates a volume on the storage (SPM verb) + status = cli.createVolume( + sdUUID, + spUUID, + imgUUID, + str( + int( + self.environment[ohostedcons.StorageEnv.CONF_IMAGE_SIZE_GB] + ) * pow(2, 30) + ), + volFormat, + preallocate, + diskType, + volUUID, + self.environment[ohostedcons.StorageEnv.CONF_IMAGE_DESC], + ) + + self.logger.debug(status) + if status['status']['code'] == 0: + self.logger.debug( + ( + 'Created configuration volume {newUUID}, request was:\n' + '- image: {imgUUID}\n' + '- volume: {volUUID}' + ).format( + newUUID=status['status']['message'], + imgUUID=imgUUID, + volUUID=volUUID, + ) + ) + else: + raise RuntimeError(status['status']['message']) + waiter = tasks.TaskWaiter(self.environment) + waiter.wait() + # Expose the image (e.g., activates the lv) on the host (HSM verb). + self.logger.debug('configuration volume: prepareImage') + response = cli.prepareImage( + spUUID, + sdUUID, + imgUUID, + volUUID + ) + self.logger.debug(response) + if response['status']['code'] != 0: + raise RuntimeError(response['status']['message']) + # TODO: add this volume to the engine to prevent misuse + + @plugin.event( + stage=plugin.Stages.STAGE_CLOSEUP, + after=( + ohostedcons.Stages.BROKER_CONF_AVAIABLE, + ohostedcons.Stages.ANSWER_FILE_AVAIABLE, + ohostedcons.Stages.SAVE_CONFIG, + ohostedcons.Stages.OS_INSTALLED, + ), + condition=lambda self: not self.environment[ + ohostedcons.CoreEnv.IS_ADDITIONAL_HOST + ] + ) + def _closeup_create_tar(self): + self.logger.info(_( + 'Saving hosted-engine configuration ' + 'on the shared storage domain' + )) + fd, _tmp_tar = tempfile.mkstemp( + suffix='.tar', + dir=self.environment[ohostedcons.CoreEnv.TEMPDIR], + ) + os.close(fd) + self.logger.debug('temp tar file: ' + _tmp_tar) + tar = tarfile.TarFile(name=_tmp_tar, mode='w') + self._add_to_tar( + tar, + ohostedcons.FileLocations.HECONFD_VERSION, + ohostedconfig.PACKAGE_VERSION, + ) + self._add_to_tar( + tar, + ohostedcons.FileLocations.HECONFD_ANSWERFILE, + self.environment[ + ohostedcons.StorageEnv.ANSWERFILE_CONTENT + ], + ) + self._add_to_tar( + tar, + ohostedcons.FileLocations.HECONFD_HECONF, + self.environment[ + ohostedcons.StorageEnv.HECONF_CONTENT + ], + ) + self._add_to_tar( + tar, + ohostedcons.FileLocations.HECONFD_BROKER_CONF, + self.environment[ + ohostedcons.StorageEnv.BROKER_CONF_CONTENT + ], + ) + self._add_to_tar( + tar, + ohostedcons.FileLocations.HECONFD_VM_CONF, + self.environment[ + ohostedcons.StorageEnv.VM_CONF_CONTENT + ], + ) + tar.close() + dest = ohostedutil.get_volume_path( + self.environment[ + ohostedcons.StorageEnv.DOMAIN_TYPE + ], + self.environment[ohostedcons.StorageEnv.SD_UUID], + self.environment[ohostedcons.StorageEnv.CONF_IMG_UUID], + self.environment[ohostedcons.StorageEnv.CONF_VOL_UUID] + + ) + self.execute( + ( + self.command.get('dd'), + 'if={source}'.format(source=_tmp_tar), + 'of={dest}'.format(dest=dest), + 'bs=4k' + ), + raiseOnError=True, + ) + os.unlink(_tmp_tar) + + +# vim: expandtab tabstop=4 shiftwidth=4 diff --git a/src/plugins/ovirt-hosted-engine-setup/vm/boot_disk.py b/src/plugins/ovirt-hosted-engine-setup/vm/boot_disk.py index e672d34..ce69ae8 100644 --- a/src/plugins/ovirt-hosted-engine-setup/vm/boot_disk.py +++ b/src/plugins/ovirt-hosted-engine-setup/vm/boot_disk.py @@ -43,6 +43,7 @@ from ovirt_hosted_engine_setup import constants as ohostedcons from ovirt_hosted_engine_setup import domains as ohosteddomains from ovirt_hosted_engine_setup.ovf import ovfenvelope +from ovirt_hosted_engine_setup import util as ohostedutil def _(m): @@ -67,35 +68,14 @@ """ Return path of the volume file inside the domain """ - volume_path = ohostedcons.FileLocations.SD_MOUNT_PARENT_DIR - if self._parent.environment[ - ohostedcons.StorageEnv.DOMAIN_TYPE - ] == 'glusterfs': - volume_path = os.path.join( - volume_path, - 'glusterSD', - ) - volume_path = os.path.join( - volume_path, - '*', + return ohostedutil.get_volume_path( + self._parent.environment[ + ohostedcons.StorageEnv.DOMAIN_TYPE + ], self._parent.environment[ohostedcons.StorageEnv.SD_UUID], - 'images', self._parent.environment[ohostedcons.StorageEnv.IMG_UUID], self._parent.environment[ohostedcons.StorageEnv.VOL_UUID] ) - volumes = glob.glob(volume_path) - if not volumes: - raise RuntimeError( - _( - 'Path to volume {vol_uuid} not found in {root}' - ).format( - vol_uuid=self._parent.environment[ - ohostedcons.StorageEnv.VOL_UUID - ], - root=ohostedcons.FileLocations.SD_MOUNT_PARENT_DIR, - ) - ) - return volumes[0] def _validate_volume(self): self._parent.logger.info( diff --git a/src/plugins/ovirt-hosted-engine-setup/vm/image.py b/src/plugins/ovirt-hosted-engine-setup/vm/image.py index 90d33f2..a3b6751 100644 --- a/src/plugins/ovirt-hosted-engine-setup/vm/image.py +++ b/src/plugins/ovirt-hosted-engine-setup/vm/image.py @@ -201,21 +201,20 @@ raise RuntimeError(vginfo['status']['message']) vgfree = int(vginfo['info']['vgfree']) available_gb = vgfree / pow(2, 30) - if int( - self.environment[ - ohostedcons.StorageEnv.IMAGE_SIZE_GB - ] - ) > available_gb: + required_size = int(self.environment[ + ohostedcons.StorageEnv.IMAGE_SIZE_GB + ]) + int(self.environment[ + ohostedcons.StorageEnv.CONF_IMAGE_SIZE_GB + ]) + if required_size > available_gb: raise ohosteddomains.InsufficientSpaceError( _( 'Error: the VG on block device has capacity of only ' '{available_gb} GiB while ' - '{image_gb} GiB is required for the image' + '{required_size} GiB is required for the image' ).format( available_gb=available_gb, - image_gb=self.environment[ - ohostedcons.StorageEnv.IMAGE_SIZE_GB - ], + required_size=required_size, ) ) -- To view, visit https://gerrit.ovirt.org/42052 To unsubscribe, visit https://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I1096aa6eabf97ac9cf9b032d0ac57fd8f63c2924 Gerrit-PatchSet: 1 Gerrit-Project: ovirt-hosted-engine-setup Gerrit-Branch: master Gerrit-Owner: Simone Tiraboschi <stira...@redhat.com> _______________________________________________ Engine-patches mailing list Engine-patches@ovirt.org http://lists.ovirt.org/mailman/listinfo/engine-patches