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

Reply via email to