mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
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>
This commit is contained in:
parent
23ac773ada
commit
c7f3c746cc
8
Makefile
8
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
|
||||
|
@ -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 \
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -14,6 +14,7 @@ app_PYTHON = \
|
||||
installutils.py \
|
||||
replication.py \
|
||||
certs.py \
|
||||
sysrestore.py \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = \
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
||||
|
253
ipa-server/ipaserver/sysrestore.py
Normal file
253
ipa-server/ipaserver/sysrestore.py
Normal file
@ -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
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user