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
Exemple #2
0
    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)
Exemple #4
0
 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)
Exemple #5
0
    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
Exemple #8
0
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
Exemple #13
0
 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": []}
Exemple #14
0
 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
Exemple #15
0
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()
Exemple #17
0
	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()