def _include_version(major, minor=None, patchlevel=None): bigger_as_start = start is None or start <= UCS_Version( (major, max_minor if minor is None else minor, max_patchlevel if patchlevel is None else patchlevel)) smaller_as_end = end is None or end >= UCS_Version( (major, 0 if minor is None else minor, 0 if patchlevel is None else patchlevel)) return bigger_as_start and smaller_as_end
def test_filter_releases_json(self, testdir): with open(join(testdir, 'mockup_upstream_releases.json'), 'r') as upstream_releases_fp: upstream_releases = json.load(upstream_releases_fp) with open(join(testdir, 'mockup_mirror_releases.json'), 'r') as mirror_releases_fp: expected_mirror_releases = json.load(mirror_releases_fp) mirrored_releases = deepcopy(upstream_releases) M.filter_releases_json(mirrored_releases, start=UCS_Version((3, 1, 1)), end=UCS_Version((4, 1, 0))) assert mirrored_releases == expected_mirror_releases
def main(): # type: () -> None parser = argparse.ArgumentParser( description='Generates a valid ucs-releases.json.') parser.add_argument( 'repodir', help='path to repository, where ucs-releases.json is created/updated.') parser.add_argument( 'versions', nargs='*', help= 'a UCS version to be added to the ucs-releases.json. If omitted, the automatic UCS version detection is activated!' ) args = parser.parse_args() releases = [] if args.versions: for version in args.versions: mmp = UCS_Version(version) releases.append((mmp.major, mmp.minor, mmp.patchlevel)) else: distdir = os.path.join(args.repodir, 'dists') for dirname in os.listdir(distdir): if not os.path.isdir(os.path.join(distdir, dirname)): continue if not dirname.startswith('ucs'): continue if len(dirname) != 6: raise Exception( 'unexpected dirname length: {}'.format(dirname)) major, minor, patchlevel = [int(x) for x in dirname[3:]] releases.append((major, minor, patchlevel)) gen_releases(args.repodir, releases)
def test_filter_releases_json(self): self.maxDiff = None with open( os.path.join(os.path.dirname(__file__), 'mockup_upstream_releases.json'), 'r') as upstream_releases_fp: upstream_releases = json.load(upstream_releases_fp) with open( os.path.join(os.path.dirname(__file__), 'mockup_mirror_releases.json'), 'r') as mirror_releases_fp: expected_mirror_releases = json.load(mirror_releases_fp) mirrored_releases = deepcopy(upstream_releases) M.filter_releases_json(mirrored_releases, start=UCS_Version((3, 1, 1)), end=UCS_Version((4, 1, 0))) self.assertEqual(mirrored_releases, expected_mirror_releases)
def __init__(self, check_access=True): # type: (bool) -> None """ Create new mirror with settings from UCR. :param bool check_access: Check if repository server is reachable on init. :raises ConfigurationError: if configured server is not available immediately. """ UniventionUpdater.__init__(self, check_access) self.log = logging.getLogger('updater.Mirror') self.log.addHandler(logging.NullHandler()) self.repository_path = self.configRegistry.get( 'repository/mirror/basepath', '/var/lib/univention-repository') version_end = self.configRegistry.get( 'repository/mirror/version/end') or self.current_version self.version_end = UCS_Version(version_end) version_start = self.configRegistry.get( 'repository/mirror/version/start') or (self.current_version.major, 0, 0) self.version_start = UCS_Version(version_start)
def release_update_available(self, ucs_version=None, errorsto='stderr'): """ Check if an update is available for the `ucs_version`. :param str ucs_version: The UCS release to check. :param str errorsto: Select method of reporting errors; on of 'stderr', 'exception', 'none'. :returns: The next UCS release or None. :rtype: str or None """ if not ucs_version: ucs_version = self.current_version return self.get_next_version(UCS_Version(ucs_version), [], errorsto)
def get_blocking_apps(cls, ucs_version): ''' checks if update is possible for this app ''' ucs_version = UCS_Version(ucs_version + '-0') next_minor = '%(major)d.%(minor)d' % ucs_version next_version = '%(major)d.%(minor)d-%(patchlevel)d' % ucs_version current_version = UCS_Version(ucr_get('version/version') + '-0') current_minor = '%(major)d.%(minor)d' % current_version # if this is just a patchlevel update, everything is fine if current_minor >= next_minor: return {} # first, update the local cache and get current apps update = get_action('update') update.logger = get_logfile_logger('update-check') update.call() current_cache = Apps(locale='en') # get apps in next version with TemporaryDirectory() as tempdir: update.call(ucs_version=next_minor, cache_dir=tempdir, just_get_cache=True) next_cache = AppCenterCache.build(ucs_versions=next_minor, server=default_server(), locale='en', cache_dir=tempdir) next_apps = next_cache.get_every_single_app() # check apps blocking_apps = dict() for app in current_cache.get_all_locally_installed_apps(): if not cls.app_can_update(app, next_version, next_apps): cls.debug('app %s is not available for %s' % (app.id, next_version)) blocking_apps[app.component_id] = app.name else: cls.debug('app %s is available for %s' % (app.id, next_version)) return blocking_apps
class TestFilter(object): """Unit test for univention.updater.mirror.filter_releases_json""" RELEASES = json.loads(gen_releases([(5, 0, 0), (5, 0, 1), (5, 0, 2)])) VER4, VER5, VER6 = (UCS_Version((major, 0, 0)) for major in [4, 5, 6]) def test_filter_releases_json(self, testdir): with open(join(testdir, 'mockup_upstream_releases.json'), 'r') as upstream_releases_fp: upstream_releases = json.load(upstream_releases_fp) with open(join(testdir, 'mockup_mirror_releases.json'), 'r') as mirror_releases_fp: expected_mirror_releases = json.load(mirror_releases_fp) mirrored_releases = deepcopy(upstream_releases) M.filter_releases_json(mirrored_releases, start=UCS_Version((3, 1, 1)), end=UCS_Version((4, 1, 0))) assert mirrored_releases == expected_mirror_releases def test_unchanged(self): data = deepcopy(self.RELEASES) M.filter_releases_json(data, start=self.VER4, end=self.VER6) assert data == self.RELEASES def test_same(self): data = deepcopy(self.RELEASES) M.filter_releases_json(data, start=self.VER5, end=UCS_Version((5, 0, 2))) assert data == self.RELEASES def test_before(self): data = deepcopy(self.RELEASES) M.filter_releases_json(data, start=self.VER4, end=self.VER4) assert data == {"releases": []} def test_after(self): data = deepcopy(self.RELEASES) M.filter_releases_json(data, start=self.VER6, end=self.VER6) assert data == {"releases": []} def test_empty(self): data = deepcopy(self.RELEASES) M.filter_releases_json(data, start=UCS_Version((5, 0, 3)), end=self.VER6) assert data == {"releases": []}
def list_local_repositories(self, start=None, end=None, maintained=True, unmaintained=False): """ This function returns a sorted list of local (un)maintained repositories. :param start: smallest version that shall be returned. :type start: UCS_Version or None :param end: largest version that shall be returned. :type end: UCS_Version or None :param bool maintained: True if list shall contain maintained repositories. :param bool unmaintained: True if list shall contain unmaintained repositories. :returns: A sorted list of repositories as (directory, UCS_Version, is_maintained) tuples. :rtype: list[tuple(str, UCS_Version, bool)] """ result = [] repobase = os.path.join(self.repository_path, 'mirror') RErepo = re.compile( '^%s/(\d+[.]\d)/(maintained|unmaintained)/(\d+[.]\d+-\d+)$' % (re.escape(repobase), )) for dirname, subdirs, files in os.walk(repobase): match = RErepo.match(dirname) if match: if not maintained and match.group(2) == 'maintained': continue if not unmaintained and match.group(2) == 'unmaintained': continue version = UCS_Version(match.group(3)) if start is not None and version < start: continue if end is not None and end < version: continue result.append( (dirname, version, match.group(2) == 'maintained')) result.sort(key=itemgetter(1)) return result
def main() -> None: options = parse_args() if options.silent: sys.stdout = open(devnull, 'w') with UpdaterLock(): check_preconditions(options) current_ucs_version = "%(version/version)s-%(version/patchlevel)s" % configRegistry options.version = UCS_Version(current_ucs_version) prepare(options) # set repository server to local system ucr_set = [ 'repository/online/server=%(hostname)s.%(domainname)s' % configRegistry, 'repository/mirror/version/start?%s' % current_ucs_version, ] # set last version contained in repository end = configRegistry.get('repository/mirror/version/end', '').strip() if not end or UCS_Version(end) < options.version: ucr_set.append('repository/mirror/version/end=%s' % options.version) handler_set(ucr_set) # create symbolic link univention-repository try: symlink('.', join(options.base, 'mirror', 'univention-repository')) except EnvironmentError as ex: if ex.errno != errno.EEXIST: raise print('Starting mirror download. This can take a long time!') print( 'Check /var/log/univention/repository.log for the current status') subprocess.call(['univention-repository-update', 'net']) handler_commit([ '/etc/apt/sources.list.d/15_ucs-online-version.list', '/etc/apt/sources.list.d/20_ucs-online-component.list', ]) print( dedent(r""" The local repository has been prepared. The repository can be updated using: univention-repository-update net The local host has been modified to use this local repository. Other hosts must be re-configured by setting the Univention Configuration Registry (UCR) variable 'repository/online/server' to the FQDN of this host. ucr set repository/online/server="%(hostname)s.%(domainname)s" The setting is best set in a domain by defining UCR Policies, which set this variable on all hosts using this repository server. For example: udm policies/repositoryserver create \ --position "cn=repository,cn=update,cn=policies,%(ldap/base)s" \ --set name="%(hostname)s repository" \ --set repositoryServer="%(hostname)s.%(domainname)s" udm container/dc modify \ --dn "%(ldap/base)s" \ --policy-reference "cn=%(hostname)s repository,cn=repository,cn=update,cn=policies,%(ldap/base)s" """ % configRegistry)) if options.version.minor != 0 or options.version.patchlevel != 0: print( dedent(""" An UCS repository must always start with minor version 0, for example with UCS {ver.major}. Please synchronize the repository by using the tool 'univention-repository-update'. """).format(ver=options.version))
def __call__(self, parser, namespace, value, option_string=None): try: UCS_Version(value + '-0') except ValueError as exc: parser.error('--ucs-version ' + str(exc)) setattr(namespace, self.dest, value)
def do_release_update(options: Namespace, checkForUpdates: bool, silent: bool) -> bool: updater = UniventionUpdater() # get next release update version dprint(silent, 'Checking for release updates: ', newline=False) version_next = updater.release_update_available() if not version_next: dprint(silent, 'none') return False if options.updateto and UCS_Version( options.updateto) < UCS_Version(version_next): dprint( silent, '%s is available but updater has been instructed to stop at version %s.' % (version_next, options.updateto)) return False dprint(silent, 'found: UCS %s' % version_next) if checkForUpdates: return True interactive = not (options.noninteractive or checkForUpdates) if interactive and not readcontinue( 'Do you want to update to %s [Y|n]?' % version_next): return False update_status(current_version=updater.current_version, next_version=version_next, status='RUNNING') dprint(silent, 'Starting update to UCS version %s at %s...' % (version_next, time.ctime()), debug=True) dprint(silent, 'Starting update to UCS version %s' % (version_next)) time.sleep(1) params = ['--silent'] if options.ignoressh: params.append('--ignoressh') if options.ignoreterm: params.append('--ignoreterm') retcode = subprocess.call([ '/usr/share/univention-updater/univention-updater', 'net', '--updateto', '%s' % (version_next) ] + params, env=os.environ) if retcode: dprint(silent, 'exitcode of univention-updater: %s' % retcode, debug=True) dprint( silent, 'ERROR: update failed. Please check /var/log/univention/updater.log\n' ) update_status(status='FAILED', errorsource='UPDATE') sys.exit(1) dprint(silent, 'Update to UCS version %s finished at %s...' % (version_next, time.ctime()), debug=True) return True
def test_empty(self): data = deepcopy(self.RELEASES) M.filter_releases_json(data, start=UCS_Version((5, 0, 3)), end=self.VER6) assert data == {"releases": []}
def test_same(self): data = deepcopy(self.RELEASES) M.filter_releases_json(data, start=self.VER5, end=UCS_Version((5, 0, 2))) assert data == self.RELEASES
def handler(dn, new, old): """Handle UDM extension modules""" if new: ocs = new.get('objectClass', []) univentionUCSVersionStart = new.get('univentionUCSVersionStart', [None])[0] univentionUCSVersionEnd = new.get('univentionUCSVersionEnd', [None])[0] current_UCS_version = "%s-%s" % (listener.configRegistry.get('version/version'), listener.configRegistry.get('version/patchlevel')) if univentionUCSVersionStart and UCS_Version(current_UCS_version) < UCS_Version(univentionUCSVersionStart): ud.debug(ud.LISTENER, ud.INFO, '%s: extension %s requires at least UCR version %s.' % (name, new['cn'][0], univentionUCSVersionStart)) new = None elif univentionUCSVersionEnd and UCS_Version(current_UCS_version) > UCS_Version(univentionUCSVersionEnd): ud.debug(ud.LISTENER, ud.INFO, '%s: extension %s specifies compatibility only up to and including UCR version %s.' % (name, new['cn'][0], univentionUCSVersionEnd)) new = None elif old: ocs = old.get('objectClass', []) if 'univentionUDMModule' in ocs: objectclass = 'univentionUDMModule' udm_module_name = 'settings/udm_module' target_subdir = 'univention/admin/handlers' elif 'univentionUDMHook' in ocs: objectclass = 'univentionUDMHook' udm_module_name = 'settings/udm_hook' target_subdir = 'univention/admin/hooks.d' elif 'univentionUDMSyntax' in ocs: objectclass = 'univentionUDMSyntax' udm_module_name = 'settings/udm_syntax' target_subdir = 'univention/admin/syntax.d' else: ud.debug(ud.LISTENER, ud.ERROR, '%s: Undetermined error: unknown objectclass: %s.' % (name, ocs)) if new: new_version = new.get('univentionOwnedByPackageVersion', [None])[0] if not new_version: return new_pkgname = new.get('univentionOwnedByPackage', [None])[0] if not new_pkgname: return if old: # check for trivial changes diff_keys = [key for key in new.keys() if new.get(key) != old.get(key) and key not in ('entryCSN', 'modifyTimestamp', 'modifiersName')] if diff_keys == ['%sActive' % objectclass] and new.get('%sActive' % objectclass)[0] == 'TRUE': ud.debug(ud.LISTENER, ud.INFO, '%s: %s: activation status changed.' % (name, new['cn'][0])) return elif diff_keys == ['univentionAppIdentifier']: ud.debug(ud.LISTENER, ud.INFO, '%s: %s: App identifier changed.' % (name, new['cn'][0])) return if new_pkgname == old.get('univentionOwnedByPackage', [None])[0]: old_version = old.get('univentionOwnedByPackageVersion', ['0'])[0] rc = apt.apt_pkg.version_compare(new_version, old_version) if not rc > -1: ud.debug(ud.LISTENER, ud.WARN, '%s: New version is lower than version of old object (%s), skipping update.' % (name, old_version)) return # ok, basic checks passed, handle the data try: new_object_data = bz2.decompress(new.get('%sData' % objectclass)[0]) except TypeError: ud.debug(ud.LISTENER, ud.ERROR, '%s: Error uncompressing data of object %s.' % (name, dn)) return new_relative_filename = new.get('%sFilename' % objectclass)[0] listener.setuid(0) try: if not install_python_file(objectclass, target_subdir, new_relative_filename, new_object_data): return install_messagecatalog(dn, new, objectclass) if objectclass == 'univentionUDMModule': install_umcregistration(dn, new) install_umcicons(dn, new) finally: listener.unsetuid() elif old: # ok, basic checks passed, handle the change old_relative_filename = old.get('%sFilename' % objectclass)[0] listener.setuid(0) try: remove_python_file(objectclass, target_subdir, old_relative_filename) remove_messagecatalog(dn, old, objectclass) if objectclass == 'univentionUDMModule': remove_umcicons(dn, old) remove_umcregistration(dn, old) finally: listener.unsetuid() # Kill running univention-cli-server and mark new extension object active listener.setuid(0) try: if new: if not listener.configRegistry.get('server/role') == 'domaincontroller_master': # Only set active flag on Master return try: lo, ldap_position = udm_uldap.getAdminConnection() udm_modules.update() udm_module = udm_modules.get(udm_module_name) udm_modules.init(lo, ldap_position, udm_module) try: udm_object = udm_module.object(None, lo, ldap_position, dn) udm_object.open() udm_object['active'] = True udm_object.modify() except udm_errors.ldapError as e: ud.debug(ud.LISTENER, ud.ERROR, '%s: Error modifying %s: %s.' % (name, dn, e)) except udm_errors.noObject as e: ud.debug(ud.LISTENER, ud.ERROR, '%s: Error modifying %s: %s.' % (name, dn, e)) except udm_errors.ldapError as e: ud.debug(ud.LISTENER, ud.ERROR, '%s: Error accessing UDM: %s' % (name, e)) finally: listener.unsetuid()
def handler(dn, new, old): """Handle UDM extension modules""" if new: ocs = new.get('objectClass', []) univentionUCSVersionStart = new.get('univentionUCSVersionStart', [b''])[0].decode('UTF-8') univentionUCSVersionEnd = new.get('univentionUCSVersionEnd', [b''])[0].decode('UTF-8') elif old: ocs = old.get('objectClass', []) if b'univentionUDMModule' in ocs: objectclass = 'univentionUDMModule' udm_module_name = 'settings/udm_module' target_subdir = 'univention/admin/handlers' elif b'univentionUDMHook' in ocs: objectclass = 'univentionUDMHook' udm_module_name = 'settings/udm_hook' target_subdir = 'univention/admin/hooks.d' elif b'univentionUDMSyntax' in ocs: objectclass = 'univentionUDMSyntax' udm_module_name = 'settings/udm_syntax' target_subdir = 'univention/admin/syntax.d' else: ud.debug( ud.LISTENER, ud.ERROR, '%s: Undetermined error: unknown objectclass: %s.' % (name, ocs)) # Bug #51622 for UCS 5.0 update: if new and not old: if listener.configRegistry.get( 'server/role') == 'domaincontroller_master': # Remove objects that don't signal Python3 support cmp_start_vs_50 = apt.apt_pkg.version_compare( univentionUCSVersionStart, "5.0") # -1 if univentionUCSVersionStart is unset # cmp_end_vs_499 = apt.apt_pkg.version_compare(univentionUCSVersionEnd, "4.99") # Keep object if cmp_start_vs_50 >= 0 [i.e. Py3] or (cmp_start_vs_50 < and univentionUCSVersionEnd) [or cmp_end_vs_499 == 0] # Otherwise remove it: if cmp_start_vs_50 < 0 and not univentionUCSVersionEnd: ud.debug( ud.LISTENER, ud.WARN, '%s: Removing incompatible extension %s (univentionUCSVersionStart=%r and univentionUCSVersionEnd not set).' % (name, new['cn'][0].decode('UTF-8'), univentionUCSVersionStart)) remove_object(udm_module_name, dn) return if new: current_UCS_version = "%s-%s" % ( listener.configRegistry.get('version/version'), listener.configRegistry.get('version/patchlevel')) if univentionUCSVersionStart and UCS_Version( current_UCS_version) < UCS_Version(univentionUCSVersionStart): ud.debug( ud.LISTENER, ud.INFO, '%s: extension %s requires at least UCS version %s.' % (name, new['cn'][0].decode('UTF-8'), univentionUCSVersionStart)) # Trigger remove on this system old = old or new new = None elif univentionUCSVersionEnd and UCS_Version( current_UCS_version) > UCS_Version(univentionUCSVersionEnd): ud.debug( ud.LISTENER, ud.INFO, '%s: extension %s specifies compatibility only up to and including UCR version %s.' % (name, new['cn'][0].decode('UTF-8'), univentionUCSVersionEnd)) # Trigger remove on this system old = old or new new = None old_relative_filename = None if old: old_relative_filename = old['%sFilename' % objectclass][0].decode('UTF-8') if new: new_version = new.get('univentionOwnedByPackageVersion', [b''])[0].decode('UTF-8') if not new_version: return new_pkgname = new.get('univentionOwnedByPackage', [None])[0] if not new_pkgname: return if old: # check for trivial changes diff_keys = [ key for key in new.keys() if new.get(key) != old.get(key) and key not in ( 'entryCSN', 'modifyTimestamp', 'modifiersName') ] if diff_keys == ['%sActive' % objectclass] and new.get( '%sActive' % objectclass)[0] == b'TRUE': ud.debug( ud.LISTENER, ud.INFO, '%s: %s: activation status changed.' % (name, new['cn'][0])) return elif diff_keys == ['univentionAppIdentifier']: ud.debug( ud.LISTENER, ud.INFO, '%s: %s: App identifier changed.' % (name, new['cn'][0].decode('UTF-8'))) return if new_pkgname == old.get('univentionOwnedByPackage', [None])[0]: old_version = old.get('univentionOwnedByPackageVersion', [b'0'])[0].decode('UTF-8') rc = apt.apt_pkg.version_compare(new_version, old_version) if not rc > -1: ud.debug( ud.LISTENER, ud.WARN, '%s: New version is lower than version of old object (%s), skipping update.' % (name, old_version)) return # ok, basic checks passed, handle the data try: new_object_data = bz2.decompress( new.get('%sData' % objectclass)[0]) except TypeError: ud.debug(ud.LISTENER, ud.ERROR, '%s: Error uncompressing data of object %s.' % (name, dn)) return new_relative_filename = new['%sFilename' % objectclass][0].decode('UTF-8') listener.setuid(0) try: if old_relative_filename and old_relative_filename != new_relative_filename: remove_python_file(objectclass, target_subdir, old_relative_filename) if not install_python_file(objectclass, target_subdir, new_relative_filename, new_object_data): return install_messagecatalog(dn, new, objectclass) install_umcmessagecatalogs(new, old) if objectclass == 'univentionUDMModule': install_umcregistration(dn, new) install_umcicons(dn, new) finally: listener.unsetuid() elif old: # ok, basic checks passed, handle the change listener.setuid(0) try: remove_python_file(objectclass, target_subdir, old_relative_filename) remove_messagecatalog(dn, old, objectclass) remove_umcmessagecatalogs(old) if objectclass == 'univentionUDMModule': remove_umcicons(dn, old) remove_umcregistration(dn, old) finally: listener.unsetuid() # TODO: Kill running univention-cli-server? # Mark new extension object active listener.setuid(0) try: if new: if not listener.configRegistry.get( 'server/role') == 'domaincontroller_master': # Only set active flag on Primary return try: lo, ldap_position = udm_uldap.getAdminConnection() udm_modules.update() udm_module = udm_modules.get(udm_module_name) udm_modules.init(lo, ldap_position, udm_module) try: udm_object = udm_module.object(None, lo, ldap_position, dn) udm_object.open() udm_object['active'] = True udm_object.modify() except udm_errors.ldapError as exc: ud.debug(ud.LISTENER, ud.ERROR, '%s: Error modifying %s: %s.' % (name, dn, exc)) except udm_errors.noObject as exc: ud.debug(ud.LISTENER, ud.ERROR, '%s: Error modifying %s: %s.' % (name, dn, exc)) except udm_errors.ldapError as exc: ud.debug(ud.LISTENER, ud.ERROR, '%s: Error accessing UDM: %s' % (name, exc)) finally: listener.unsetuid()
def handler(self, dn, new, old, name=None): """Handle LDAP ACL extensions on Master, Backup and Slave""" if not listener.configRegistry.get('ldap/server/type'): return if not listener.configRegistry.get('server/role') in ('domaincontroller_master'): # new, ignore first *inactive* appearance, has to be activated on master first if new and not old and new.get('univentionLDAPACLActive', ['FALSE'])[0] != 'TRUE': ud.debug(ud.LISTENER, ud.PROCESS, '%s: ignore first appearance of %s, not yet activated' % (name, dn)) return # ignore change unless (re) activated if new and old: if not new.get('univentionLDAPACLActive', ['FALSE'])[0] == 'TRUE': ud.debug(ud.LISTENER, ud.PROCESS, '%s: ignore modify of %s, not yet activated' % (name, dn)) return # Check UCS version requirements first and skip new if they are not met. if new: univentionUCSVersionStart = new.get('univentionUCSVersionStart', [None])[0] univentionUCSVersionEnd = new.get('univentionUCSVersionEnd', [None])[0] current_UCS_version = "%s-%s" % (listener.configRegistry.get('version/version'), listener.configRegistry.get('version/patchlevel')) if univentionUCSVersionStart and UCS_Version(current_UCS_version) < UCS_Version(univentionUCSVersionStart): ud.debug(ud.LISTENER, ud.INFO, '%s: extension %s requires at least UCR version %s.' % (name, new['cn'][0], univentionUCSVersionStart)) new = None elif univentionUCSVersionEnd and UCS_Version(current_UCS_version) > UCS_Version(univentionUCSVersionEnd): ud.debug(ud.LISTENER, ud.INFO, '%s: extension %s specifies compatibility only up to and including UCR version %s.' % (name, new['cn'][0], univentionUCSVersionEnd)) new = None if new: new_version = new.get('univentionOwnedByPackageVersion', [None])[0] if not new_version: return new_pkgname = new.get('univentionOwnedByPackage', [None])[0] if not new_pkgname: return ud.debug(ud.LISTENER, ud.PROCESS, '%s: %s active? %s' % (name, dn, new.get('univentionLDAPACLActive'))) if old: # check for trivial changes diff_keys = [key for key in new.keys() if new.get(key) != old.get(key) and key not in ('entryCSN', 'modifyTimestamp', 'modifiersName')] if diff_keys == ['univentionLDAPACLActive'] and new.get('univentionLDAPACLActive')[0] == 'TRUE': # ignore status change on master, already activated if listener.configRegistry.get('server/role') in ('domaincontroller_master'): ud.debug(ud.LISTENER, ud.INFO, '%s: extension %s: activation status changed.' % (name, new['cn'][0])) return elif diff_keys == ['univentionAppIdentifier']: ud.debug(ud.LISTENER, ud.INFO, '%s: extension %s: App identifier changed.' % (name, new['cn'][0])) return ud.debug(ud.LISTENER, ud.INFO, '%s: extension %s: changed attributes: %s' % (name, new['cn'][0], diff_keys)) if new_pkgname == old.get('univentionOwnedByPackage', [None])[0]: old_version = old.get('univentionOwnedByPackageVersion', ['0'])[0] rc = apt.apt_pkg.version_compare(new_version, old_version) if not rc > -1: ud.debug(ud.LISTENER, ud.WARN, '%s: New version is lower than version of old object (%s), skipping update.' % (name, old_version)) return try: new_object_data = bz2.decompress(new.get('univentionLDAPACLData')[0]) except TypeError: ud.debug(ud.LISTENER, ud.ERROR, '%s: Error uncompressing data of object %s.' % (name, dn)) return new_basename = new.get('univentionLDAPACLFilename')[0] new_filename = os.path.join(self.ucr_slapd_conf_subfile_dir, new_basename) listener.setuid(0) try: backup_filename = None backup_ucrinfo_filename = None backup_backlink_filename = None if old: old_filename = os.path.join(self.ucr_slapd_conf_subfile_dir, old.get('univentionLDAPACLFilename')[0]) if os.path.exists(old_filename): backup_fd, backup_filename = tempfile.mkstemp() ud.debug(ud.LISTENER, ud.INFO, '%s: Moving old file %s to %s.' % (name, old_filename, backup_filename)) try: shutil.move(old_filename, backup_filename) except IOError: ud.debug(ud.LISTENER, ud.WARN, '%s: Error renaming old file %s, removing it.' % (name, old_filename)) os.unlink(old_filename) backup_filename = None os.close(backup_fd) # plus the old backlink file old_backlink_filename = "%s.info" % old_filename if os.path.exists(old_backlink_filename): backup_backlink_fd, backup_backlink_filename = tempfile.mkstemp() ud.debug(ud.LISTENER, ud.INFO, '%s: Moving old backlink file %s to %s.' % (name, old_backlink_filename, backup_backlink_filename)) try: shutil.move(old_backlink_filename, backup_backlink_filename) except IOError: ud.debug(ud.LISTENER, ud.WARN, '%s: Error renaming old backlink file %s, removing it.' % (name, old_backlink_filename)) os.unlink(old_backlink_filename) backup_backlink_filename = None os.close(backup_backlink_fd) # and the old UCR registration old_ucrinfo_filename = os.path.join(self.ucr_info_basedir, "%s%s.info" % (self.file_prefix, old.get('univentionLDAPACLFilename')[0])) if os.path.exists(old_ucrinfo_filename): backup_ucrinfo_fd, backup_ucrinfo_filename = tempfile.mkstemp() ud.debug(ud.LISTENER, ud.INFO, '%s: Moving old UCR info file %s to %s.' % (name, old_ucrinfo_filename, backup_ucrinfo_filename)) try: shutil.move(old_ucrinfo_filename, backup_ucrinfo_filename) except IOError: ud.debug(ud.LISTENER, ud.WARN, '%s: Error renaming old UCR info file %s, removing it.' % (name, old_ucrinfo_filename)) os.unlink(old_ucrinfo_filename) backup_ucrinfo_filename = None os.close(backup_ucrinfo_fd) if not os.path.isdir(self.ucr_slapd_conf_subfile_dir): if os.path.exists(self.ucr_slapd_conf_subfile_dir): ud.debug(ud.LISTENER, ud.WARN, '%s: Directory name %s occupied, renaming blocking file.' % (name, self.ucr_slapd_conf_subfile_dir)) shutil.move(self.ucr_slapd_conf_subfile_dir, "%s.bak" % self.ucr_slapd_conf_subfile_dir) ud.debug(ud.LISTENER, ud.INFO, '%s: Create directory %s.' % (name, self.ucr_slapd_conf_subfile_dir)) os.makedirs(self.ucr_slapd_conf_subfile_dir, 0o755) # Create new extension file try: ud.debug(ud.LISTENER, ud.INFO, '%s: Writing new extension file %s.' % (name, new_filename)) with open(new_filename, 'w') as f: f.write(new_object_data) except IOError: ud.debug(ud.LISTENER, ud.ERROR, '%s: Error writing file %s.' % (name, new_filename)) return # plus backlink file try: new_backlink_filename = "%s.info" % new_filename ud.debug(ud.LISTENER, ud.INFO, '%s: Writing backlink file %s.' % (name, new_backlink_filename)) with open(new_backlink_filename, 'w') as f: f.write("%s\n" % dn) except IOError: ud.debug(ud.LISTENER, ud.ERROR, '%s: Error writing backlink file %s.' % (name, new_backlink_filename)) return # and UCR registration try: new_ucrinfo_filename = os.path.join(self.ucr_info_basedir, "%s%s.info" % (self.file_prefix, new.get('univentionLDAPACLFilename')[0])) ud.debug(ud.LISTENER, ud.INFO, '%s: Writing UCR info file %s.' % (name, new_ucrinfo_filename)) with open(new_ucrinfo_filename, 'w') as f: f.write("Type: multifile\nMultifile: etc/ldap/slapd.conf\n\nType: subfile\nMultifile: etc/ldap/slapd.conf\nSubfile: etc/ldap/slapd.conf.d/%s\n" % new_basename) except IOError: ud.debug(ud.LISTENER, ud.ERROR, '%s: Error writing UCR info file %s.' % (name, new_ucrinfo_filename)) return # Commit to slapd.conf ucr = ConfigRegistry() ucr.load() ucr_handlers = configHandlers() ucr_handlers.load() ucr_handlers.update() ucr_handlers.commit(ucr, ['/etc/ldap/slapd.conf']) # validate p = subprocess.Popen(['/usr/sbin/slaptest', '-u'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True) stdout, stderr = p.communicate() if p.returncode != 0: ud.debug(ud.LISTENER, ud.ERROR, '%s: slapd.conf validation failed:\n%s.' % (name, stdout)) # Revert changes ud.debug(ud.LISTENER, ud.ERROR, '%s: Removing new file %s.' % (name, new_filename)) os.unlink(new_filename) os.unlink(new_backlink_filename) os.unlink(new_ucrinfo_filename) if backup_filename: ud.debug(ud.LISTENER, ud.ERROR, '%s: Restoring previous file %s.' % (name, old_filename)) try: shutil.move(backup_filename, old_filename) os.close(backup_fd) except IOError: ud.debug(ud.LISTENER, ud.ERROR, '%s: Error reverting to old file %s.' % (name, old_filename)) # plus backlink file if backup_backlink_filename: ud.debug(ud.LISTENER, ud.ERROR, '%s: Restoring previous backlink file %s.' % (name, old_backlink_filename)) try: shutil.move(backup_backlink_filename, old_backlink_filename) os.close(backup_backlink_fd) except IOError: ud.debug(ud.LISTENER, ud.ERROR, '%s: Error reverting to old backlink file %s.' % (name, old_backlink_filename)) # and the old UCR registration if backup_ucrinfo_filename: ud.debug(ud.LISTENER, ud.ERROR, '%s: Restoring previous UCR info file %s.' % (name, old_ucrinfo_filename)) try: shutil.move(backup_ucrinfo_filename, old_ucrinfo_filename) os.close(backup_ucrinfo_fd) except IOError: ud.debug(ud.LISTENER, ud.ERROR, '%s: Error reverting to old UCR info file %s.' % (name, old_ucrinfo_filename)) # Commit and exit ucr_handlers.commit(ucr, ['/etc/ldap/slapd.conf']) return ud.debug(ud.LISTENER, ud.INFO, '%s: validation successful.' % (name,)) # cleanup backup if backup_filename: ud.debug(ud.LISTENER, ud.INFO, '%s: Removing backup of old file %s.' % (name, backup_filename)) os.unlink(backup_filename) os.close(backup_fd) # plus backlink file if backup_backlink_filename: ud.debug(ud.LISTENER, ud.INFO, '%s: Removing backup of old backlink file %s.' % (name, backup_backlink_filename)) os.unlink(backup_backlink_filename) os.close(backup_backlink_fd) # and the old UCR registration if backup_ucrinfo_filename: ud.debug(ud.LISTENER, ud.INFO, '%s: Removing backup of old UCR info file %s.' % (name, backup_ucrinfo_filename)) os.unlink(backup_ucrinfo_filename) os.close(backup_ucrinfo_fd) self._todo_list.append(dn) self._do_reload = True finally: listener.unsetuid() elif old: old_filename = os.path.join(self.ucr_slapd_conf_subfile_dir, old.get('univentionLDAPACLFilename')[0]) # plus backlink file old_backlink_filename = "%s.info" % old_filename # and the old UCR registration old_ucrinfo_filename = os.path.join(self.ucr_info_basedir, "%s%s.info" % (self.file_prefix, old.get('univentionLDAPACLFilename')[0])) if os.path.exists(old_filename): listener.setuid(0) try: ud.debug(ud.LISTENER, ud.INFO, '%s: Removing extension %s.' % (name, old['cn'][0])) if os.path.exists(old_ucrinfo_filename): os.unlink(old_ucrinfo_filename) if os.path.exists(old_backlink_filename): os.unlink(old_backlink_filename) os.unlink(old_filename) ucr = ConfigRegistry() ucr.load() ucr_handlers = configHandlers() ucr_handlers.load() ucr_handlers.update() ucr_handlers.commit(ucr, ['/etc/ldap/slapd.conf']) self._todo_list.append(dn) self._do_reload = True finally: listener.unsetuid()