[Freeipa-devel] [PATCH 7 of 8] Backup system state in ipa-server-install

Mark McLoughlin markmc at redhat.com
Fri Jan 11 12:00:44 UTC 2008


# HG changeset patch
# User Mark McLoughlin <markmc at redhat.com>
# Date 1200049593 0
# Node ID 8640eee04855769ce8d0592e0fd7580e63d81dcf
# Parent  dbe13997b7a29237a134d0c23f6e34503e91898d
Backup system state in ipa-server-install

This patch adds a sysrestore module which allows ipa-server-install
code to backup any system state so that it can be restored again
with e.g. ipa-server-install --uninstall.

The idea is that any files ipa-server-install modifies gets backed
up to /var/cache/ipa/sysrestore/ while any "meta" state, like
whether a service is enabled with chkconfig, is saved to
/var/cache/ipa/sysrestore.state.

Signed-off-by: Mark McLoughlin <markmc at redhat.com>

diff -r dbe13997b7a2 -r 8640eee04855 Makefile
--- a/Makefile	Fri Jan 11 10:36:25 2008 +0000
+++ b/Makefile	Fri Jan 11 11:06:33 2008 +0000
@@ -57,12 +57,12 @@ all: bootstrap-autogen
 	done
 
 bootstrap-autogen:
-	cd ipa-server; if [ ! -e Makefile ]; then ./autogen.sh --prefix=/usr --sysconfdir=/etc --libdir=$(LIBDIR); fi
-	cd ipa-client; if [ ! -e Makefile ]; then ./autogen.sh --prefix=/usr --sysconfdir=/etc --libdir=$(LIBDIR); fi
+	cd ipa-server; if [ ! -e Makefile ]; then ./autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --libdir=$(LIBDIR); fi
+	cd ipa-client; if [ ! -e Makefile ]; then ./autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --libdir=$(LIBDIR); fi
 
 autogen:
-	cd ipa-server; ./autogen.sh --prefix=/usr --sysconfdir=/etc --libdir=$(LIBDIR)
-	cd ipa-client; ./autogen.sh --prefix=/usr --sysconfdir=/etc --libdir=$(LIBDIR)
+	cd ipa-server; ./autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --libdir=$(LIBDIR)
+	cd ipa-client; ./autogen.sh --prefix=/usr --sysconfdir=/etc --localstatedir=/var --libdir=$(LIBDIR)
 
 configure:
 	cd ipa-server; ./configure --prefix=/usr --sysconfdir=/etc
diff -r dbe13997b7a2 -r 8640eee04855 ipa-server/Makefile.am
--- a/ipa-server/Makefile.am	Fri Jan 11 10:36:25 2008 +0000
+++ b/ipa-server/Makefile.am	Fri Jan 11 11:06:33 2008 +0000
@@ -12,6 +12,13 @@ SUBDIRS =			\
 	ipa-slapi-plugins	\
 	xmlrpc-server		\
 	$(NULL)
+
+install-exec-local:
+	mkdir -p $(DESTDIR)$(localstatedir)/cache/ipa/sysrestore
+
+uninstall-local:
+	rmdir $(DESTDIR)$(localstatedir)/cache/watercooler/sys
+	rmdir $(DESTDIR)$(localstatedir)/cache/watercooler
 
 EXTRA_DIST =			\
 	ipa-server.spec		\
diff -r dbe13997b7a2 -r 8640eee04855 ipa-server/ipa-install/ipa-server-install
--- a/ipa-server/ipa-install/ipa-server-install	Fri Jan 11 10:36:25 2008 +0000
+++ b/ipa-server/ipa-install/ipa-server-install	Fri Jan 11 11:06:33 2008 +0000
@@ -49,6 +49,7 @@ import ipaserver.webguiinstance
 import ipaserver.webguiinstance
 
 from ipaserver import service
+from ipaserver import sysrestore
 from ipaserver.installutils import *
 
 from ipa.ipautil import *
@@ -167,6 +168,7 @@ def read_ip_address(host_name):
             continue
 
         print "Adding ["+ip+" "+host_name+"] to your /etc/hosts file"
+        sysrestore.backup_file("/etc/hosts")
         hosts_fd = open('/etc/hosts', 'r+')
         hosts_fd.seek(0, 2)
         hosts_fd.write(ip+'\t'+host_name+' '+host_name[:host_name.find('.')]+'\n')
@@ -420,6 +422,7 @@ def main():
     ds.change_admin_password(admin_password)
 
     # Create the config file
+    sysrestore.backup_file("/etc/ipa/ipa.conf")
     fd = open("/etc/ipa/ipa.conf", "w")
     fd.write("[defaults]\n")
     fd.write("server=" + host_name + "\n")
diff -r dbe13997b7a2 -r 8640eee04855 ipa-server/ipa-server.spec
--- a/ipa-server/ipa-server.spec	Fri Jan 11 10:36:25 2008 +0000
+++ b/ipa-server/ipa-server.spec	Fri Jan 11 11:06:33 2008 +0000
@@ -48,7 +48,7 @@ Ipa is a server for identity, policy, an
 
 %prep
 %setup -q
-./configure --prefix=%{buildroot}/usr --libdir=%{buildroot}/%{_libdir} --sysconfdir=%{buildroot}/etc
+./configure --prefix=%{buildroot}/usr --libdir=%{buildroot}/%{_libdir} --sysconfdir=%{buildroot}/etc --localstatedir=%{buildroot}/var
 
 %build
 
@@ -106,6 +106,7 @@ fi
 %attr(755,root,root) %{plugin_dir}/libipa-memberof-plugin.so
 %attr(755,root,root) %{plugin_dir}/libipa-dna-plugin.so
 
+%dir %{_localstatedir}/cache/ipa
 
 %changelog
 * Fri Dec 21 2007 Karl MacMillan <kmacmill at redhat.com> - 0.6.0-1
diff -r dbe13997b7a2 -r 8640eee04855 ipa-server/ipa-server.spec.in
--- a/ipa-server/ipa-server.spec.in	Fri Jan 11 10:36:25 2008 +0000
+++ b/ipa-server/ipa-server.spec.in	Fri Jan 11 11:06:33 2008 +0000
@@ -48,7 +48,7 @@ Ipa is a server for identity, policy, an
 
 %prep
 %setup -q
-./configure --prefix=%{buildroot}/usr --libdir=%{buildroot}/%{_libdir} --sysconfdir=%{buildroot}/etc
+./configure --prefix=%{buildroot}/usr --libdir=%{buildroot}/%{_libdir} --sysconfdir=%{buildroot}/etc --localstatedir=%{buildroot}/var
 
 %build
 
@@ -107,6 +107,7 @@ fi
 %attr(755,root,root) %{plugin_dir}/libipa-memberof-plugin.so
 %attr(755,root,root) %{plugin_dir}/libipa-dna-plugin.so
 
+%dir %{_localstatedir}/cache/ipa
 
 %changelog
 * Fri Dec 21 2007 Karl MacMillan <kmacmill at redhat.com> - 0.6.0-1
diff -r dbe13997b7a2 -r 8640eee04855 ipa-server/ipaserver/Makefile.am
--- a/ipa-server/ipaserver/Makefile.am	Fri Jan 11 10:36:25 2008 +0000
+++ b/ipa-server/ipaserver/Makefile.am	Fri Jan 11 11:06:33 2008 +0000
@@ -14,6 +14,7 @@ app_PYTHON = 			\
 	installutils.py		\
 	replication.py		\
 	certs.py		\
+	sysrestore.py		\
 	$(NULL)
 
 EXTRA_DIST =			\
diff -r dbe13997b7a2 -r 8640eee04855 ipa-server/ipaserver/bindinstance.py
--- a/ipa-server/ipaserver/bindinstance.py	Fri Jan 11 10:36:25 2008 +0000
+++ b/ipa-server/ipaserver/bindinstance.py	Fri Jan 11 11:06:33 2008 +0000
@@ -25,6 +25,7 @@ import socket
 import socket
 
 import service
+import sysrestore
 from ipa import ipautil
 
 class BindInstance(service.Service):
@@ -72,6 +73,7 @@ class BindInstance(service.Service):
         self.__setup_named_conf()
 
         try:
+            self.backup_state("running", self.is_running())
             self.start()
         except:
             print "named service failed to start"
@@ -84,14 +86,15 @@ class BindInstance(service.Service):
                              REALM=self.realm)
 
     def __setup_zone(self):
+        self.backup_state("domain", self.domain)
         zone_txt = ipautil.template_file(ipautil.SHARE_DIR + "bind.zone.db.template", self.sub_dict)
+        sysrestore.backup_file('/var/named/'+self.domain+'.zone.db')
         zone_fd = open('/var/named/'+self.domain+'.zone.db', 'w')
         zone_fd.write(zone_txt)
         zone_fd.close()
 
     def __setup_named_conf(self):
-        if os.path.exists('/etc/named.conf'):
-            shutil.copy2('/etc/named.conf', '/etc/named.conf.ipabkp')
+        sysrestore.backup_file('/etc/named.conf')
         named_txt = ipautil.template_file(ipautil.SHARE_DIR + "bind.named.conf.template", self.sub_dict)
         named_fd = open('/etc/named.conf', 'w')
         named_fd.seek(0)
@@ -99,8 +102,7 @@ class BindInstance(service.Service):
         named_fd.write(named_txt)
         named_fd.close()
 
-        if os.path.exists('/etc/resolve.conf'):
-            shutil.copy2('/etc/resolve.conf', '/etc/resolv.conf.ipabkp')
+        sysrestore.backup_file('/etc/resolve.conf')
         resolve_txt = "search "+self.domain+"\nnameserver "+self.ip_address+"\n"
         resolve_fd = open('/etc/resolve.conf', 'w')
         resolve_fd.seek(0)
diff -r dbe13997b7a2 -r 8640eee04855 ipa-server/ipaserver/certs.py
--- a/ipa-server/ipaserver/certs.py	Fri Jan 11 10:36:25 2008 +0000
+++ b/ipa-server/ipaserver/certs.py	Fri Jan 11 11:06:33 2008 +0000
@@ -318,4 +318,17 @@ class CertDB(object):
         self.trust_root_cert(nickname)
         self.create_pin_file()
         self.export_ca_cert()
+
+    def backup_files(self):
+        sysrestore.backup_file(self.noise_fname)
+        sysrestore.backup_file(self.passwd_fname)
+        sysrestore.backup_file(self.certdb_fname)
+        sysrestore.backup_file(self.keydb_fname)
+        sysrestore.backup_file(self.secmod_fname)
+        sysrestore.backup_file(self.cacert_fname)
+        sysrestore.backup_file(self.pk12_fname)
+        sysrestore.backup_file(self.pin_fname)
+        sysrestore.backup_file(self.certreq_fname)
+        sysrestore.backup_file(self.certder_fname)
+
         
diff -r dbe13997b7a2 -r 8640eee04855 ipa-server/ipaserver/dsinstance.py
--- a/ipa-server/ipaserver/dsinstance.py	Fri Jan 11 10:36:25 2008 +0000
+++ b/ipa-server/ipaserver/dsinstance.py	Fri Jan 11 11:06:33 2008 +0000
@@ -154,9 +154,13 @@ class DsInstance(service.Service):
         self.step("initializing group membership",
                   self.__init_memberof)
 
-        self.step("configuring directory to start on boot", self.chkconfig_on)
+        self.step("configuring directory to start on boot", self.__enable)
 
         self.start_creation("Configuring directory server:")
+
+    def __enable(self):
+        self.backup_state("enabled", self.is_enabled())
+        self.chkconfig_on()
 
     def __setup_sub_dict(self):
         server_root = find_server_root()
@@ -166,10 +170,12 @@ class DsInstance(service.Service):
                              SERVER_ROOT=server_root, DOMAIN=self.domain)
 
     def __create_ds_user(self):
+        user_exists = True
 	try:
             pwd.getpwnam(self.ds_user)
             logging.debug("ds user %s exists" % self.ds_user)
 	except KeyError:
+            user_exists = False
             logging.debug("adding ds user %s" % self.ds_user)
             args = ["/usr/sbin/useradd", "-c", "DS System User", "-d", "/var/lib/dirsrv", "-M", "-r", "-s", "/sbin/nologin", self.ds_user]
             try:
@@ -178,7 +184,12 @@ class DsInstance(service.Service):
             except ipautil.CalledProcessError, e:
                 logging.critical("failed to add user %s" % e)
 
+        self.backup_state("user", self.ds_user)
+        self.backup_state("user_exists", user_exists)
+
     def __create_instance(self):
+        self.backup_state("running", self.is_running())
+        self.backup_state("serverid", self.serverid)
         inf_txt = ipautil.template_str(INF_TEMPLATE, self.sub_dict)
         logging.debug(inf_txt)
         inf_fd = ipautil.write_tmp_file(inf_txt)
diff -r dbe13997b7a2 -r 8640eee04855 ipa-server/ipaserver/httpinstance.py
--- a/ipa-server/ipaserver/httpinstance.py	Fri Jan 11 10:36:25 2008 +0000
+++ b/ipa-server/ipaserver/httpinstance.py	Fri Jan 11 11:06:33 2008 +0000
@@ -29,6 +29,7 @@ import shutil
 import shutil
 
 import service
+import sysrestore
 import certs
 import dsinstance
 import installutils
@@ -63,10 +64,18 @@ class HTTPInstance(service.Service):
         self.step("Setting up ssl", self.__setup_ssl)
         self.step("Setting up browser autoconfig", self.__setup_autoconfig)
         self.step("configuring SELinux for httpd", self.__selinux_config)
-        self.step("restarting httpd", self.restart)
-        self.step("configuring httpd to start on boot", self.chkconfig_on)
+        self.step("restarting httpd", self.__start)
+        self.step("configuring httpd to start on boot", self.__enable)
 
         self.start_creation("Configuring the web interface")
+
+    def __start(self):
+        self.backup_state("running", self.is_running())
+        self.restart()
+
+    def __enable(self):
+        self.backup_state("enabled", self.is_running())
+        self.chkconfig_on()
 
     def __selinux_config(self):
         selinux=0
@@ -79,6 +88,14 @@ class HTTPInstance(service.Service):
             pass
 
         if selinux:
+            try:
+                # returns e.g. "httpd_can_network_connect --> off"
+                (stdout, stderr) = ipautils.run(["/usr/sbin/getsebool",
+                                                 "httpd_can_network_connect"])
+                self.backup_state("httpd_can_network_connect", stdout.split()[2])
+            except:
+                pass
+
             # Allow apache to connect to the turbogears web gui
             # This can still fail even if selinux is enabled
             try:
@@ -96,6 +113,7 @@ class HTTPInstance(service.Service):
 
     def __configure_http(self):
         http_txt = ipautil.template_file(ipautil.SHARE_DIR + "ipa.conf", self.sub_dict)
+        sysrestore.backup_file("/etc/httpd/conf.d/ipa.conf")
         http_fd = open("/etc/httpd/conf.d/ipa.conf", "w")
         http_fd.write(http_txt)
         http_fd.close()                
@@ -103,9 +121,11 @@ class HTTPInstance(service.Service):
 
     def __disable_mod_ssl(self):
         if os.path.exists(SSL_CONF):
-            os.rename(SSL_CONF, "%s.moved_by_ipa" % SSL_CONF)
+            sysrestore.backup_file(SSL_CONF)
+            os.unlink(SSL_CONF)
 
     def __set_mod_nss_port(self):
+        sysrestore.backup_file(NSS_CONF)
         if installutils.update_file(NSS_CONF, '8443', '443') != 0:
             print "Updating %s failed." % NSS_CONF
 
diff -r dbe13997b7a2 -r 8640eee04855 ipa-server/ipaserver/krbinstance.py
--- a/ipa-server/ipaserver/krbinstance.py	Fri Jan 11 10:36:25 2008 +0000
+++ b/ipa-server/ipaserver/krbinstance.py	Fri Jan 11 11:06:33 2008 +0000
@@ -32,6 +32,7 @@ import shutil
 import shutil
 
 import service
+import sysrestore
 import installutils
 from ipa import ipautil
 from ipa import ipaerror
@@ -107,6 +108,7 @@ class KrbInstance(service.Service):
             logging.critical("Could not connect to DS")
             raise e
 
+        self.backup_state("running", self.is_running())
         try:
             self.stop()
         except:
@@ -115,7 +117,7 @@ class KrbInstance(service.Service):
 
     def __common_post_setup(self):
         self.step("starting the KDC", self.__start_instance)
-        self.step("configuring KDC to start on boot", self.chkconfig_on)
+        self.step("configuring KDC to start on boot", self.__enable)
         self.step("enabling and starting ipa-kpasswd", self.__enable_kpasswd)
 
     def create_instance(self, ds_user, realm_name, host_name, admin_password, master_password):
@@ -155,6 +157,7 @@ class KrbInstance(service.Service):
         self.start_creation("Configuring Kerberos KDC")
 
     def __copy_ldap_passwd(self, filename):
+        sysrestore.backup_file("/var/kerberos/krb5kdc/ldappwd")
         shutil.copy(filename, "/var/kerberos/krb5kdc/ldappwd")
         os.chmod("/var/kerberos/krb5kdc/ldappwd", 0600)
         
@@ -163,11 +166,16 @@ class KrbInstance(service.Service):
         hexpwd = ''
 	for x in self.kdc_password:
             hexpwd += (hex(ord(x))[2:])
+        sysrestore.backup_file("/var/kerberos/krb5kdc/ldappwd")
         pwd_fd = open("/var/kerberos/krb5kdc/ldappwd", "w")
         pwd_fd.write("uid=kdc,cn=sysaccounts,cn=etc,"+self.suffix+"#{HEX}"+hexpwd+"\n")
         pwd_fd.close()
         os.chmod("/var/kerberos/krb5kdc/ldappwd", 0600)
 
+    def __enable(self):
+        self.backup_state("enabled", self.is_enabled())
+        self.chkconfig_on()
+
     def __start_instance(self):
         try:
             self.start()
@@ -175,6 +183,8 @@ class KrbInstance(service.Service):
             logging.critical("krb5kdc service failed to start")
 
     def __enable_kpasswd(self):
+        sysrestore.backup_state("ipa-kpasswd", "enabled", service.is_enabled("ipa-kpasswd"))
+        sysrestore.backup_state("ipa-kpasswd", "running", service.is_running("ipa-kpasswd"))
         service.chkconfig_on("ipa-kpasswd")
         service.start("ipa-kpasswd")
 
@@ -265,6 +275,7 @@ class KrbInstance(service.Service):
     def __template_file(self, path):
         template = os.path.join(ipautil.SHARE_DIR, os.path.basename(path) + ".template")
         conf = ipautil.template_file(template, self.sub_dict)
+        sysrestore.backup_file(path)
         fd = open(path, "w+")
         fd.write(conf)
         fd.close()
@@ -337,8 +348,11 @@ class KrbInstance(service.Service):
     def __create_ds_keytab(self):
         ldap_principal = "ldap/" + self.fqdn + "@" + self.realm
         installutils.kadmin_addprinc(ldap_principal)
+
+        sysrestore.backup_file("/etc/dirsrv/ds.keytab")
         installutils.create_keytab("/etc/dirsrv/ds.keytab", ldap_principal)
 
+        sysrestore.backup_file("/etc/sysconfig/dirsrv")
         update_key_val_in_file("/etc/sysconfig/dirsrv", "export KRB5_KTNAME", "/etc/dirsrv/ds.keytab")
         pent = pwd.getpwnam(self.ds_user)
         os.chown("/etc/dirsrv/ds.keytab", pent.pw_uid, pent.pw_gid)
@@ -346,6 +360,8 @@ class KrbInstance(service.Service):
     def __create_host_keytab(self):
         host_principal = "host/" + self.fqdn + "@" + self.realm
         installutils.kadmin_addprinc(host_principal)
+
+        sysrestore.backup_file("/etc/krb5.keytab")
         installutils.create_keytab("/etc/krb5.keytab", host_principal)
 
         # Make sure access is strictly reserved to root only for now
@@ -354,8 +370,11 @@ class KrbInstance(service.Service):
 
     def __export_kadmin_changepw_keytab(self):
         installutils.kadmin_modprinc("kadmin/changepw", "+requires_preauth")
+
+        sysrestore.backup_file("/var/kerberos/krb5kdc/kpasswd.keytab")
         installutils.create_keytab("/var/kerberos/krb5kdc/kpasswd.keytab", "kadmin/changepw")
 
+        sysrestore.backup_file("/etc/sysconfig/ipa-kpasswd")
         update_key_val_in_file("/etc/sysconfig/ipa-kpasswd", "export KRB5_KTNAME", "/var/kerberos/krb5kdc/kpasswd.keytab")
         pent = pwd.getpwnam(self.ds_user)
         os.chown("/var/kerberos/krb5kdc/kpasswd.keytab", pent.pw_uid, pent.pw_gid)
diff -r dbe13997b7a2 -r 8640eee04855 ipa-server/ipaserver/ntpinstance.py
--- a/ipa-server/ipaserver/ntpinstance.py	Fri Jan 11 10:36:25 2008 +0000
+++ b/ipa-server/ipaserver/ntpinstance.py	Fri Jan 11 11:06:33 2008 +0000
@@ -20,6 +20,7 @@ import shutil
 import shutil
 
 import service
+import sysrestore
 from ipa import ipautil
 
 class NTPInstance(service.Service):
@@ -45,11 +46,19 @@ class NTPInstance(service.Service):
 
         ntp_conf = ipautil.template_file(ipautil.SHARE_DIR + "ntp.conf.server.template", sub_dict)
 
-        shutil.copy("/etc/ntp.conf", "/etc/ntp.conf.ipasave")
+        sysrestore.backup_file("/etc/ntp.conf")
 
         fd = open("/etc/ntp.conf", "w")
         fd.write(ntp_conf)
         fd.close()
+
+    def __start(self):
+        self.backup_state("running", self.is_running())
+        self.start()
+
+    def __enable(self):
+        self.backup_state("enabled", self.is_enabled())
+        self.chkconfig_on()
 
     def create_instance(self):
         self.step("writing configuration", self.__write_config)
@@ -57,7 +66,7 @@ class NTPInstance(service.Service):
         # we might consider setting the date manually using ntpd -qg in case
         # the current time is very far off.
 
-        self.step("starting ntpd", self.start)
-        self.step("configuring ntpd to start on boot", self.chkconfig_on)
+        self.step("starting ntpd", self.__start)
+        self.step("configuring ntpd to start on boot", self.__enable)
 
         self.start_creation("Configuring ntpd")
diff -r dbe13997b7a2 -r 8640eee04855 ipa-server/ipaserver/service.py
--- a/ipa-server/ipaserver/service.py	Fri Jan 11 10:36:25 2008 +0000
+++ b/ipa-server/ipaserver/service.py	Fri Jan 11 11:06:33 2008 +0000
@@ -18,6 +18,7 @@
 #
 
 import logging, sys
+import sysrestore
 from ipa import ipautil
 
 
@@ -100,6 +101,9 @@ class Service:
     def is_enabled(self):
         return is_enabled(self.service_name)
 
+    def backup_state(self, key, value):
+        sysrestore.backup_state(self.service_name, key, value)
+
     def print_msg(self, message):
         print_msg(message, self.output_fd)
 
diff -r dbe13997b7a2 -r 8640eee04855 ipa-server/ipaserver/sysrestore.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ipa-server/ipaserver/sysrestore.py	Fri Jan 11 11:06:33 2008 +0000
@@ -0,0 +1,253 @@
+# Authors: Mark McLoughlin <markmc at redhat.com>
+#
+# Copyright (C) 2007  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# 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 or later
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+#
+# This module provides a very simple API which allows
+# ipa-server-install --uninstall to restore certain
+# parts of the system configuration to the way it was
+# before ipa-server-install was first run
+#
+
+import os
+import os.path
+import errno
+import shutil
+import logging
+import ConfigParser
+
+from ipa import ipautil
+
+SYSRESTORE_CACHE_PATH = "/var/cache/ipa/sysrestore"
+SYSRESTORE_STATEFILE_PATH = "/var/cache/ipa/sysrestore.state"
+
+def _mktree(basedir, reldir):
+    """Create the tree of directories specified by @reldir
+    under the directory @base.
+
+    Caveats:
+      - @basedir must exist
+      - @reldir must not be absolute
+      - @reldir must refer to a directory
+    """
+    (parentdir, subdir) = os.path.split(reldir)
+    if parentdir:
+        _mktree(basedir, parentdir)
+    
+    absdir = os.path.join(basedir, reldir)
+    try:
+        logging.debug("Creating directory '%s'", absdir)
+        os.mkdir(absdir)
+    except OSError, err:
+        if err.errno != errno.EEXIST:
+            raise err
+
+def _rmtree(basedir, reldir):
+    """Delete a tree of directories specified by @reldir
+    under the directory @base, excluding the @base itself.
+    Only empty directories will be deleted.
+
+    Caveats:
+      - @reldir must not be absolute
+      - @reldir must refer to a directory
+    """
+    absdir = os.path.join(basedir, reldir)
+    try:
+        logging.debug("Deleting directory '%s'", absdir)
+        os.rmdir(absdir)
+    except OSError, err:
+        if err.errno == errno.ENOTEMPTY:
+            logging.debug("Directory '%s' not empty", absdir)
+            return
+        else:
+            raise err
+
+    (parentdir, subdir) = os.path.split(reldir)
+    if parentdir:
+        _rmtree(basedir, parentdir)
+    
+def backup_file(path):
+    """Create a copy of the file at @path - so long as a copy
+    does not already exist - which will be restored to its
+    original location by restore_files().
+    """
+    logging.debug("Backing up system configuration file '%s'", path)
+    
+    if not os.path.isabs(path):
+        raise ValueError("Absolute path required")
+
+    if not os.path.isfile(path):
+        logging.debug("  -> Not backing up - '%s' doesn't exist", path)
+        return
+
+    relpath = path[1:]
+
+    backup_path = os.path.join(SYSRESTORE_CACHE_PATH, relpath)
+    if os.path.exists(backup_path):
+        logging.debug("  -> Not backing up - already have a copy of '%s'", path)
+        return
+
+    (reldir, file) = os.path.split(relpath)
+    if reldir:
+        _mktree(SYSRESTORE_CACHE_PATH, reldir)
+
+    shutil.copy2(path, backup_path)
+
+def restore_file(path):
+    """Restore the copy of a file at @path to its original
+    location and delete the copy.
+
+    Returns #True if the file was restored, #False if there
+    was no backup file to restore
+    """
+    logging.debug("Restoring system configuration file '%s'", path)
+
+    if not os.path.isabs(path):
+        raise ValueError("Absolute path required")
+
+    relpath = path[1:]
+
+    backup_path = os.path.join(SYSRESTORE_CACHE_PATH, relpath)
+    if not os.path.exists(backup_path):
+        logging.debug("  -> Not restoring - '%s' doesn't exist", backup_path)
+        return False
+
+    shutil.move(backup_path, path)
+
+    ipautil.run(["/sbin/restorecon", path])
+    
+    (reldir, file) = os.path.split(relpath)
+    if reldir:
+        _rmtree(SYSRESTORE_CACHE_PATH, reldir)
+
+    return True
+
+class _StateFile:
+    """A metadata file for recording system state which can
+    be backed up and later restored. The format is something
+    like:
+
+    [httpd]
+    running=True
+    enabled=False
+    """
+                
+    def __init__(self, path = SYSRESTORE_STATEFILE_PATH):
+        """Create a _StateFile object, loading from @path.
+
+        The dictionary @modules, a member of the returned object,
+        is where the state can be modified. @modules is indexed
+        using a module name to return another dictionary containing
+        key/value pairs with the saved state of that module.
+
+        The keys in these latter dictionaries are arbitrary strings
+        and the values may either be strings or booleans.
+        """
+        self._path = path
+        
+        self.modules = {}
+        
+        self._load()
+
+    def _load(self):
+        """Load the modules from the file @_path. @modules will
+        be an empty dictionary if the file doesn't exist.
+        """
+        logging.debug("Loading StateFile from '%s'", self._path)
+        
+        self.modules = {}
+
+        p = ConfigParser.SafeConfigParser()
+        p.read(self._path)
+
+        for module in p.sections():
+            self.modules[module] = {}
+            for (key, value) in p.items(module):
+                if value == str(True):
+                    value = True
+                elif value == str(False):
+                    value = False
+                self.modules[module][key] = value
+
+    def save(self):
+        """Save the modules to @_path. If @modules is an empty
+        dict, then @_path should be removed.
+        """
+        logging.debug("Saving StateFile to '%s'", self._path)
+        
+        for module in self.modules.keys():
+            if len(self.modules[module]) == 0:
+                del self.modules[module]
+
+        if len(self.modules) == 0:
+            logging.debug("  -> no modules, removing file")
+            if os.path.exists(self._path):
+                os.remove(self._path)
+            return
+
+        p = ConfigParser.SafeConfigParser()
+
+        for module in self.modules.keys():
+            p.add_section(module)
+            for (key, value) in self.modules[module].items():
+                p.set(module, key, str(value))
+
+        f = file(self._path, "w")
+        p.write(f)
+        f.close()
+
+def backup_state(module, key, value):
+    """Backup an item of system state from @module, identified
+    by the string @key and with the value @value. @value may be
+    a string or boolean.
+    """
+    if not (isinstance(value, str) or isinstance(value, bool)):
+        raise ValueError("Only strings or booleans supported")
+
+    state = _StateFile()
+    
+    if not state.modules.has_key(module):
+        state.modules[module] = {}
+       
+    if not state.modules.has_key(key):
+        state.modules[module][key] = value
+    
+    state.save()
+
+def restore_state(module, key):
+    """Return the value of an item of system state from @module,
+    identified by the string @key, and remove it from the backed
+    up system state.
+
+    If the item doesn't exist, #None will be returned, otherwise
+    the original string or boolean value is returned.
+    """
+    state = _StateFile()
+    
+    if not state.modules.has_key(module):
+        return None
+    
+    if not state.modules[module].has_key(key):
+        return None
+
+    value = state.modules[module][key]
+    del state.modules[module][key]
+
+    state.save()
+
+    return value
diff -r dbe13997b7a2 -r 8640eee04855 ipa-server/ipaserver/webguiinstance.py
--- a/ipa-server/ipaserver/webguiinstance.py	Fri Jan 11 10:36:25 2008 +0000
+++ b/ipa-server/ipaserver/webguiinstance.py	Fri Jan 11 11:06:33 2008 +0000
@@ -24,6 +24,14 @@ class WebGuiInstance(service.Service):
         service.Service.__init__(self, "ipa-webgui")
 
     def create_instance(self):
-        self.step("starting ipa-webgui", self.restart)
-        self.step("configuring ipa-webgui to start on boot", self.chkconfig_on)
+        self.step("starting ipa-webgui", self.__start)
+        self.step("configuring ipa-webgui to start on boot", self.__enable)
         self.start_creation("Configuring ipa-webgui")
+
+    def __start(self):
+        self.backup_state("running", self.is_running())
+        self.restart()
+
+    def __enable(self):
+        self.backup_state("enabled", self.is_enabled())
+        self.chkconfig_on()




More information about the Freeipa-devel mailing list