The OpenDS schema is slightly different from the OpenLDAP one, but it’s quite simple to convert schema files from one format to another.
OpenDS, like Sun Directory Server Enterprise Edition and Fedora DS, uses a strict RFC 4512 and LDIF format.
In OpenLDAP, the actual text of the schema definition is similar and described using the RFC 4512 notation but uses the printer friendly notation, similar to the textual description in RFC documents.
So when converting schema files from OpenLDAP, for use in OpenDS, there are mainly 4 differences to take care of:
- In OpenLDAP, an attribute definition begins with “attributetype” while in OpenDS it begins with “attributetypes: “
- Similarly, in OpenLDAP, an object class definition has an “objectclass” prefix while it is “objectclasses: “
- OpenDS follows the LDIF conventions that the continuation line begins with a single space character, and that an empty line is an entry separator
- Finally, OpenDS schema files have a .ldif extension and only this extension is considered when loading schema from the config/schema directory.
The following python script can be used to convert an OpenLDAP schema file to a format usable by OpenDS (as well as Sun Directory Enterprise Edition). The script also recursively expands the OID macro format used in OpenLDAP schema files.
For now, syntax definitions are currently ignored as they cannot be loaded in OpenDS as they require associated code.
Usage is quite simple: schema-convert.py -o result.ldif openldap-schema-file
Enjoy and don’t hesitate to send feedback, suggestions for improvements.
Update on March 15: I’ve added support for name prefixed OIDs substitution as suggested by Martin Gwerder.
Update on April 9: OpenDS schema files uses the .ldif extension, and only files with this extension are loaded by the server from the config/schema directory.
Update on July 31: Now checking and removing quotes around Sup or Syntaxes values.
#!/usr/bin/env python # encoding: utf-8 """ schema-convert.py # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (the "License"). You may not use this file except in compliance # with the License. # # You can obtain a copy of the license at # trunk/opends/resource/legal-notices/OpenDS.LICENSE # or https://OpenDS.dev.java.net/OpenDS.LICENSE. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at # trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, # add the following below this CDDL HEADER, with the fields enclosed # by brackets "[]" replaced with your own identifying information: # Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2009 Sun Microsystems, Inc. Created by Ludovic Poitou on 2009-01-28. This program converts an OpenLDAP schema file to the OpenDS schema file format. """ import sys import getopt import re import string help_message = ''' Usage: schema-convert.py [options] <openldap-schema-file> options: \t -o output : specifies the output file, otherwise stdout is used \t -v : verbose mode ''' class Usage(Exception): def __init__(self, msg): self.msg = msg def main(argv=None): output = "" seclineoid = 0 IDs = {} if argv is None: argv = sys.argv try: try: opts, args = getopt.getopt(argv[1:], "ho:v", ["help", "output="]) except getopt.error, msg: raise Usage(msg) # option processing for option, value in opts: if option == "-v": verbose = True if option in ("-h", "--help"): raise Usage(help_message) if option in ("-o", "--output"): output = value except Usage, err: print >> sys.stderr, sys.argv[0].split("/")[-1] + ": " + str(err.msg) print >> sys.stderr, "\t for help use --help" return 2 try: infile = open(args[0], "r") except Usage, err: print >> sys.stderr, "Can't open file: " + str(err.msg) if output != "": try: outfile = open(output, "w") except Usage, err: print >> sys.stderr, "Can't open output file: " + str(err.msg) else: outfile = sys.stdout outfile.write("dn: cn=schema\n") outfile.write("objectclass: top\n") outfile.write("") for i in infile: newline = "" if not i.strip(): continue #if i.startswith("#"): # continue if re.match("objectidentifier", i, re.IGNORECASE): # Need to fill in an array of identifiers oid = i.split() if not re.match ("[0-9.]+", oid[2]): suboid = oid[2].split(':') IDs[oid[1]] = IDs[suboid[0]] + "." + suboid[1] else: IDs[oid[1]] = oid[2] continue if seclineoid == 1: subattr = i.split() if not re.match("[0-9.]+", subattr[0]): if re.match (".*:", subattr[0]): # The OID is an name prefixed OID. Replace string with the OID suboid = subattr[0].split(":") repl = IDs[suboid[0]] + "." + suboid[1] else: # The OID is a name. Replace string with the OID repl = IDs[subattr[0]] newline = string.replace(i, subattr[0], repl, 1) seclineoid = 0 if re.match("attributetype ", i, re.IGNORECASE): newline = re.sub("attribute[tT]ype", "attributeTypes:", i) # replace OID string with real OID if necessary subattr = newline.split() if len(subattr) < 3: seclineoid = 1 else: if not re.match("[0-9.]+", subattr[2]): if re.match (".*:", subattr[2]): # The OID is an name prefixed OID. Replace string with the OID suboid = subattr[2].split(":") repl = IDs[suboid[0]] + "." + suboid[1] else: # The OID is a name. Replace string with the OID repl = IDs[subattr[2]] newline = string.replace(newline, subattr[2], repl, 1) if re.match("objectclass ", i, re.IGNORECASE): newline = re.sub("object[cC]lass", "objectClasses:", i) # replace OID String with real OID subattr = newline.split() if len(subattr) < 3: seclineoid = 1 else: if not re.match("[0-9.]+", subattr[2]): if re.match (".*:", subattr[2]): # The OID is an name prefixed OID. Replace string with the OID suboid = subattr[2].split(":") repl = IDs[suboid[0]] + "." + suboid[1] else: # The OID is a name. Replace string with the OID repl = IDs[subattr[2]] newline = string.replace(newline, subattr[2], repl, 1) # Remove quoted syntax. if re.search("SYNTAX\s'[\d.]+'", newline): # Found a quoted syntax in an already updated line newline = re.sub("SYNTAX '([\d.]+)'", "SYNTAX \g<1>", newline) else: if re.search("SYNTAX\s'[\d.]+'", i): # Found a quoted syntax in the original line newline = re.sub("SYNTAX '([\d.]+)'", "SYNTAX \g<1>", i) # Remove quoted SUP if re.search("SUP\s'[\w\-]+'", newline): # Found a quoted sup in an already updated line newline = re.sub("SUP '([\w\-]+)'", "SUP \g<1>", newline) else: if re.search("SUP\s'[\w\-]+'", i): # Found a quoted sup in the original line newline = re.sub("SUP '([\w\-]+)'", "SUP \g<1>", i) # transform continuation lines with only 2 spaces if re.match(" +|\t", i): if newline != "": newline = " " + newline.strip() + "\n" else: newline = " " + i.strip() + "\n" if newline != "": outfile.write(newline) else: outfile.write(i) outfile.close() if __name__ == "__main__": sys.exit(main())
Technorati Tags: directory-server, java, ldap, opends, opensource, puppy, python, tip
Thanks a lot for that useful script. It saved me a lot of problems. As an improvemnt I would suggest the following improvement to make it handle name prefixed OIDs
<code>
if re.match("attributetype ", i, re.IGNORECASE):
__newline = re.sub("attribute[tT]ype", "attributeTypes:", i)
__# replace OID string with real OID if necessary
__subattr = newline.split()
__if not re.match("[0-9.]+", subattr[2]):
____if re.match (".*:", subattr[2]):
______# The OID is not an name prefixed OID. Replace string with the OID
______suboid = subattr[2].split(":")
______repl = IDs[suboid[0]] + "." + suboid[1]
____else:
______# The OID is a name. Replace string with the OID
______repl = IDs[subattr[0]]
____newline = string.replace(newline, subattr[2], repl, 1)
if re.match("objectclass ", i, re.IGNORECASE):
__newline = re.sub("object[cC]lass", "objectClasses:", i)
__# replace OID String with real OID
__subattr = newline.split()
__if not re.match("[0-9.]+", subattr[2]):
____if re.match (".*:", subattr[2]):
______# The OID is a name prefixed OID. Replace string with the OID
______suboid = subattr[2].split(":")
______repl = IDs[suboid[0]] + "." + suboid[1]
____else:
______# The OID is not an OID. Replace string with the OID
______repl = IDs[subattr[0]]
____newline = string.replace(newline, subattr[2], repl, 1)
</code>
The "__" are replacements for tabs as HTML was not allowed in my post
Hi Martin,
Thanks for your suggestion. I didn’t have a sample file with this notation but it makes sense to support that.
I’ve inserted the proposed changes to handle name prefixed OIDs.
I believe there is an error in your code above, the repl = IDs[subattr[0]] lines should be repl = IDs[subattr[2]] (as in the original code).
Best regards,
Ludovic.
Ludovic
You are right about the error. My coding skills (and especially debugging) in python are unfortunately more than rusty.
I will send you a sample file to your mailbox.
Regards
Martin