Exemple #1
0
def update_schema(schema_files, ldapi=False, dm_password=None,):
    """Update schema to match the given ldif files

    Schema elements present in the LDIF files but missing from the DS schema
    are added.
    Schema elements that differ between LDIF files and DS schema are updated
    to match the LDIF files. The comparison ignores tags that python-ldap's
    schema parser does not understand (such as X-ORIGIN).
    Extra elements present only in the DS schema are left untouched.

    An X-ORIGIN tag containing the current IPA version is added to all new
    and updated schema elements.

    :param schema_files: List of filenames to update from
    :param ldapi: if true, use ldapi to connect
    :param dm_password: directory manager password

    :return:
        True if modifications were made
    """
    SCHEMA_ELEMENT_CLASSES_KEYS = [x[0] for x in SCHEMA_ELEMENT_CLASSES]

    conn = connect(ldapi=ldapi, dm_password=dm_password,
                   realm=api.env.realm,
                   fqdn=installutils.get_fqdn())

    old_schema = conn.schema


    schema_entry = conn.get_entry(DN(('cn', 'schema')),
                                  SCHEMA_ELEMENT_CLASSES_KEYS)

    modified = False

    # The exact representation the DS gives us for each OID
    # (for debug logging)
    old_entries_by_oid = {cls(str(attr)).oid: str(attr)
                          for (attrname, cls) in SCHEMA_ELEMENT_CLASSES
                          for attr in schema_entry[attrname]}

    for filename in schema_files:
        log.debug('Processing schema LDIF file %s', filename)
        dn, new_schema = ldap.schema.subentry.urlfetch(filename)

        for attrname, cls in SCHEMA_ELEMENT_CLASSES:
            for oids_set in _get_oid_dependency_order(new_schema, cls):
                # Set of all elements of this class, as strings given by the DS
                new_elements = []
                for oid in oids_set:
                    new_obj = new_schema.get_obj(cls, oid)
                    old_obj = old_schema.get_obj(cls, oid)
                    # Compare python-ldap's sanitized string representations
                    # to see if the value is different
                    # This can give false positives, e.g. with case differences
                    # in case-insensitive names.
                    # But, false positives are harmless (and infrequent)
                    if not old_obj or str(new_obj) != str(old_obj):
                        # Note: An add will automatically replace any existing
                        # schema with the same OID. So, we only add.
                        value = add_x_origin(new_obj)
                        new_elements.append(value)

                        if old_obj:
                            old_attr = old_entries_by_oid.get(oid)
                            log.debug('Replace: %s', old_attr)
                            log.debug('   with: %s', value)
                        else:
                            log.debug('Add: %s', value)

                modified = modified or new_elements
                schema_entry[attrname].extend(new_elements)
                # we need to iterate schema updates, due to dependencies (SUP)
                # schema_entry doesn't respect order of objectclasses/attributes
                # so updates must be executed with groups of independent OIDs
                if new_elements:
                    modlist = schema_entry.generate_modlist()
                    log.debug("Schema modlist:\n%s", pprint.pformat(modlist))
                    conn.update_entry(schema_entry)

    if not modified:
        log.debug('Not updating schema')

    return modified
Exemple #2
0
def update_schema(
    schema_files,
    ldapi=False,
    dm_password=None,
):
    """Update schema to match the given ldif files

    Schema elements present in the LDIF files but missing from the DS schema
    are added.
    Schema elements that differ between LDIF files and DS schema are updated
    to match the LDIF files. The comparison ignores tags that python-ldap's
    schema parser does not understand (such as X-ORIGIN).
    Extra elements present only in the DS schema are left untouched.

    An X-ORIGIN tag containing the current IPA version is added to all new
    and updated schema elements.

    :param schema_files: List of filenames to update from
    :param ldapi: if true, use ldapi to connect
    :param dm_password: directory manager password

    :return:
        True if modifications were made
    """
    SCHEMA_ELEMENT_CLASSES_KEYS = [x[0] for x in SCHEMA_ELEMENT_CLASSES]

    conn = connect(ldapi=ldapi,
                   dm_password=dm_password,
                   realm=api.env.realm,
                   fqdn=installutils.get_fqdn())

    old_schema = conn.schema

    schema_entry = conn.get_entry(DN(('cn', 'schema')),
                                  SCHEMA_ELEMENT_CLASSES_KEYS)

    modified = False

    # The exact representation the DS gives us for each OID
    # (for debug logging)
    old_entries_by_oid = {
        cls(attr).oid: attr.decode('utf-8')
        for (attrname, cls) in SCHEMA_ELEMENT_CLASSES
        for attr in schema_entry[attrname]
    }

    for filename in schema_files:
        log.debug('Processing schema LDIF file %s', filename)
        url = "file://{}".format(filename)
        _dn, new_schema = ldap.schema.subentry.urlfetch(url)

        for attrname, cls in SCHEMA_ELEMENT_CLASSES:
            for oids_set in _get_oid_dependency_order(new_schema, cls):
                # Set of all elements of this class, as strings given by the DS
                new_elements = []
                for oid in oids_set:
                    new_obj = new_schema.get_obj(cls, oid)
                    old_obj = old_schema.get_obj(cls, oid)
                    # Compare python-ldap's sanitized string representations
                    # to see if the value is different
                    # This can give false positives, e.g. with case differences
                    # in case-insensitive names.
                    # But, false positives are harmless (and infrequent)
                    if not old_obj or str(new_obj) != str(old_obj):
                        # Note: An add will automatically replace any existing
                        # schema with the same OID. So, we only add.
                        value = add_x_origin(new_obj)

                        if old_obj:
                            old_attr = old_entries_by_oid.get(oid)
                            log.debug('Replace: %s', old_attr)
                            log.debug('   with: %s', value)
                        else:
                            log.debug('Add: %s', value)

                        new_elements.append(value.encode('utf-8'))

                modified = modified or new_elements
                schema_entry[attrname].extend(new_elements)
                # we need to iterate schema updates, due to dependencies (SUP)
                # schema_entry doesn't respect order of objectclasses/attributes
                # so updates must be executed with groups of independent OIDs
                if new_elements:
                    modlist = schema_entry.generate_modlist()
                    log.debug("Schema modlist:\n%s", pprint.pformat(modlist))
                    conn.update_entry(schema_entry)

    if not modified:
        log.debug('Not updating schema')

    return modified
Exemple #3
0
def update_schema(schema_files, ldapi=False, dm_password=None, live_run=True):
    """Update schema to match the given ldif files

    Schema elements present in the LDIF files but missing from the DS schema
    are added.
    Schema elements that differ between LDIF files and DS schema are updated
    to match the LDIF files. The comparison ignores tags that python-ldap's
    schema parser does not understand (such as X-ORIGIN).
    Extra elements present only in the DS schema are left untouched.

    An X-ORIGIN tag containing the current IPA version is added to all new
    and updated schema elements.

    :param schema_files: List of filenames to update from
    :param ldapi: if true, use ldapi to connect
    :param dm_password: directory manager password
    :live_run: if false, changes will not be applied

    :return:
        True if modifications were made
        (or *would be* made, for live_run=false)
    """
    conn = connect(ldapi=ldapi, dm_password=dm_password,
                   realm=krbV.default_context().default_realm,
                   fqdn=installutils.get_fqdn())

    old_schema = conn.schema

    schema_entry = conn.get_entry(DN(('cn', 'schema')),
                                  SCHEMA_ELEMENT_CLASSES.keys())

    modified = False

    # The exact representation the DS gives us for each OID
    # (for debug logging)
    old_entries_by_oid = {cls(str(attr)).oid: str(attr)
                          for attrname, cls in SCHEMA_ELEMENT_CLASSES.items()
                          for attr in schema_entry[attrname]}

    for filename in schema_files:
        log.info('Processing schema LDIF file %s', filename)
        dn, new_schema = ldap.schema.subentry.urlfetch(filename)

        for attrname, cls in SCHEMA_ELEMENT_CLASSES.items():

            # Set of all elements of this class, as strings given by the DS
            new_elements = []

            for oid in new_schema.listall(cls):
                new_obj = new_schema.get_obj(cls, oid)
                old_obj = old_schema.get_obj(cls, oid)
                # Compare python-ldap's sanitized string representations
                # to see if the value is different
                # This can give false positives, e.g. with case differences
                # in case-insensitive names.
                # But, false positives are harmless (and infrequent)
                if not old_obj or str(new_obj) != str(old_obj):
                    # Note: An add will automatically replace any existing
                    # schema with the same OID. So, we only add.
                    value = add_x_origin(new_obj)
                    new_elements.append(value)

                    if old_obj:
                        old_attr = old_entries_by_oid.get(oid)
                        log.info('Replace: %s', old_attr)
                        log.info('   with: %s', value)
                    else:
                        log.info('Add: %s', value)

            modified = modified or new_elements
            schema_entry[attrname].extend(new_elements)

    # FIXME: We should have a better way to display the modlist,
    # for now display raw output of our internal routine
    modlist = conn._generate_modlist(schema_entry.dn, schema_entry)
    log.debug("Complete schema modlist:\n%s", pprint.pformat(modlist))

    if modified and live_run:
        conn.update_entry(schema_entry)
    else:
        log.info('Not updating schema')

    return modified