doc/snapper.xml:doc/snapper.xml.in
doc/snapperd.xml:doc/snapperd.xml.in
doc/snapper-configs.xml:doc/snapper-configs.xml.in
+ doc/snapper-zypp-plugin.xml:doc/snapper-zypp-plugin.xml.in
+ doc/snapper-zypp-plugin.conf.xml:doc/snapper-zypp-plugin.conf.xml.in
doc/pam_snapper.xml:doc/pam_snapper.xml.in
po/Makefile
testsuite/Makefile
#
EXTRA_DIST = sysconfig.snapper base.txt lvm.txt x11.txt snapper.logrotate \
- default-config org.opensuse.Snapper.conf org.opensuse.Snapper.service
+ default-config org.opensuse.Snapper.conf org.opensuse.Snapper.service \
+ zypp-plugin.conf
install-data-local:
install -D -m 644 snapper.logrotate $(DESTDIR)/etc/logrotate.d/snapper
install -D -m 644 x11.txt $(DESTDIR)/etc/snapper/filters/x11.txt
install -D -m 644 org.opensuse.Snapper.conf $(DESTDIR)/etc/dbus-1/system.d/org.opensuse.Snapper.conf
install -D -m 644 org.opensuse.Snapper.service $(DESTDIR)/usr/share/dbus-1/system-services/org.opensuse.Snapper.service
+ install -D -m 644 zypp-plugin.conf $(DESTDIR)/etc/snapper/zypp-plugin.conf
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<snapper-zypp-plugin-conf>
+
+ <!-- List of solvables. If the zypp commit transaction includes any solvable
+ listed below, snapper will create pre and post snapshots. If any of the
+ solvables is marked as important, the snapshots will also be marked as
+ important. The solvables names are matched with Unix shell-style
+ wildcards. -->
+ <solvables>
+ <solvable important="true">kernel-*</solvable>
+ <solvable important="true">glibc</solvable>
+ <solvable important="true">systemd</solvable>
+ <solvable important="true">udev</solvable>
+ <solvable>*</solvable>
+ </solvables>
+
+</snapper-zypp-plugin-conf>
snapper.xml
snapperd.xml
snapper-configs.xml
+snapper-zypp-plugin.xml
+snapper-zypp-plugin.conf.xml
pam_snapper.xml
snapper.8
snapperd.8
snapper-configs.5
+snapper-zypp-plugin.8
+snapper-zypp-plugin.conf.5
pam_snapper.8
snapper.html
snapperd.html
snapper-configs.html
+snapper-zypp-plugin.html
+snapper-zypp-plugin.conf.html
pam_snapper.html
# Makefile.am for snapper/doc
#
-man_MANS = snapper.8 snapperd.8 snapper-configs.5
+man_MANS = snapper.8 snapperd.8 snapper-configs.5 snapper-zypp-plugin.8 \
+ snapper-zypp-plugin.conf.5
if HAVE_PAM
man_MANS += pam_snapper.8
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<refentry id='snapper-zypp-plugin.conf5'>
+
+ <refentryinfo>
+ <date>2013-11-22</date>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>snapper-zypp-plugin.conf</refentrytitle>
+ <manvolnum>5</manvolnum>
+ <refmiscinfo class='date'>2013-11-22</refmiscinfo>
+ <refmiscinfo class='version'>@VERSION@</refmiscinfo>
+ <refmiscinfo class='manual'>Filesystem Snapshot Management</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>snapper-zypp-plugin.conf</refname>
+ <refpurpose>Configuration file for snapper-zypp-plugin</refpurpose>
+ </refnamediv>
+
+ <refsect1 id='description'>
+ <title>DESCRIPTION</title>
+ <para>The file <filename>/etc/snapper/zypp-plugin.conf</filename> contains
+ the configuation for snapper-zypp-plugin.</para>
+
+ <para>The file uses XML syntax. For the structure look at the provided
+ default config.</para>
+ </refsect1>
+
+ <refsect1 id='homepage'>
+ <title>HOMEPAGE</title>
+ <para><ulink url='http://snapper.io/'>http://snapper.io/</ulink></para>
+ </refsect1>
+
+ <refsect1 id='authors'>
+ <title>AUTHORS</title>
+ <para>Arvin Schnell <email>aschnell@suse.de</email></para>
+ </refsect1>
+
+ <refsect1 id='see_also'>
+ <title>SEE ALSO</title>
+ <para>
+ <citerefentry><refentrytitle>snapper-zypp-plugin</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<refentry id='snapper-zypp-plugin8'>
+
+ <refentryinfo>
+ <date>2013-11-22</date>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>snapper-zypp-plugin</refentrytitle>
+ <manvolnum>8</manvolnum>
+ <refmiscinfo class='date'>2013-11-22</refmiscinfo>
+ <refmiscinfo class='version'>@VERSION@</refmiscinfo>
+ <refmiscinfo class='manual'>Filesystem Snapshot Management</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname>snapper-zypp-plugin</refname>
+ <refpurpose>Snapper plugin for libzypp</refpurpose>
+ </refnamediv>
+
+ <refsect1 id='description'>
+ <title>DESCRIPTION</title>
+ <para>With the snapper-zypp-plugin, snapper can create snapshots whenever
+ libzypp, and thus e.g. zypper, installs, updates or removes packages.</para>
+ </refsect1>
+
+ <refsect1 id='files'>
+ <title>FILES</title>
+ <variablelist>
+ <varlistentry>
+ <term><filename>/etc/snapper/zypp-plugin.conf</filename></term>
+ <listitem>
+ <para>Configuration file.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
+ <refsect1 id='homepage'>
+ <title>HOMEPAGE</title>
+ <para><ulink url='http://snapper.io/'>http://snapper.io/</ulink></para>
+ </refsect1>
+
+ <refsect1 id='authors'>
+ <title>AUTHORS</title>
+ <para>Arvin Schnell <email>aschnell@suse.de</email></para>
+ </refsect1>
+
+ <refsect1 id='see_also'>
+ <title>SEE ALSO</title>
+ <para>
+ <citerefentry><refentrytitle>snapper-zypp-plugin.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>snapper</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>zypper</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
+-------------------------------------------------------------------
+Mon Nov 25 11:38:43 CET 2013 - aschnell@suse.de
+
+- allow to configure snapshotting from zypp-plugin depending on
+ package list (fate#316203)
+
-------------------------------------------------------------------
Mon Oct 21 10:24:43 CEST 2013 - aschnell@suse.de
from os import readlink, getppid
from os.path import basename
-from sys import stderr
+from fnmatch import fnmatch
+import logging
from dbus import SystemBus, Interface
+import xml.dom.minidom as minidom
+import xml.parsers.expat as expat
+import json
from zypp_plugin import Plugin
+
+class Solvable:
+
+ def __init__(self, name, important):
+ self.name = name
+ self.important = important
+
+ def __repr__(self):
+ return "name:%s important:%s" % (self.name, self.important)
+
+ def match(self, name):
+ return fnmatch(name, self.name)
+
+
+
+class Config:
+
+ def __init__(self):
+ self.solvables = []
+ self.loglevel = None
+ self.load_file("/etc/snapper/zypp-plugin.conf")
+
+
+ def load_file(self, filename):
+ try:
+ self.load_dom(minidom.parse(filename))
+ except IOError:
+ logging.error("failed to open %s" % filename)
+ except expat.ExpatError:
+ logging.error("failed to parse %s" % filename)
+ except:
+ logging.error("failed to load %s" % filename)
+
+
+ def load_dom(self, dom):
+ try:
+ for tmp1 in dom.getElementsByTagName("solvables"):
+ for tmp2 in tmp1.getElementsByTagName("solvable"):
+ self.solvables.append(Solvable(tmp2.childNodes[0].data,
+ tmp2.getAttribute("important") == "true"))
+ except:
+ pass
+
+
+
class MyPlugin(Plugin):
+ def __init__(self):
+ Plugin.__init__(self)
+ self.num1 = self.num2 = None
+ self.description = ""
+ self.cleanup = "number"
+ self.userdata = {}
+
+
+ def parse_userdata(self, s):
+ userdata = {}
+ for kv in s.split(","):
+ k, v = kv.split("=", 1)
+ k = k.strip()
+ if not k:
+ raise ValueError
+ userdata[k] = v.strip()
+ return userdata
+
+
+ def get_userdata(self, headers):
+ try:
+ return self.parse_userdata(headers['userdata'])
+ except KeyError:
+ pass
+ except ValueError:
+ logging.error("invalid userdata")
+ return {}
+
+
+ def get_solvables(self, body):
+ tmp = json.loads(body)
+ tsl = tmp["TransactionStepList"]
+ solvables = set()
+ for ts in tsl:
+ solvables.add(ts["solvable"]["n"])
+ return solvables
+
+
+ def match_solvables(self, names):
+ found = important = False
+ for name in names:
+ for solvable in config.solvables:
+ if solvable.match(name):
+ found = True
+ important = important or solvable.important
+ if found and important:
+ return True, True
+ return found, important
+
+
+ def PLUGINBEGIN(self, headers, body):
+
+ logging.info("PLUGINBEGIN")
+
+ logging.debug("headers: %s" % headers)
+
+ self.description = "zypp(%s)" % basename(readlink("/proc/%d/exe" % getppid()))
+ self.userdata = self.get_userdata(headers)
+
+ self.ack()
+
+
+ def COMMITBEGIN(self, headers, body):
+
+ logging.info("COMMITBEGIN")
+
+ solvables = self.get_solvables(body)
+ logging.debug("solvables: %s" % solvables)
+
+ found, important = self.match_solvables(solvables)
+ logging.info("found: %s, important: %s" % (found, important))
+
+ if found or important:
+
+ self.userdata["important"] = "yes" if important else "no"
+
+ logging.info("creating pre snapshot")
+ self.num1 = snapper.CreatePreSnapshot("root", self.description, self.cleanup,
+ self.userdata)
+
+ self.ack()
- def parse_userdata(self, s):
- ud = {}
- for kv in s.split(","):
- k, v = kv.split("=", 1)
- k = k.strip()
- if not k:
- raise ValueError
- ud[k] = v.strip()
- return ud
+ def COMMITEND(self, headers, body):
- def PLUGINBEGIN(self, headers, body):
+ logging.info("COMMITEND")
- exe = basename(readlink("/proc/%d/exe" % getppid()))
+ if self.num1:
- self.userdata = {}
+ logging.info("creating post snapshot")
+ self.num2 = snapper.CreatePostSnapshot("root", self.num1, "", self.cleanup,
+ self.userdata)
- try:
- self.userdata = self.parse_userdata(headers['userdata'])
- except KeyError:
- pass
- except ValueError:
- stderr.write("invalid userdata")
+ self.ack()
- self.num1 = snapper.CreatePreSnapshot("root", "zypp(%s)" % exe, "number", self.userdata)
- self.ack()
+ def PLUGINEND(self, headers, body):
+ logging.info("PLUGINEND")
- def PLUGINEND(self, headers, body):
+ self.ack()
- self.num2 = snapper.CreatePostSnapshot("root", self.num1, "", "number", self.userdata)
- self.ack()
+config = Config()
bus = SystemBus()
%defattr(-,root,root)
%{prefix}/bin/snapper
%{prefix}/sbin/snapperd
-%doc %{_mandir}/*/snapper*.*
+%doc %{_mandir}/*/snapper.8*
+%doc %{_mandir}/*/snapperd.8*
+%doc %{_mandir}/*/snapper-configs.5*
%config(noreplace) %{_sysconfdir}/logrotate.d/snapper
/etc/cron.hourly/suse.de-snapper
/etc/cron.daily/suse.de-snapper
%package -n snapper-zypp-plugin
Requires: dbus-1-python
+Requires: libzypp(plugin:commit)
+Requires: python-xml
Requires: snapper = %version
Requires: zypp-plugin-python
-Requires: libzypp(plugin:commit)
Summary: A zypp commit plugin for calling snapper
Group: System/Packages
%files -n snapper-zypp-plugin
%defattr(-,root,root)
+%config(noreplace) %{_sysconfdir}/snapper/zypp-plugin.conf
%if 0%{?suse_version} < 1210
%dir /usr/lib/zypp
%dir /usr/lib/zypp/plugins
%dir /usr/lib/zypp/plugins/commit
%endif
/usr/lib/zypp/plugins/commit/snapper.py*
+%doc %{_mandir}/*/snapper-zypp-plugin.8*
+%doc %{_mandir}/*/snapper-zypp-plugin.conf.5*
%package -n grub-snapper-plugin
Requires: python
/%{_lib}/security/pam_snapper.so
%dir /usr/lib/pam_snapper
/usr/lib/pam_snapper/*.sh
-%doc %{_mandir}/*/pam_snapper*.*
+%doc %{_mandir}/*/pam_snapper.8*
%changelog