summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob Crittenden <rcritten@redhat.com>2008-01-14 12:43:26 -0500
committerRob Crittenden <rcritten@redhat.com>2008-01-14 12:43:26 -0500
commitc7f3c746ccfd74480064dbe73fbc754548c30927 (patch)
treee0e8296dd50c0f9815f930713a2b3e917cb83bdf
parent23ac773ada10867767e779823ab5f66c79b0dc04 (diff)
downloadfreeipa-c7f3c746ccfd74480064dbe73fbc754548c30927.tar.gz
freeipa-c7f3c746ccfd74480064dbe73fbc754548c30927.tar.xz
freeipa-c7f3c746ccfd74480064dbe73fbc754548c30927.zip
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@redhat.com>
-rw-r--r--Makefile8
-rw-r--r--ipa-server/Makefile.am7
-rw-r--r--ipa-server/ipa-install/ipa-server-install3
-rwxr-xr-xipa-server/ipa-server.spec5
-rw-r--r--ipa-server/ipa-server.spec.in4
-rw-r--r--ipa-server/ipaserver/Makefile.am1
-rw-r--r--ipa-server/ipaserver/bindinstance.py10
-rw-r--r--ipa-server/ipaserver/certs.py13
-rw-r--r--ipa-server/ipaserver/dsinstance.py13
-rw-r--r--ipa-server/ipaserver/httpinstance.py26
-rw-r--r--ipa-server/ipaserver/krbinstance.py21
-rw-r--r--ipa-server/ipaserver/ntpinstance.py15
-rw-r--r--ipa-server/ipaserver/service.py4
-rw-r--r--ipa-server/ipaserver/sysrestore.py253
-rw-r--r--ipa-server/ipaserver/webguiinstance.py12
15 files changed, 375 insertions, 20 deletions
diff --git a/Makefile b/Makefile
index ff4c36979..f8b61081d 100644
--- a/Makefile
+++ b/Makefile
@@ -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 --git a/ipa-server/Makefile.am b/ipa-server/Makefile.am
index a145ac031..b04a4cf98 100644
--- a/ipa-server/Makefile.am
+++ b/ipa-server/Makefile.am
@@ -13,6 +13,13 @@ SUBDIRS = \
xmlrpc-server \
$(NULL)
+install-exec-local:
+ mkdir -p $(DESTDIR)$(localstatedir)/cache/ipa/sysrestore
+
+uninstall-local:
+ -rmdir $(DESTDIR)$(localstatedir)/cache/ipa/sysrestore
+ -rmdir $(DESTDIR)$(localstatedir)/cache/ipa
+
EXTRA_DIST = \
ipa-server.spec \
COPYING \
diff --git a/ipa-server/ipa-install/ipa-server-install b/ipa-server/ipa-install/ipa-server-install
index 5f0879d73..bc4d8e27a 100644
--- a/ipa-server/ipa-install/ipa-server-install
+++ b/ipa-server/ipa-install/ipa-server-install
@@ -49,6 +49,7 @@ import ipaserver.ntpinstance
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 --git a/ipa-server/ipa-server.spec b/ipa-server/ipa-server.spec
index cbd0de54e..98de382cf 100755
--- a/ipa-server/ipa-server.spec
+++ b/ipa-server/ipa-server.spec
@@ -48,7 +48,7 @@ Ipa is a server for identity, policy, and audit.
%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
@@ -93,6 +93,7 @@ fi
%{_sbindir}/ipa-server-install
%{_sbindir}/ipa-replica-install
%{_sbindir}/ipa-replica-prepare
+%{_sbindir}/ipa-replica-manage
%{_sbindir}/ipa-server-certinstall
%{_sbindir}/ipa_kpasswd
%{_sbindir}/ipa-webgui
@@ -106,6 +107,8 @@ 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
+%dir %{_localstatedir}/cache/ipa/sysrestore
%changelog
* Fri Dec 21 2007 Karl MacMillan <kmacmill@redhat.com> - 0.6.0-1
diff --git a/ipa-server/ipa-server.spec.in b/ipa-server/ipa-server.spec.in
index 526415487..24b02c9ea 100644
--- a/ipa-server/ipa-server.spec.in
+++ b/ipa-server/ipa-server.spec.in
@@ -48,7 +48,7 @@ Ipa is a server for identity, policy, and audit.
%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,8 @@ 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
+%dir %{_localstatedir}/cache/ipa/sysrestore
%changelog
* Fri Dec 21 2007 Karl MacMillan <kmacmill@redhat.com> - 0.6.0-1
diff --git a/ipa-server/ipaserver/Makefile.am b/ipa-server/ipaserver/Makefile.am
index b1d00a80a..99b74b504 100644
--- a/ipa-server/ipaserver/Makefile.am
+++ b/ipa-server/ipaserver/Makefile.am
@@ -14,6 +14,7 @@ app_PYTHON = \
installutils.py \
replication.py \
certs.py \
+ sysrestore.py \
$(NULL)
EXTRA_DIST = \
diff --git a/ipa-server/ipaserver/bindinstance.py b/ipa-server/ipaserver/bindinstance.py
index cc99eacfa..770663c67 100644
--- a/ipa-server/ipaserver/bindinstance.py
+++ b/ipa-server/ipaserver/bindinstance.py
@@ -25,6 +25,7 @@ import os
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 --git a/ipa-server/ipaserver/certs.py b/ipa-server/ipaserver/certs.py
index d8bc35f24..b39cf2244 100644
--- a/ipa-server/ipaserver/certs.py
+++ b/ipa-server/ipaserver/certs.py
@@ -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 --git a/ipa-server/ipaserver/dsinstance.py b/ipa-server/ipaserver/dsinstance.py
index 0de891fc6..78a847597 100644
--- a/ipa-server/ipaserver/dsinstance.py
+++ b/ipa-server/ipaserver/dsinstance.py
@@ -154,10 +154,14 @@ 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()
self.sub_dict = dict(FQHN=self.host_name, SERVERID=self.serverid,
@@ -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 --git a/ipa-server/ipaserver/httpinstance.py b/ipa-server/ipaserver/httpinstance.py
index beca3e83a..76e314dfa 100644
--- a/ipa-server/ipaserver/httpinstance.py
+++ b/ipa-server/ipaserver/httpinstance.py
@@ -29,6 +29,7 @@ import sys
import shutil
import service
+import sysrestore
import certs
import dsinstance
import installutils
@@ -63,11 +64,19 @@ 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
try:
@@ -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 --git a/ipa-server/ipaserver/krbinstance.py b/ipa-server/ipaserver/krbinstance.py
index db7004e4c..10dab364e 100644
--- a/ipa-server/ipaserver/krbinstance.py
+++ b/ipa-server/ipaserver/krbinstance.py
@@ -32,6 +32,7 @@ import socket
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 --git a/ipa-server/ipaserver/ntpinstance.py b/ipa-server/ipaserver/ntpinstance.py
index b321ec075..c40b12b08 100644
--- a/ipa-server/ipaserver/ntpinstance.py
+++ b/ipa-server/ipaserver/ntpinstance.py
@@ -20,6 +20,7 @@
import shutil
import service
+import sysrestore
from ipa import ipautil
class NTPInstance(service.Service):
@@ -45,19 +46,27 @@ 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)
# 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 --git a/ipa-server/ipaserver/service.py b/ipa-server/ipaserver/service.py
index f6df6e5b3..0ea3f6618 100644
--- a/ipa-server/ipaserver/service.py
+++ b/ipa-server/ipaserver/service.py
@@ -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 --git a/ipa-server/ipaserver/sysrestore.py b/ipa-server/ipaserver/sysrestore.py
new file mode 100644
index 000000000..4716aa0da
--- /dev/null
+++ b/ipa-server/ipaserver/sysrestore.py
@@ -0,0 +1,253 @@
+# Authors: Mark McLoughlin <markmc@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 --git a/ipa-server/ipaserver/webguiinstance.py b/ipa-server/ipaserver/webguiinstance.py
index 285435587..f39002450 100644
--- a/ipa-server/ipaserver/webguiinstance.py
+++ b/ipa-server/ipaserver/webguiinstance.py
@@ -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()