summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <ssorce@redhat.com>2008-03-27 19:01:38 -0400
committerSimo Sorce <ssorce@redhat.com>2008-03-27 19:01:38 -0400
commitaac086582aee79ccf72206faf118e997c623170c (patch)
tree7c806025ad651531015b62ca806db7a32f55c2a8
parentb7924139d895c7ebe019fbfc9c85fed3baae642e (diff)
downloadfreeipa.git-aac086582aee79ccf72206faf118e997c623170c.tar.gz
freeipa.git-aac086582aee79ccf72206faf118e997c623170c.tar.xz
freeipa.git-aac086582aee79ccf72206faf118e997c623170c.zip
Move sysrestore to ipa-python so it can be used by client scripts too.
Change backup format so files are all in a single directory (no dir hierarchies) and use an index file so we can save also ownership and permission info for the restore (and eventually other data later on).
-rw-r--r--ipa-python/sysrestore.py319
-rw-r--r--ipa-server/Makefile.am6
-rw-r--r--ipa-server/ipa-install/ipa-server-install26
-rwxr-xr-xipa-server/ipa-server.spec4
-rw-r--r--ipa-server/ipa-server.spec.in4
-rw-r--r--ipa-server/ipaserver/Makefile.am1
-rw-r--r--ipa-server/ipaserver/bindinstance.py55
-rw-r--r--ipa-server/ipaserver/certs.py35
-rw-r--r--ipa-server/ipaserver/httpinstance.py28
-rw-r--r--ipa-server/ipaserver/krbinstance.py53
-rw-r--r--ipa-server/ipaserver/ntpinstance.py23
-rw-r--r--ipa-server/ipaserver/service.py14
-rw-r--r--ipa-server/ipaserver/sysrestore.py253
13 files changed, 471 insertions, 350 deletions
diff --git a/ipa-python/sysrestore.py b/ipa-python/sysrestore.py
new file mode 100644
index 00000000..46233ef8
--- /dev/null
+++ b/ipa-python/sysrestore.py
@@ -0,0 +1,319 @@
+# 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 only
+#
+# 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-xxx-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
+import random
+import string
+
+from ipa import ipautil
+
+SYSRESTORE_PATH = "/tmp"
+SYSRESTORE_INDEXFILE = "sysrestore.index"
+SYSRESTORE_STATEFILE = "sysrestore.state"
+
+class FileStore:
+ """Class for handling backup and restore of files"""
+
+ def __init__(self, path = SYSRESTORE_PATH):
+ """Create a _StoreFiles object, that uses @path as the
+ base directory.
+
+ The file @path/sysrestore.index is used to store information
+ about the original location of the saved files.
+ """
+ self._path = path+"/"+SYSRESTORE_INDEXFILE
+
+ self.random = random.Random()
+
+ self.files = {}
+ self._load()
+
+ def _load(self):
+ """Load the file list from the index file. @files will
+ be an empty dictionary if the file doesn't exist.
+ """
+
+ logging.debug("Loading Index file from '%s'", self._path)
+
+ self.files = {}
+
+ p = ConfigParser.SafeConfigParser()
+ p.read(self._path)
+
+ for section in p.sections():
+ if section == "files":
+ for (key, value) in p.items(section):
+ self.files[key] = value
+
+
+ def save(self):
+ """Save the file list to @_path. If @files is an empty
+ dict, then @_path should be removed.
+ """
+ logging.debug("Saving Index File to '%s'", self._path)
+
+ if len(self.files) == 0:
+ logging.debug(" -> no files, removing file")
+ if os.path.exists(self._path):
+ os.remove(self._path)
+ return
+
+ p = ConfigParser.SafeConfigParser()
+
+ p.add_section('files')
+ for (key, value) in self.files.items():
+ p.set('files', key, str(value))
+
+ f = file(self._path, "w")
+ p.write(f)
+ f.close()
+
+ def backup_file(self, 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
+
+ (reldir, file) = os.path.split(path)
+
+ filename = ""
+ for i in range(8):
+ h = "%02x" % self.random.randint(0,255)
+ filename += h
+ filename += "-"+file
+
+ backup_path = os.path.join(SYSRESTORE_PATH, filename)
+ if os.path.exists(backup_path):
+ logging.debug(" -> Not backing up - already have a copy of '%s'", path)
+ return
+
+ shutil.copy2(path, backup_path)
+
+ stat = os.stat(path)
+
+ self.files[filename] = string.join([str(stat.st_mode),str(stat.st_uid),str(stat.st_gid),path], ',')
+ self.save()
+
+ def restore_file(self, 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")
+
+ mode = None
+ uid = None
+ gid = None
+ filename = None
+
+ for (key, value) in self.files.items():
+ (mode,uid,gid,filepath) = string.split(value, ',', 3)
+ if (filepath == path):
+ filename = key
+ break
+
+ if not filename:
+ raise ValueError("No such file name in the index")
+
+ backup_path = os.path.join(SYSRESTORE_PATH, filename)
+ if not os.path.exists(backup_path):
+ logging.debug(" -> Not restoring - '%s' doesn't exist", backup_path)
+ return False
+
+ shutil.move(backup_path, path)
+ os.chown(path, int(uid), int(gid))
+ os.chmod(path, int(mode))
+
+ ipautil.run(["/sbin/restorecon", path])
+
+ del self.files[filename]
+ self.save()
+
+ return True
+
+ def restore_all_files(self):
+ """Restore the files in the inbdex to their original
+ location and delete the copy.
+
+ Returns #True if the file was restored, #False if there
+ was no backup file to restore
+ """
+
+ if len(self.files) == 0:
+ return False
+
+ for (filename, value) in self.files.items():
+
+ (mode,uid,gid,path) = string.split(value, ',', 3)
+
+ backup_path = os.path.join(SYSRESTORE_PATH, filename)
+ if not os.path.exists(backup_path):
+ logging.debug(" -> Not restoring - '%s' doesn't exist", backup_path)
+
+ shutil.move(backup_path, path)
+ os.chown(path, int(uid), int(gid))
+ os.chmod(path, int(mode))
+
+ ipautil.run(["/sbin/restorecon", path])
+
+ #force file to be deleted
+ self.files = {}
+ self.save()
+
+ 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_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+"/"+SYSRESTORE_STATEFILE
+
+ 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/Makefile.am b/ipa-server/Makefile.am
index d26aa290..35d3074e 100644
--- a/ipa-server/Makefile.am
+++ b/ipa-server/Makefile.am
@@ -15,12 +15,14 @@ SUBDIRS = \
$(NULL)
install-exec-local:
- mkdir -p $(DESTDIR)$(localstatedir)/cache/ipa/sysrestore
+ mkdir -p $(DESTDIR)$(localstatedir)/lib/ipa/sysrestore
+ chmod 700 $(DESTDIR)$(localstatedir)/lib/ipa/sysrestore
mkdir -p $(DESTDIR)$(localstatedir)/cache/ipa/sessions
chmod 700 $(DESTDIR)$(localstatedir)/cache/ipa/sessions
uninstall-local:
- -rmdir $(DESTDIR)$(localstatedir)/cache/ipa/sysrestore
+ -rmdir $(DESTDIR)$(localstatedir)/lib/ipa/sysrestore
+ -rmdir $(DESTDIR)$(localstatedir)/lib/ipa
-rmdir $(DESTDIR)$(localstatedir)/cache/ipa/sessions
-rmdir $(DESTDIR)$(localstatedir)/cache/ipa
diff --git a/ipa-server/ipa-install/ipa-server-install b/ipa-server/ipa-install/ipa-server-install
index 8b5c4412..1be1d3c9 100644
--- a/ipa-server/ipa-install/ipa-server-install
+++ b/ipa-server/ipa-install/ipa-server-install
@@ -46,9 +46,9 @@ import ipaserver.httpinstance
import ipaserver.ntpinstance
from ipaserver import service
-from ipaserver import sysrestore
from ipaserver.installutils import *
+from ipa import sysrestore
from ipa.ipautil import *
def parse_options():
@@ -173,7 +173,7 @@ def read_ip_address(host_name):
continue
print "Adding ["+ip+" "+host_name+"] to your /etc/hosts file"
- sysrestore.backup_file("/etc/hosts")
+ fstore.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')
@@ -292,14 +292,13 @@ def check_dirsrv():
sys.exit(1)
def uninstall():
- ipaserver.ntpinstance.NTPInstance().uninstall()
- ipaserver.bindinstance.BindInstance().uninstall()
+ ipaserver.ntpinstance.NTPInstance(fstore).uninstall()
+ ipaserver.bindinstance.BindInstance(fstore).uninstall()
ipaserver.httpinstance.WebGuiInstance().uninstall()
- ipaserver.httpinstance.HTTPInstance().uninstall()
- ipaserver.krbinstance.KrbInstance().uninstall()
+ ipaserver.httpinstance.HTTPInstance(fstore).uninstall()
+ ipaserver.krbinstance.KrbInstance(fstore).uninstall()
ipaserver.dsinstance.DsInstance().uninstall()
- sysrestore.restore_file("/etc/hosts")
- sysrestore.restore_file("/etc/ipa/ipa.conf")
+ fstore.restore_all_files()
return 0
def main():
@@ -321,6 +320,9 @@ def main():
standard_logging_setup("/var/log/ipaserver-install.log", options.debug)
print "\nThe log file for this installation can be found in /var/log/ipaserver-install.log"
+ global fstore
+ fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
+
if options.uninstall:
return uninstall()
@@ -454,7 +456,7 @@ def main():
# Configure ntpd
if options.conf_ntp:
- ntp = ipaserver.ntpinstance.NTPInstance()
+ ntp = ipaserver.ntpinstance.NTPInstance(fstore)
ntp.create_instance()
# Create a directory server instance
@@ -462,11 +464,11 @@ def main():
ds.create_instance(ds_user, realm_name, host_name, domain_name, dm_password)
# Create a kerberos instance
- krb = ipaserver.krbinstance.KrbInstance()
+ krb = ipaserver.krbinstance.KrbInstance(fstore)
krb.create_instance(ds_user, realm_name, host_name, domain_name, dm_password, master_password)
# Create a HTTP instance
- http = ipaserver.httpinstance.HTTPInstance()
+ http = ipaserver.httpinstance.HTTPInstance(fstore)
http.create_instance(realm_name, host_name, domain_name)
# Create a Web Gui instance
@@ -499,7 +501,7 @@ def main():
ds.change_admin_password(admin_password)
# Create the config file
- sysrestore.backup_file("/etc/ipa/ipa.conf")
+ fstore.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 97b0e473..43609241 100755
--- a/ipa-server/ipa-server.spec
+++ b/ipa-server/ipa-server.spec
@@ -149,8 +149,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
+%dir %{_localstatedir}/lib/ipa
+%dir %{_localstatedir}/lib/ipa/sysrestore
%attr(700,apache,apache) %dir %{_localstatedir}/cache/ipa/sessions
%{_mandir}/man8/ipactl.8.gz
diff --git a/ipa-server/ipa-server.spec.in b/ipa-server/ipa-server.spec.in
index 57813f4b..46adec95 100644
--- a/ipa-server/ipa-server.spec.in
+++ b/ipa-server/ipa-server.spec.in
@@ -149,8 +149,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
+%dir %{_localstatedir}/lib/ipa
+%dir %{_localstatedir}/lib/ipa/sysrestore
%attr(700,apache,apache) %dir %{_localstatedir}/cache/ipa/sessions
%{_mandir}/man8/ipactl.8.gz
diff --git a/ipa-server/ipaserver/Makefile.am b/ipa-server/ipaserver/Makefile.am
index 4a33b954..13029e14 100644
--- a/ipa-server/ipaserver/Makefile.am
+++ b/ipa-server/ipaserver/Makefile.am
@@ -13,7 +13,6 @@ 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 e9824ef0..a331b9d5 100644
--- a/ipa-server/ipaserver/bindinstance.py
+++ b/ipa-server/ipaserver/bindinstance.py
@@ -22,13 +22,14 @@ import tempfile
import shutil
import os
import socket
+import logging
import service
-import sysrestore
+from ipa import sysrestore
from ipa import ipautil
class BindInstance(service.Service):
- def __init__(self):
+ def __init__(self, fstore=None):
service.Service.__init__(self, "named")
self.fqdn = None
self.domain = None
@@ -37,6 +38,11 @@ class BindInstance(service.Service):
self.realm = None
self.sub_dict = None
+ if fstore:
+ self.fstore = fstore
+ else:
+ self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
+
def setup(self, fqdn, ip_address, realm_name, domain_name):
self.fqdn = fqdn
self.ip_address = ip_address
@@ -68,15 +74,25 @@ class BindInstance(service.Service):
except:
pass
- self.__setup_zone()
- self.__setup_named_conf()
+ self.step("Setting up our zone", self.__setup_zone)
+ self.step("Setting up named.conf", self.__setup_named_conf)
+
+ self.step("restarting named", self.__start)
+ self.step("configuring named to start on boot", self.__enable)
+
+ self.step("Changing resolve.conf to point to ourselves", self.__setup_resolve_conf)
+ def __start(self):
try:
self.backup_state("running", self.is_running())
- self.start()
+ self.restart()
except:
print "named service failed to start"
+ def __enable(self):
+ self.backup_state("enabled", self.is_running())
+ self.chkconfig_on()
+
def __setup_sub_dict(self):
self.sub_dict = dict(FQDN=self.fqdn,
IP=self.ip_address,
@@ -87,13 +103,13 @@ class BindInstance(service.Service):
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')
+ self.fstore.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):
- sysrestore.backup_file('/etc/named.conf')
+ self.fstore.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)
@@ -101,7 +117,8 @@ class BindInstance(service.Service):
named_fd.write(named_txt)
named_fd.close()
- sysrestore.backup_file('/etc/resolve.conf')
+ def __setup_resolve_conf(self):
+ self.fstore.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)
@@ -111,16 +128,28 @@ class BindInstance(service.Service):
def uninstall(self):
running = self.restore_state("running")
+ enabled = self.restore_state("enabled")
domain = self.restore_state("domain")
if not running is None:
self.stop()
- if not domain is None:
- sysrestore.restore_file(os.path.join ("/var/named/", domain + ".zone.db"))
-
- sysrestore.restore_file('/etc/named.conf')
- sysrestore.restore_file('/etc/resolve.conf')
+ if not domain is None:
+ try:
+ self.fstore.restore_file(os.path.join ("/var/named/", domain + ".zone.db"))
+ except ValueError, error:
+ logging.debug(error)
+ pass
+
+ for f in ["/etc/named.conf", "/etc/resolve.conf"]:
+ try:
+ self.fstore.restore_file(f)
+ except ValueError, error:
+ logging.debug(error)
+ pass
+
+ if not enabled is None and not enabled:
+ self.chkconfig_off()
if not running is None and running:
self.start()
diff --git a/ipa-server/ipaserver/certs.py b/ipa-server/ipaserver/certs.py
index 2ad842c9..12fb354b 100644
--- a/ipa-server/ipaserver/certs.py
+++ b/ipa-server/ipaserver/certs.py
@@ -21,10 +21,11 @@ import os, stat, subprocess, re
import sha
import errno
+from ipa import sysrestore
from ipa import ipautil
class CertDB(object):
- def __init__(self, dir):
+ def __init__(self, dir, fstore=None):
self.secdir = dir
self.noise_fname = self.secdir + "/noise.txt"
@@ -57,7 +58,12 @@ class CertDB(object):
mode = os.stat(self.secdir)
self.uid = mode[stat.ST_UID]
self.gid = mode[stat.ST_GID]
-
+
+ if fstore:
+ self.fstore = fstore
+ else:
+ self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
+
def set_serial_from_pkcs12(self):
"""A CA cert was loaded from a PKCS#12 file. Set up our serial file"""
@@ -188,7 +194,7 @@ class CertDB(object):
return x.group(1)
raise RuntimeError("Unable to find serial number")
-
+
def create_server_cert(self, nickname, name, other_certdb=None):
cdb = other_certdb
if not cdb:
@@ -198,7 +204,7 @@ class CertDB(object):
self.add_cert(self.certder_fname, nickname)
os.unlink(self.certreq_fname)
os.unlink(self.certder_fname)
-
+
def create_signing_cert(self, nickname, name, other_certdb=None):
cdb = other_certdb
if not cdb:
@@ -322,7 +328,6 @@ class CertDB(object):
server_certs.append((name, flags))
return server_certs
-
def import_pkcs12(self, pkcs12_fname, passwd_fname=None):
args = ["/usr/bin/pk12util", "-d", self.secdir,
@@ -369,13 +374,13 @@ class CertDB(object):
self.export_ca_cert(False)
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)
+ self.fstore.backup_file(self.noise_fname)
+ self.fstore.backup_file(self.passwd_fname)
+ self.fstore.backup_file(self.certdb_fname)
+ self.fstore.backup_file(self.keydb_fname)
+ self.fstore.backup_file(self.secmod_fname)
+ self.fstore.backup_file(self.cacert_fname)
+ self.fstore.backup_file(self.pk12_fname)
+ self.fstore.backup_file(self.pin_fname)
+ self.fstore.backup_file(self.certreq_fname)
+ self.fstore.backup_file(self.certder_fname)
diff --git a/ipa-server/ipaserver/httpinstance.py b/ipa-server/ipaserver/httpinstance.py
index da89f6bd..a55cf255 100644
--- a/ipa-server/ipaserver/httpinstance.py
+++ b/ipa-server/ipaserver/httpinstance.py
@@ -29,10 +29,10 @@ import sys
import shutil
import service
-import sysrestore
import certs
import dsinstance
import installutils
+from ipa import sysrestore
from ipa import ipautil
HTTPD_DIR = "/etc/httpd"
@@ -52,8 +52,12 @@ class WebGuiInstance(service.SimpleServiceInstance):
service.SimpleServiceInstance.__init__(self, "ipa_webgui")
class HTTPInstance(service.Service):
- def __init__(self):
+ def __init__(self, fstore = None):
service.Service.__init__(self, "httpd")
+ if fstore:
+ self.fstore = fstore
+ else:
+ self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
def create_instance(self, realm, fqdn, domain_name, autoconfig=True, pkcs12_info=None):
self.fqdn = fqdn
@@ -61,7 +65,7 @@ class HTTPInstance(service.Service):
self.domain = domain_name
self.pkcs12_info = pkcs12_info
self.sub_dict = { "REALM" : realm, "FQDN": fqdn, "DOMAIN" : self.domain }
-
+
self.step("disabling mod_ssl in httpd", self.__disable_mod_ssl)
self.step("Setting mod_nss port to 443", self.__set_mod_nss_port)
self.step("Adding URL rewriting rules", self.__add_include)
@@ -109,7 +113,7 @@ class HTTPInstance(service.Service):
ipautil.run(["/usr/sbin/setsebool", "-P", "httpd_can_network_connect", "true"])
except:
self.print_msg(selinux_warning)
-
+
def __create_http_keytab(self):
http_principal = "HTTP/" + self.fqdn + "@" + self.realm
installutils.kadmin_addprinc(http_principal)
@@ -120,24 +124,24 @@ 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")
+ self.fstore.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()
http_txt = ipautil.template_file(ipautil.SHARE_DIR + "ipa-rewrite.conf", self.sub_dict)
- sysrestore.backup_file("/etc/httpd/conf.d/ipa-rewrite.conf")
+ self.fstore.backup_file("/etc/httpd/conf.d/ipa-rewrite.conf")
http_fd = open("/etc/httpd/conf.d/ipa-rewrite.conf", "w")
http_fd.write(http_txt)
http_fd.close()
def __disable_mod_ssl(self):
if os.path.exists(SSL_CONF):
- sysrestore.backup_file(SSL_CONF)
+ self.fstore.backup_file(SSL_CONF)
os.unlink(SSL_CONF)
def __set_mod_nss_port(self):
- sysrestore.backup_file(NSS_CONF)
+ self.fstore.backup_file(NSS_CONF)
if installutils.update_file(NSS_CONF, '8443', '443') != 0:
print "Updating port in %s failed." % NSS_CONF
@@ -160,7 +164,7 @@ class HTTPInstance(service.Service):
prefs_txt = ipautil.template_file(ipautil.SHARE_DIR + "preferences.html.template", self.sub_dict)
prefs_fd = open("/usr/share/ipa/html/preferences.html", "w")
prefs_fd.write(prefs_txt)
- prefs_fd.close()
+ prefs_fd.close()
# The signing cert is generated in __setup_ssl
ds_ca = certs.CertDB(dsinstance.config_dirname(dsinstance.realm_to_serverid(self.realm)))
@@ -189,7 +193,11 @@ class HTTPInstance(service.Service):
self.chkconfig_off()
for f in ["/etc/httpd/conf.d/ipa.conf", SSL_CONF, NSS_CONF]:
- sysrestore.restore_file(f)
+ try:
+ self.fstore.restore_file(f)
+ except ValueError, error:
+ logging.debug(error)
+ pass
sebool_state = self.restore_state("httpd_can_network_connect")
if not sebool_state is None:
diff --git a/ipa-server/ipaserver/krbinstance.py b/ipa-server/ipaserver/krbinstance.py
index 9d0a2579..778b4e2d 100644
--- a/ipa-server/ipaserver/krbinstance.py
+++ b/ipa-server/ipaserver/krbinstance.py
@@ -31,8 +31,8 @@ import socket
import shutil
import service
-import sysrestore
import installutils
+from ipa import sysrestore
from ipa import ipautil
from ipa import ipaerror
@@ -71,9 +71,9 @@ def update_key_val_in_file(filename, key, val):
class KpasswdInstance(service.SimpleServiceInstance):
def __init__(self):
service.SimpleServiceInstance.__init__(self, "ipa_kpasswd")
-
+
class KrbInstance(service.Service):
- def __init__(self):
+ def __init__(self, fstore=None):
service.Service.__init__(self, "krb5kdc")
self.ds_user = None
self.fqdn = None
@@ -88,9 +88,14 @@ class KrbInstance(service.Service):
self.kpasswd = KpasswdInstance()
+ if fstore:
+ self.fstore = fstore
+ else:
+ self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
+
def __common_setup(self, ds_user, realm_name, host_name, domain_name, admin_password):
self.ds_user = ds_user
- self.fqdn = host_name
+ self.fqdn = host_name
self.realm = realm_name.upper()
self.host = host_name.split(".")[0]
self.ip = socket.gethostbyname(host_name)
@@ -161,16 +166,16 @@ class KrbInstance(service.Service):
self.kpasswd.create_instance()
def __copy_ldap_passwd(self, filename):
- sysrestore.backup_file("/var/kerberos/krb5kdc/ldappwd")
+ self.fstore.backup_file("/var/kerberos/krb5kdc/ldappwd")
shutil.copy(filename, "/var/kerberos/krb5kdc/ldappwd")
os.chmod("/var/kerberos/krb5kdc/ldappwd", 0600)
-
-
+
+
def __configure_kdc_account_password(self):
hexpwd = ''
for x in self.kdc_password:
hexpwd += (hex(ord(x))[2:])
- sysrestore.backup_file("/var/kerberos/krb5kdc/ldappwd")
+ self.fstore.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()
@@ -273,7 +278,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)
+ self.fstore.backup_file(path)
fd = open(path, "w+")
fd.write(conf)
fd.close()
@@ -347,10 +352,10 @@ class KrbInstance(service.Service):
ldap_principal = "ldap/" + self.fqdn + "@" + self.realm
installutils.kadmin_addprinc(ldap_principal)
- sysrestore.backup_file("/etc/dirsrv/ds.keytab")
+ self.fstore.backup_file("/etc/dirsrv/ds.keytab")
installutils.create_keytab("/etc/dirsrv/ds.keytab", ldap_principal)
- sysrestore.backup_file("/etc/sysconfig/dirsrv")
+ self.fstore.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)
@@ -359,7 +364,7 @@ class KrbInstance(service.Service):
host_principal = "host/" + self.fqdn + "@" + self.realm
installutils.kadmin_addprinc(host_principal)
- sysrestore.backup_file("/etc/krb5.keytab")
+ self.fstore.backup_file("/etc/krb5.keytab")
installutils.create_keytab("/etc/krb5.keytab", host_principal)
# Make sure access is strictly reserved to root only for now
@@ -369,10 +374,10 @@ 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")
+ self.fstore.backup_file("/var/kerberos/krb5kdc/kpasswd.keytab")
installutils.create_keytab("/var/kerberos/krb5kdc/kpasswd.keytab", "kadmin/changepw")
- sysrestore.backup_file("/etc/sysconfig/ipa_kpasswd")
+ self.fstore.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)
@@ -386,21 +391,15 @@ class KrbInstance(service.Service):
if not running is None:
self.stop()
+ for f in ["/var/kerberos/krb5kdc/ldappwd", "/var/kerberos/krb5kdc/kdc.conf", "/etc/krb5.conf"]:
+ try:
+ self.fstore.restore_file(f)
+ except ValueError, error:
+ logging.debug(error)
+ pass
+
if not enabled is None and not enabled:
self.chkconfig_off()
- for f in ["/var/kerberos/krb5kdc/ldappwd",
- "/var/kerberos/krb5kdc/kdc.conf",
- "/etc/krb5.conf",
- "/usr/share/ipa/html/krb5.ini",
- "/usr/share/ipa/html/krb.con",
- "/usr/share/ipa/html/krbrealm.con",
- "/etc/dirsrv/ds.keytab",
- "/etc/sysconfig/dirsrv",
- "/etc/krb5.keytab",
- "/var/kerberos/krb5kdc/kpasswd.keytab",
- "/etc/sysconfig/ipa_kpasswd"]:
- sysrestore.restore_file(f)
-
if not running is None and running:
self.start()
diff --git a/ipa-server/ipaserver/ntpinstance.py b/ipa-server/ipaserver/ntpinstance.py
index 53858810..e2ec6065 100644
--- a/ipa-server/ipaserver/ntpinstance.py
+++ b/ipa-server/ipaserver/ntpinstance.py
@@ -18,15 +18,21 @@
#
import shutil
+import logging
import service
-import sysrestore
+from ipa import sysrestore
from ipa import ipautil
class NTPInstance(service.Service):
- def __init__(self):
+ def __init__(self, fstore=None):
service.Service.__init__(self, "ntpd")
+ if fstore:
+ self.fstore = fstore
+ else:
+ self.fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore')
+
def __write_config(self):
# The template sets the config to point towards ntp.pool.org, but
# they request that software not point towards the default pool.
@@ -47,8 +53,8 @@ class NTPInstance(service.Service):
ntp_conf = ipautil.template_file(ipautil.SHARE_DIR + "ntp.conf.server.template", sub_dict)
ntp_sysconf = ipautil.template_file(ipautil.SHARE_DIR + "ntpd.sysconfig.template", {})
- sysrestore.backup_file("/etc/ntp.conf")
- sysrestore.backup_file("/etc/sysconfig/ntpd")
+ self.fstore.backup_file("/etc/ntp.conf")
+ self.fstore.backup_file("/etc/sysconfig/ntpd")
fd = open("/etc/ntp.conf", "w")
fd.write(ntp_conf)
@@ -87,10 +93,15 @@ class NTPInstance(service.Service):
if not running is None:
self.stop()
+
+ try:
+ self.fstore.restore_file("/etc/ntp.conf")
+ except ValueError, error:
+ logging.debug(error)
+ pass
+
if not enabled is None and not enabled:
self.chkconfig_off()
- sysrestore.restore_file("/etc/ntp.conf")
-
if not running is None and running:
self.start()
diff --git a/ipa-server/ipaserver/service.py b/ipa-server/ipaserver/service.py
index dbdc9a72..f9270f2e 100644
--- a/ipa-server/ipaserver/service.py
+++ b/ipa-server/ipaserver/service.py
@@ -18,7 +18,7 @@
#
import logging, sys
-import sysrestore
+from ipa import sysrestore
from ipa import ipautil
@@ -30,7 +30,7 @@ def start(service_name):
def restart(service_name):
ipautil.run(["/sbin/service", service_name, "restart"])
-
+
def is_running(service_name):
ret = True
try:
@@ -38,7 +38,7 @@ def is_running(service_name):
except ipautil.CalledProcessError:
ret = False
return ret
-
+
def chkconfig_on(service_name):
ipautil.run(["/sbin/chkconfig", service_name, "on"])
@@ -70,12 +70,12 @@ def is_enabled(service_name):
break
return (runlevels[3] and runlevels[4] and runlevels[5])
-
+
def print_msg(message, output_fd=sys.stdout):
logging.debug(message)
output_fd.write(message)
output_fd.write("\n")
-
+
class Service:
def __init__(self, service_name):
@@ -85,7 +85,7 @@ class Service:
def set_output(self, fd):
self.output_fd = fd
-
+
def stop(self):
stop(self.service_name)
@@ -133,7 +133,7 @@ class Service:
self.print_msg(" [%d/%d]: %s" % (step+1, len(self.steps), message))
method()
step += 1
-
+
self.print_msg("done configuring %s." % self.service_name)
self.steps = []
diff --git a/ipa-server/ipaserver/sysrestore.py b/ipa-server/ipaserver/sysrestore.py
deleted file mode 100644
index 4954b8d8..00000000
--- a/ipa-server/ipaserver/sysrestore.py
+++ /dev/null
@@ -1,253 +0,0 @@
-# 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 only
-#
-# 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