[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