[node-patches] Change in ovirt-node[master]: Mechanism to detect changed runtime images at boot

fabiand at fedoraproject.org fabiand at fedoraproject.org
Fri Jun 20 18:15:02 UTC 2014


Fabian Deutsch has uploaded a new change for review.

Change subject: Mechanism to detect changed runtime images at boot
......................................................................

Mechanism to detect changed runtime images at boot

This patch adds a mechanism to detect if the current runtime image is
the same as during the last run.

This mechanism consist of several parts:
1. Reliable 'fingerprint' for an image
2. Safe way to store the image fingerprint
3. A helper to detect image changes and trigger a hook

Change-Id: I120326e25de0a0de3e117204777891591b91c604
Signed-off-by: Fabian Deutsch <fabiand at fedoraproject.org>
---
M ovirt-node.spec.in
A scripts/on-boot-runtime-image-check.py
M src/ovirt/node/config/defaults.py
M src/ovirt/node/utils/hooks.py
A src/ovirt/node/utils/image.py
M src/ovirt/node/utils/security.py
6 files changed, 198 insertions(+), 3 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-node refs/changes/05/29005/1

diff --git a/ovirt-node.spec.in b/ovirt-node.spec.in
index e1f0c48..9c816b8 100644
--- a/ovirt-node.spec.in
+++ b/ovirt-node.spec.in
@@ -433,6 +433,7 @@
 %{__install} -d -m0755 %{buildroot}%{_libexecdir}/ovirt-node/hooks/post-upgrade
 %{__install} -d -m0755 %{buildroot}%{_libexecdir}/ovirt-node/hooks/rollback
 %{__install} -d -m0755 %{buildroot}%{_libexecdir}/ovirt-node/hooks/on-boot
+%{__install} -d -m0755 %{buildroot}%{_libexecdir}/ovirt-node/hooks/on-changed-boot-image
 
 %if %{is_f16}
 # install libvirtd systemd service
diff --git a/scripts/on-boot-runtime-image-check.py b/scripts/on-boot-runtime-image-check.py
new file mode 100644
index 0000000..1aca4e5
--- /dev/null
+++ b/scripts/on-boot-runtime-image-check.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2014 Red Hat, Inc.
+# Written by Fabian Deutsch <fabiand at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.  A copy of the GNU General Public License is
+# also available at http://www.gnu.org/copyleft/gpl.html.
+
+"""
+A helper script which cheks the runtime boot image, and tries to detect changed
+images
+"""
+
+from ovirt.node import log
+from ovirt.node.utils import image, hooks
+from ovirt.node.config import defaults
+
+from ovirt.node import base
+
+LOGGER = log.getLogger(__name__)
+
+if __name__ == "__main__":
+    imagestate = defaults.RuntimeImageState()
+    has_changed = imagestate.test_and_set()
+
+    if has_changed:
+        hooks.emit("on-changed-boot-image")
diff --git a/src/ovirt/node/config/defaults.py b/src/ovirt/node/config/defaults.py
index 11a9e10..46d6553 100644
--- a/src/ovirt/node/config/defaults.py
+++ b/src/ovirt/node/config/defaults.py
@@ -22,7 +22,7 @@
 from ovirt.node.config.network import NicConfig
 from ovirt.node.exceptions import InvalidData
 from ovirt.node.utils import storage, process, fs, AugeasWrapper, console, \
-    system, firewall
+    system, firewall, image
 from ovirt.node.utils.fs import ShellVarFile, File
 from ovirt.node.utils.network import NIC, Bridges, Bonds
 from ovirt.node.utils.system import Bootloader
@@ -1682,3 +1682,49 @@
 
     def has_managed_ifnames(self):
         return True if self.retrieve()["managed_ifnames"] else False
+
+
+class RuntimeImageState(NodeConfigFileSection):
+    """Track informations about the image state
+    """
+    keys = ("RUNTIME_IMAGE_FINGERPRINT_LAST"
+            )
+
+    @NodeConfigFileSection.map_and_update_defaults_decorator
+    def update(self, fingerprint):
+        pass
+
+    def current(self):
+        """Return the fingerprint of the current runtime image
+        """
+        return image.current().fingerprint()
+
+    def previous(self):
+        """Return the fingerprint of the previous/other runtime image
+        """
+        return image.previous().fingerprint()
+
+    def stored(self):
+        """Return the stored fingerprint
+        """
+        return self.retrieve()["fingerprint"]
+
+    def is_different(self):
+        """Determin if the current runtime image is different
+        compared to the last time the config file was updated
+        """
+        return self.stored() != self.current()
+
+    def test_and_set(self):
+        """Check if the image state changed, and update to current
+
+        Returns:
+            True - if the state of the image changed (new image)
+        """
+        current = self.current()
+        is_different = self.is_different()
+
+        if is_different:
+            self.update(current)
+
+        return is_different
diff --git a/src/ovirt/node/utils/hooks.py b/src/ovirt/node/utils/hooks.py
index 6443041..97b8947 100644
--- a/src/ovirt/node/utils/hooks.py
+++ b/src/ovirt/node/utils/hooks.py
@@ -35,10 +35,26 @@
     beyond the normal install
     """
 
+    known = ["pre-upgrade", "post-upgrade", "rollback", "on-boot",
+             "on-changed-boot-image"]
+
+    legacy_hooks_directory = "/etc/ovirt-config-boot.d/"
+    hooks_path_tpl = "/usr/libexec/hooks/{name}"
+
     @staticmethod
     def post_auto_install():
-        hooks_directory = "/etc/ovirt-config-boot.d/"
-        Hooks.__run(hooks_directory)
+        Hooks.__run(Hooks.legacy_hooks_directory)
+
+    @staticmethod
+    def emit(name):
+        """Signal that a specific event appeared, and trigger the hook handlers
+
+        Args:
+            name: Name of the hook (bust be in Hooks.known)
+        """
+        assert name in Hooks.known
+        path = Hooks.hooks_path_tpl.format(name=name)
+        Hooks._run(path)
 
     @staticmethod
     def __run(hooks_directory):
diff --git a/src/ovirt/node/utils/image.py b/src/ovirt/node/utils/image.py
new file mode 100644
index 0000000..8dc890a
--- /dev/null
+++ b/src/ovirt/node/utils/image.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# image.py - Copyright (C) 2014 Red Hat, Inc.
+# Written by Fabian Deutsch <fabiand at redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA  02110-1301, USA.  A copy of the GNU General Public License is
+# also available at http://www.gnu.org/copyleft/gpl.html.
+
+"""
+Some convenience functions realted to the underlying image
+"""
+
+from ovirt.node import log
+from ovirt.node.utils import security, process
+from ovirt.node.utils.system import Filesystem
+import os
+
+from ovirt.node import base
+
+LOGGER = log.getLogger(__name__)
+
+
+def current():
+    """Return the current image in use
+    """
+    return Image("Root")
+
+
+def previous():
+    """Return the previous/backup image available
+    """
+    return Image("RootBackup")
+
+
+class Image(base.Base):
+    """A class providing access to the images available on the system
+    """
+    _checksum_algorithm = "sha256"
+
+    def __init__(self, label):
+        self._fs = Filesystem.from_label(label)
+
+    def mountpoint(self):
+        mps = self._fs.mountpoints()
+        assert mps
+        return mps[0]
+
+    def fingerprint(self):
+        """The "fingerprint" of an image can be used to distinguish two images
+        Or to identify an image.
+        """
+        imagefile = os.path.join(self.mountpoint(), "LiveOS", "squashfs.img")
+        squashinfo = process.check_output(["unsquashfs", "-s", imagefile])
+        return security.Checksum("sha256").for_data(squashinfo)
diff --git a/src/ovirt/node/utils/security.py b/src/ovirt/node/utils/security.py
index 0d9b7b7..b3dd0f8 100644
--- a/src/ovirt/node/utils/security.py
+++ b/src/ovirt/node/utils/security.py
@@ -23,6 +23,7 @@
 from ovirt.node.utils.fs import File
 import PAM as _PAM  # @UnresolvedImport
 import cracklib
+import hashlib
 import os.path
 import process
 
@@ -239,3 +240,27 @@
         for i in range(len(query_list)):
             resp.append((self._password, 0))
         return resp
+
+
+class Checksum(base.Base):
+    """Wrapper to calculate a checksum for a real file
+    Main reason for this class is to support an efficient way for large data
+    chunks.
+    """
+    _algorithm = None
+    algorithms = ["sha256", "sha512"]
+
+    def __init__(self, algo):
+        assert algo in self.algorithms
+        self._algorithm = algo
+
+    def for_data(self, data):
+        """Determin the checksum of some data chunk
+
+        >>> Checksum("sha256").for_data("bar")
+        'fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9'
+        """
+        hasher = {"sha256": hashlib.sha256,
+                  "sha512": hashlib.sha512}[self._algorithm]()
+        hasher.update(data)
+        return hasher.hexdigest()


-- 
To view, visit http://gerrit.ovirt.org/29005
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I120326e25de0a0de3e117204777891591b91c604
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-node
Gerrit-Branch: master
Gerrit-Owner: Fabian Deutsch <fabiand at fedoraproject.org>



More information about the node-patches mailing list