summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Hrozek <jhrozek@redhat.com>2009-09-25 00:32:50 +0200
committerStephen Gallagher <sgallagh@redhat.com>2009-09-25 09:03:29 -0400
commit3c02938a2643fdc8ff83d81400334172f0743823 (patch)
treeaf28ca96e9a4c777e638de892bdf6d61cbf9687c
parentfee6c6a4cb7f851bb757088dae9e0720ae073d3c (diff)
downloadsssd-3c02938a2643fdc8ff83d81400334172f0743823.tar.gz
sssd-3c02938a2643fdc8ff83d81400334172f0743823.tar.xz
sssd-3c02938a2643fdc8ff83d81400334172f0743823.zip
script to upgrade config to v2
-rw-r--r--contrib/sssd.spec.in6
-rw-r--r--server/Makefile.am3
-rw-r--r--server/upgrade/upgrade_config.py352
3 files changed, 361 insertions, 0 deletions
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 786b2e9e0..87ee5a9d6 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -18,6 +18,7 @@ BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
Requires: libldb = 0.9.3
Requires: libtdb >= 1.1.3
+Requires(post): python
Requires(preun): initscripts chkconfig
Requires(postun): /sbin/service
@@ -132,6 +133,8 @@ rm -rf $RPM_BUILD_ROOT
%post
/sbin/ldconfig
/sbin/chkconfig --add %{servicename}
+# a one-time upgrade from confdb v1 to v2
+python %{_libexecdir}/%{servicename}/upgrade_config.py
%preun
if [ $1 = 0 ]; then
@@ -146,6 +149,9 @@ if [ $1 -ge 1 ] ; then
fi
%changelog
+* Fri Sep 25 2009 Stephen Gallagher <sgallagh@redhat.com> - 0.6.0-0
+- Convert to new config file format
+
* Wed Sep 02 2009 Stephen Gallagher <sgallagh@redhat.com> - 0.5.0-0
- New upstream release 0.5.0
diff --git a/server/Makefile.am b/server/Makefile.am
index 41eeefb42..a5555204f 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -57,6 +57,9 @@ sssdlibexec_PROGRAMS = \
$(sssd_pk) \
$(sssd_info)
+dist_sssdlibexec_SCRIPTS = \
+ upgrade/upgrade_config.py
+
if HAVE_CHECK
non_interactive_check_based_tests = \
sysdb-tests \
diff --git a/server/upgrade/upgrade_config.py b/server/upgrade/upgrade_config.py
new file mode 100644
index 000000000..412fad534
--- /dev/null
+++ b/server/upgrade/upgrade_config.py
@@ -0,0 +1,352 @@
+#!/usr/bin/python
+#coding=utf-8
+
+# SSSD
+#
+# upgrade_config.py
+#
+# Copyright (C) Jakub Hrozek <jhrozek@redhat.com> 2009
+#
+# 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; either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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, see <http://www.gnu.org/licenses/>.
+
+import sys
+import shutil
+import traceback
+from ConfigParser import RawConfigParser
+from optparse import OptionParser
+
+class SSSDConfigParser(RawConfigParser):
+ def raw_set(self, section, option):
+ " set without interpolation "
+ pass
+
+ def raw_get(self, section, option):
+ " get without interpolation "
+ return self._sections[section].get(option)
+
+ def _write_section(self, section, fp):
+ fp.write("[%s]\n" % section)
+ for (key, value) in sorted(self._sections[section].items()):
+ if key != "__name__":
+ fp.write("%s = %s\n" %
+ (key, str(value).replace('\n', '\n\t')))
+ fp.write("\n")
+
+ def write(self, fp):
+ """
+ SSSD Config file uses a logical order of sections
+ ConfigParser does not allow sorting the sections, so
+ we hackishly sort them here..
+ """
+ # Write SSSD first
+ if "sssd" in self._sections:
+ self._write_section("sssd", fp)
+ if (self.has_option('sssd', 'domains')):
+ active_domains = [s.strip() for s in self.get('sssd','domains').split(',')]
+ else:
+ #There were no active domains configured
+ active_domains = []
+ del self._sections["sssd"]
+ # Write the other services
+ for service in [ s for s in self._sections if not s.startswith('domain/') ]:
+ self._write_section(service, fp)
+ del self._sections[service]
+
+ # Write the domains in the order that is specified in domains =
+ for dom in active_domains:
+ self._write_section('domain/%s' % dom, fp)
+ del self._sections['domain/%s' % dom]
+
+ # Write inactive domains
+ for section in sorted(self._sections):
+ self._write_section(section, fp)
+
+class SSSDConfigFile(object):
+ def __init__(self, file_name):
+ self.file_name = file_name
+ self._config = SSSDConfigParser()
+ self._new_config = SSSDConfigParser()
+ self._config.read(file_name)
+
+ def get_version(self):
+ " Guess if we are looking at v1 config file "
+ if not self._config.has_section('sssd'):
+ return 1
+ if not self._config.has_option('sssd', 'config_file_version'):
+ return 1
+ return self._config.getint('sssd', 'config_file_version')
+
+ def _backup_file(self):
+ " Copy the file we operate on to a backup location "
+ shutil.copy(self.file_name, self.file_name+".bak")
+
+ def _migrate_if_exists(self, to_section, to_option, from_section, from_option):
+ """
+ Move value of parameter from one section to another, renaming the parameter
+ """
+ if self._config.has_section(from_section) and \
+ self._config.has_option(from_section, from_option):
+ self._new_config.set(to_section, to_option,
+ self._config.get(from_section, from_option))
+
+ def _migrate_kw(self, to_section, from_section, new_old_dict):
+ """
+ Move value of parameter from one section to another according to
+ mapping in ``new_old_dict``
+ """
+ for new, old in new_old_dict.items():
+ self._migrate_if_exists(to_section, new, from_section, old)
+
+ def _migrate_enumerate(self, to_section, from_section):
+ " Enumerate was special as it turned into bool from (0,1,2,3) enum "
+ if self._config.has_section(from_section) and \
+ self._config.has_option(from_section, 'enumerate'):
+ enumvalue = self._config.get(from_section, 'enumerate')
+ if enumvalue.upper() in ['TRUE', 'FALSE']:
+ self._new_config.set(to_section, 'enumerate', enumvalue)
+ else:
+ try:
+ enumvalue = int(enumvalue)
+ except ValueError:
+ raise ValueError('Cannot convert value %s in domain %s' % (enumvalue, from_section))
+
+ if enumvalue == 0:
+ self._new_config.set(to_section, 'enumerate', 'FALSE')
+ elif enumvalue > 0:
+ self._new_config.set(to_section, 'enumerate', 'TRUE')
+ else:
+ raise ValueError('Cannot convert value %s in domain %s' % (enumvalue, from_section))
+
+ def _migrate_domain(self, domain):
+ new_domsec = 'domain/%s' % domain
+ old_domsec = 'domains/%s' % domain
+ self._new_config.add_section(new_domsec)
+
+ # Generic options - new:old
+ generic_kw = { 'min_id' : 'minID',
+ 'max_id': 'maxID',
+ 'timeout': 'timeout',
+ 'magic_private_groups' : 'magicPrivateGroups',
+ 'cache_credentials' : 'cache-credentials',
+ 'id_provider' : 'provider',
+ 'auth_provider' : 'auth-module',
+ 'access_provider' : 'access-module',
+ 'chpass_provider' : 'chpass-module',
+ 'use_fully_qualified_names' : 'useFullyQualifiedNames',
+ }
+ # Proxy options
+ proxy_kw = { 'proxy_pam_target' : 'pam-target',
+ 'proxy_lib_name' : 'libName',
+ }
+ # LDAP options - new:old
+ ldap_kw = { 'ldap_uri' : 'ldapUri',
+ 'ldap_schema' : 'ldapSchema',
+ 'ldap_default_bind_dn' : 'defaultBindDn',
+ 'ldap_default_authtok_type' : 'defaultAuthtokType',
+ 'ldap_default_authtok' : 'defaultAuthtok',
+ 'ldap_user_search_base' : 'userSearchBase',
+ 'ldap_user_search_scope' : 'userSearchScope',
+ 'ldap_user_search_filter' : 'userSearchFilter',
+ 'ldap_user_object_class' : 'userObjectClass',
+ 'ldap_user_name' : 'userName',
+ 'ldap_user_pwd' : 'userPassword',
+ 'ldap_user_uid_number' : 'userUidNumber',
+ 'ldap_user_gid_number' : 'userGidNumber',
+ 'ldap_user_gecos' : 'userGecos',
+ 'ldap_user_home_directory' : 'userHomeDirectory',
+ 'ldap_user_shell' : 'userShell',
+ 'ldap_user_uuid' : 'userUUID',
+ 'ldap_user_principal' : 'userPrincipal',
+ 'ldap_force_upper_case_realm' : 'force_upper_case_realm',
+ 'ldap_user_fullname' : 'userFullname',
+ 'ldap_user_member_of' : 'userMemberOf',
+ 'ldap_user_modify_timestamp' : 'modifyTimestamp',
+ 'ldap_group_search_base' : 'groupSearchBase',
+ 'ldap_group_search_scope' : 'groupSearchScope',
+ 'ldap_group_search_filter' : 'groupSearchFilter',
+ 'ldap_group_object_class' : 'groupObjectClass',
+ 'ldap_group_name' : 'groupName',
+ 'ldap_group_pwd' : 'userPassword',
+ 'ldap_group_gid_number' : 'groupGidNumber',
+ 'ldap_group_member' : 'groupMember',
+ 'ldap_group_uuid' : 'groupUUID',
+ 'ldap_group_modify_timestamp' : 'modifyTimestamp',
+ 'ldap_network_timeout' : 'network_timeout',
+ 'ldap_offline_timeout' : 'offline_timeout',
+ 'ldap_enumeration_refresh_timeout' : 'enumeration_refresh_timeout',
+ 'ldap_stale_time' : 'stale_time',
+ 'ldap_opt_timeout' : 'opt_timeout',
+ 'ldap_tls_reqcert' : 'tls_reqcert',
+ }
+ krb5_kw = { 'krb5_kdcip' : 'krb5KDCIP',
+ 'krb5_realm' : 'krb5REALM',
+ 'krb5_try_simple_upn' : 'krb5try_simple_upn',
+ 'krb5_changepw_principle' : 'krb5changepw_principle',
+ 'krb5_ccachedir' : 'krb5ccache_dir',
+ 'krb5_auth_timeout' : 'krb5auth_timeout',
+ 'krb5_ccname_template' : 'krb5ccname_template',
+ }
+ user_defaults_kw = { 'default_shell' : 'defaultShell',
+ 'base_directory' : 'baseDirectory',
+ }
+
+ self._migrate_enumerate(new_domsec, old_domsec)
+ self._migrate_kw(new_domsec, old_domsec, generic_kw)
+ self._migrate_kw(new_domsec, old_domsec, proxy_kw)
+ self._migrate_kw(new_domsec, old_domsec, ldap_kw)
+ self._migrate_kw(new_domsec, old_domsec, krb5_kw)
+
+ # if domain was local, update with parameters from [user_defaults]
+ if self._new_config.get(new_domsec, 'id_provider') == 'local':
+ self._migrate_kw(new_domsec, 'user_defaults', user_defaults_kw)
+
+
+ def _migrate_domains(self):
+ for domain in [ s.replace('domains/','') for s in self._config.sections() if s.startswith("domains/") ]:
+ domain = domain.strip()
+ self._migrate_domain(domain)
+
+ def upgrade_v2(self, out_file_name, backup=True):
+ """
+ Upgrade the config file to V2 format and write the result into
+ ``out_file_name```.
+ """
+ if backup:
+ self._backup_file()
+
+ # [service] - options common to all services, no section as in v1
+ service_kw = { 'reconnection_retries' : 'reconnection_retries',
+ 'debug_level' : 'debug-level',
+ 'debug_timestamps' : 'debug-timestamps',
+ 'command' : 'command',
+ 'timeout' : 'timeout',
+ }
+
+ # [sssd] - monitor service
+ self._new_config.add_section('sssd')
+ self._new_config.set('sssd', 'config_file_version', '2')
+ self._migrate_if_exists('sssd', 'domains',
+ 'domains', 'domains')
+ self._migrate_if_exists('sssd', 'services',
+ 'services', 'activeServices')
+ self._migrate_if_exists('sssd', 'sbus_timeout',
+ 'services/monitor', 'sbusTimeout')
+ self._migrate_if_exists('sssd', 're_expression',
+ 'names', 're-expression')
+ self._migrate_if_exists('sssd', 're_expression',
+ 'names', 'full-name-format')
+ self._migrate_kw('sssd', 'services', service_kw)
+ self._migrate_kw('sssd', 'services/monitor', service_kw)
+
+ # [nss] - Name service
+ self._new_config.add_section('nss')
+ nss_kw = { 'enum_cache_timeout' : 'EnumCacheTimeout',
+ 'entry_cache_timeout' : 'EntryCacheTimeout',
+ 'entry_cache_nowait_timeout' : 'EntryCacheNoWaitRefreshTimeout',
+ 'entry_negative_timeout ' : 'EntryNegativeTimeout',
+ 'filter_users' : 'filterUsers',
+ 'filter_groups' : 'filterGroups',
+ 'filter_users_in_groups' : 'filterUsersInGroups',
+ }
+ nss_kw.update(service_kw)
+ self._migrate_kw('nss', 'services', service_kw)
+ self._migrate_kw('nss', 'services/nss', nss_kw)
+
+ # [pam] - Authentication service
+ self._new_config.add_section('pam')
+ pam_kw = {}
+ pam_kw.update(service_kw)
+ self._migrate_kw('pam', 'services', service_kw)
+ self._migrate_kw('pam', 'services/pam', pam_kw)
+
+ # [dp] - Data provider
+ self._new_config.add_section('dp')
+ provider_kw = {'sbus_timeout' : 'sbusTimeout',
+ }
+ provider_kw.update(service_kw)
+ self._migrate_kw('dp', 'services', service_kw)
+ self._migrate_kw('dp', 'services/dp', provider_kw)
+
+ # Migrate domains
+ self._migrate_domains()
+
+ # all done, write the file
+ of = open(out_file_name, "wb")
+ self._new_config.write(of)
+
+def parse_options():
+ parser = OptionParser()
+ parser.add_option("-f", "--file",
+ dest="filename", default="/etc/sssd/sssd.conf",
+ help="Set input file to FILE", metavar="FILE")
+ parser.add_option("-o", "--outfile",
+ dest="outfile", default=None,
+ help="Set output file to OUTFILE", metavar="OUTFILE")
+ parser.add_option("", "--no-backup", action="store_false",
+ dest="backup", default=True,
+ help="""Do not provide backup file after conversion.
+The script copies the original file with the suffix .bak
+by default""")
+ parser.add_option("-v", "--verbose", action="store_true",
+ dest="verbose", default=False,
+ help="Be verbose")
+ (options, args) = parser.parse_args()
+ if len(args) > 0:
+ print >>sys.stderr, "Stray arguments: %s" % ' '.join([a for a in args])
+ return None
+
+ # do the conversion in place by default
+ if not options.outfile:
+ options.outfile = options.filename
+
+ return options
+
+def verbose(msg, verbose):
+ if verbose:
+ print msg
+
+def main():
+ options = parse_options()
+ if not options:
+ print >>sys.stderr, "Cannot parse options"
+ return 1
+
+ try:
+ config = SSSDConfigFile(options.filename)
+ except SSSDConfigParser.ParsingError:
+ print >>sys.stderr, "Cannot parse config file %s" % options.filename
+ return 1
+
+ if config.get_version() == 2:
+ #Config file is already at the correct version. No upgrade needed
+ print >>sys.stderr, "Config file is v2. No upgrade required."
+ return 0
+
+ if config.get_version() != 1:
+ print >>sys.stderr, "Can only upgrade from v1 to v2, file %s looks like version %d" % (options.filename, config.get_version())
+ return 1
+
+ try:
+ config.upgrade_v2(options.outfile, options.backup)
+ except Exception, e:
+ print "ERROR: %s" % e
+ verbose(traceback.format_exc(), options.verbose)
+ return 1
+
+ return 0
+
+if __name__ == "__main__":
+ ret = main()
+ sys.exit(ret)
+