Converting Sun DS schema to OpenLDAP

As I mentioned in another post, I recently recommenced experimentation with OpenLDAP. One of the “needfuls” that had to be attended to was converting custom schemae from other directories (in my case, Sun Directory 5) to OpenLDAP format. I’ve written scripts to do this in the past for both Sun DS to OpenLDAP and Sun DS to Oracle Internet Directory (OID) conversions, so it wasn’t that big a challenge. The main thing was that I wanted to avoid bringing over all those proprietary Netscape (and other vendor) object classes and attributes, as well as prevent the duplication of the standard objects already in the OpenLDAP schema (accidentally duplicating an objectclass or attribute that already exists in the target directory schema can result in a world of hurt).

Here’s a little script I wrote many years ago to do this.

In this code I’ve substituted ‘example’ to filter on the naming convention used for my custom objects (this constitutes the prefix for each custom attribute, like “exampleregion” or “examplelocation”). I’ve also added ‘companyname’ to the regex to capture a custom attribute that doesn’t follow my convention. It works pretty well, but, as always, YMMVE (Your Mileage May Vary Exceptionally).

#!/usr/bin/perl -w
# ns2oldap.pl Convert Netscape (iPlanet/Sun) to OpenLDAP Schema
# Created 12/29/05 by P Lembo
# (c) 2005 by Philip M Lembo. All rights reserved.
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.

use strict;
use Net::LDAP;
use Net::LDAP::Entry;
use Net::LDAP::Schema;

my $dirHost = "ldap.example.com";

my $outfile = "custom.schema";

open FH, ">$outfile" or die $!;

print FH "# Schema converted from Netscape/iPlanet/Sun Directorynn";

my $ldap = Net::LDAP->new($dirHost);
my $mesg = $ldap->bind();
my $schema = $ldap->schema();

my @attrs = $schema->all_attributes();
my @ocs = $schema->all_objectclasses();

foreach my $attr(@attrs) {
    if ($attr->{name} =~ /example|companyname/gi) {
        print FH "attributetype ( ", $attr->{oid}, " NAME '", $attr->{name}, "'n";

        if ($attr->{desc}) {
            print FH "tDESC '", $attr->{desc}, "'n";
        }
        if ($attr->{sup}) {
            print FH "tSUP ", $attr->{sup}, "n";
        }
        if ($attr->{equality}) {
            print FH "tEQUALITY ", $attr->{equality}, "n";
        }
        if ($attr->{substr}) {
            print FH "tSUBSTR ", $attr->{substr}, "n";
        }
        if ($attr->{syntax}) {
            print FH "tSYNTAX ", $attr->{syntax}, "n";
        }
        if ($attr->{'single-value'}) {
            print FH "tSINGLE-VALUEn";
        }
        print FH " )n";

    }
}

foreach my $oc(@ocs) {
    if ($oc->{name} =~ /example/gi) {
        print FH "objectclass ( ", $oc->{oid}, " NAME '", $oc->{name}, "'n";
        if ($oc->{desc}) {
            print FH "tDESC '", $oc->{desc}, "'n";
        }
        if ($oc->{sup}) {
            print FH "tSUP ", @{$oc->{sup}}, "n";
        }
        if ($oc->{auxilliary}) {
            print FH "tAUXILLIARYn";
        }
        if ($oc->{structural}) {
            print FH "tSTRUCTURALn";
        }
        if ($oc->{must}) {
            print FH join ( " $ ", "tMUST (",
                        ref ($oc->{must} ) ? @{$oc->{must}} : $oc->{must} ),
                        " )n";
        }

        if ($oc->{may}) {
            print FH join ( " $ ", "tMAY (",
                        ref ($oc->{may} ) ? @{$oc->{may}} : $oc->{may} ),
                        " )n";
        }|companyname

        print FH " )n";
    }
}

close FH;
$ldap->unbind;

@ARGV = glob "$outfile" or die $!;
$^I = "~";
while() {

    s/MUSTs(s$/MUST (/;
    s/MAYs(s$/MAY (/;
    print;

}

__END__;

P.S. Keep in mind that if the custom schema objects you’re looking to convert don’t follow a regular pattern, then you may have to rework this code somewhat. How much will depend on the number of objects involved. If it’s a dozen or so you could just add the names to the regexes. For example “$attr-&gt{name} =~ /example|companyname|oddnamedattribute|realloddnamedattribute|anotherattribute/gi)”. If you’re looking at more than that, you might want to consider wrapping the queries in a loop that iterates through the entire list. Avoiding this kind of problem is one of the reasons that I’ve emphasized the importance of consistency in naming custom objectclasses and attributes, establishing an OID registry, etc.

One other point: the script as written assumes anonymous access to “cn=schema” on the source directory side, on a default install things might not be so open. If anonymous doesn’t have access you can either add an aci to grant it, or modify the script so it binds as a privileged user.