Пример #1
0
    def test_update_complex(self):
        # compare two entries created with different methods
        nsuffix, replid, replicatype = "dc=example,dc=com", 5, dsadmin.REPLICA_RDWR_TYPE
        binddnlist, legacy = ['uid=pippo, cn=config'], 'off'
        dn = "dc=example,dc=com"
        entry = Entry(dn)
        entry.setValues(
            'objectclass', "top", "nsds5replica", "extensibleobject")
        entry.setValues('cn', "replica")
        entry.setValues('nsds5replicaroot', nsuffix)
        entry.setValues('nsds5replicaid', str(replid))
        entry.setValues('nsds5replicatype', str(replicatype))
        entry.setValues('nsds5flags', "1")
        entry.setValues('nsds5replicabinddn', binddnlist)
        entry.setValues('nsds5replicalegacyconsumer', legacy)

        uentry = Entry((
            dn, {
            'objectclass': ["top", "nsds5replica", "extensibleobject"],
            'cn': ["replica"],
            })
        )
        log.debug("Entry created with dict:", uentry)
        # Entry.update *replaces*, so be careful with multi-valued attrs
        uentry.update({
            'nsds5replicaroot': nsuffix,
            'nsds5replicaid': str(replid),
            'nsds5replicatype': str(replicatype),
            'nsds5flags': '1',
            'nsds5replicabinddn': binddnlist,
            'nsds5replicalegacyconsumer': legacy
        })
        uentry_s, entry_s = map(str, (uentry, entry))
        assert uentry_s == entry_s, "Mismatching entries [%r] vs [%r]" % (
            uentry, entry)
Пример #2
0
    def test_update_complex(self):
        # compare two entries created with different methods
        nsuffix, replid, replicatype = "dc=example,dc=com", 5, dsadmin.REPLICA_RDWR_TYPE
        binddnlist, legacy = ['uid=pippo, cn=config'], 'off'
        dn = "dc=example,dc=com"
        entry = Entry(dn)
        entry.setValues(
            'objectclass', "top", "nsds5replica", "extensibleobject")
        entry.setValues('cn', "replica")
        entry.setValues('nsds5replicaroot', nsuffix)
        entry.setValues('nsds5replicaid', str(replid))
        entry.setValues('nsds5replicatype', str(replicatype))
        entry.setValues('nsds5flags', "1")
        entry.setValues('nsds5replicabinddn', binddnlist)
        entry.setValues('nsds5replicalegacyconsumer', legacy)

        uentry = Entry((
            dn, {
            'objectclass': ["top", "nsds5replica", "extensibleobject"],
            'cn': ["replica"],
            })
        )
        print uentry
        # Entry.update *replaces*, so be careful with multi-valued attrs
        uentry.update({
            'nsds5replicaroot': nsuffix,
            'nsds5replicaid': str(replid),
            'nsds5replicatype': str(replicatype),
            'nsds5flags': '1',
            'nsds5replicabinddn': binddnlist,
            'nsds5replicalegacyconsumer': legacy
        })
        uentry_s, entry_s = map(str, (uentry, entry))
        assert uentry_s == entry_s, "Mismatching entries [%r] vs [%r]" % (
            uentry, entry)
Пример #3
0
    def setup_mt(self, suffix, bename, parent=None):
        """Setup a suffix with the given backend-name.

            @param suffix
            @param bename
            @param parent   - the parent suffix 
            @param verbose  - None 

            This method does not create the matching entry in the tree,
            nor the given backend. Both should be created apart.
            
            Ex. setup_mt(suffix='o=addressbook1', bename='addressbook1')
                creates:
                    - the mapping in "cn=mapping tree,cn=config"
                you have to create:
                    - the backend 
                    - the ldap entry "o=addressbook1" *after*
        """
        nsuffix = normalizeDN(suffix)
        #escapedn = escapeDNValue(nsuffix)
        if parent:
            nparent = normalizeDN(parent)
        else:
            nparent = ""
            
        filt = suffixfilt(suffix)
        # if suffix exists, return
        try:
            entry = self.conn.getEntry(
                DN_MAPPING_TREE, ldap.SCOPE_SUBTREE, filt)
            return entry
        except NoSuchEntryError:
            entry = None

        # fix me when we can actually used escaped DNs
        #dn = "cn=%s,cn=mapping tree,cn=config" % escapedn
        dn = ','.join(('cn="%s"' % nsuffix, DN_MAPPING_TREE))
        entry = Entry(dn)
        entry.update({
            'objectclass': ['top', 'extensibleObject', 'nsMappingTree'],
            'nsslapd-state': 'backend',
            # the value in the dn has to be DN escaped
            # internal code will add the quoted value - unquoted value is useful for searching
            'cn': nsuffix,
            'nsslapd-backend': bename
        })
        #entry.setValues('cn', [escapedn, nsuffix]) # the value in the dn has to be DN escaped
        # the other value can be the unescaped value
        if parent:
            entry.setValues('nsslapd-parent-suffix', nparent)
        try:
            self.log.debug("Creating entry: %r" % entry)
            self.conn.add_s(entry)
        except ldap.LDAPError, e:
            raise ldap.LDAPError("Error adding suffix entry " + dn, e)
Пример #4
0
    def setup_mt(self, suffix, bename, parent=None):
        """Setup a suffix with the given backend-name.

            @param suffix
            @param bename
            @param parent   - the parent suffix 
            @param verbose  - None 

            This method does not create the matching entry in the tree,
            nor the given backend. Both should be created apart.
            
            Ex. setup_mt(suffix='o=addressbook1', bename='addressbook1')
                creates:
                    - the mapping in "cn=mapping tree,cn=config"
                you have to create:
                    - the backend 
                    - the ldap entry "o=addressbook1" *after*
        """
        nsuffix = normalizeDN(suffix)
        #escapedn = escapeDNValue(nsuffix)
        if parent:
            nparent = normalizeDN(parent)
        else:
            nparent = ""
            
        filt = suffixfilt(suffix)
        # if suffix exists, return
        try:
            entry = self.conn.getEntry(
                DN_MAPPING_TREE, ldap.SCOPE_SUBTREE, filt)
            return entry
        except NoSuchEntryError:
            entry = None

        # fix me when we can actually used escaped DNs
        #dn = "cn=%s,cn=mapping tree,cn=config" % escapedn
        dn = ','.join(('cn="%s"' % nsuffix, DN_MAPPING_TREE))
        entry = Entry(dn)
        entry.update({
            'objectclass': ['top', 'extensibleObject', 'nsMappingTree'],
            'nsslapd-state': 'backend',
            # the value in the dn has to be DN escaped
            # internal code will add the quoted value - unquoted value is useful for searching
            'cn': nsuffix,
            'nsslapd-backend': bename
        })
        #entry.setValues('cn', [escapedn, nsuffix]) # the value in the dn has to be DN escaped
        # the other value can be the unescaped value
        if parent:
            entry.setValues('nsslapd-parent-suffix', nparent)
        try:
            self.log.debug("Creating entry: %r" % entry)
            self.conn.add_s(entry)
        except ldap.LDAPError, e:
            raise ldap.LDAPError("Error adding suffix entry " + dn, e)
Пример #5
0
    def test_update(self):
        expected = 'pluto minnie'
        given = {'cn': expected}
        t = ('o=pippo', {
             'o': ['pippo'],
             'objectclass': ['organization', 'top']
             })

        e = Entry(t)
        e.update(given)
        assert e.cn == expected, "Bad cn: %s, expected: %s" % (e.cn, expected)
Пример #6
0
    def test_update(self):
        expected = 'pluto minnie'
        given = {'cn': expected}
        t = ('o=pippo', {
             'o': ['pippo'],
             'objectclass': ['organization', 'top']
             })

        e = Entry(t)
        e.update(given)
        assert e.cn == expected, "Bad cn: %s, expected: %s" % (e.cn, expected)
Пример #7
0
    def enable_ssl(self, secport=636, secargs=None):
        """Configure SSL support into cn=encryption,cn=config.

            secargs is a dict like {
                'nsSSLPersonalitySSL': 'Server-Cert'
            }
        """
        self.log.debug("configuring SSL with secargs:%r" % secargs)
        secargs = secargs or {}

        dn_enc = 'cn=encryption,cn=config'
        ciphers = '-rsa_null_md5,+rsa_rc4_128_md5,+rsa_rc4_40_md5,+rsa_rc2_40_md5,+rsa_des_sha,' + \
            '+rsa_fips_des_sha,+rsa_3des_sha,+rsa_fips_3des_sha,' + \
            '+tls_rsa_export1024_with_rc4_56_sha,+tls_rsa_export1024_with_des_cbc_sha'
        mod = [(ldap.MOD_REPLACE, 'nsSSL3', secargs.get('nsSSL3', 'on')),
               (ldap.MOD_REPLACE, 'nsSSLClientAuth',
                secargs.get('nsSSLClientAuth', 'allowed')),
               (ldap.MOD_REPLACE, 'nsSSL3Ciphers', secargs.get('nsSSL3Ciphers', ciphers))]
        self.conn.modify_s(dn_enc, mod)

        dn_rsa = 'cn=RSA,cn=encryption,cn=config'
        e_rsa = Entry(dn_rsa)
        e_rsa.update({
            'objectclass': ['top', 'nsEncryptionModule'],
            'nsSSLPersonalitySSL': secargs.get('nsSSLPersonalitySSL', 'Server-Cert'),
            'nsSSLToken': secargs.get('nsSSLToken', 'internal (software)'),
            'nsSSLActivation': secargs.get('nsSSLActivation', 'on')
        })
        try:
            self.conn.add_s(e_rsa)
        except ldap.ALREADY_EXISTS:
            pass

        mod = [
            (ldap.MOD_REPLACE,
                'nsslapd-security', secargs.get('nsslapd-security', 'on')),
            (ldap.MOD_REPLACE,
                'nsslapd-ssl-check-hostname', secargs.get('nsslapd-ssl-check-hostname', 'off')),
            (ldap.MOD_REPLACE,
                'nsslapd-secureport', str(secport))
        ]
        self.log.debug("trying to modify %r with %r" % (DN_CONFIG, mod))
        self.conn.modify_s(DN_CONFIG, mod)

        fields = 'nsslapd-security nsslapd-ssl-check-hostname'.split()
        return self.conn.getEntry(DN_CONFIG, attrlist=fields)
Пример #8
0
    def enable_ssl(self, secport=636, secargs=None):
        """Configure SSL support into cn=encryption,cn=config.

            secargs is a dict like {
                'nsSSLPersonalitySSL': 'Server-Cert'
            }
        """
        self.log.debug("configuring SSL with secargs:%r" % secargs)
        secargs = secargs or {}

        dn_enc = 'cn=encryption,cn=config'
        ciphers = '-rsa_null_md5,+rsa_rc4_128_md5,+rsa_rc4_40_md5,+rsa_rc2_40_md5,+rsa_des_sha,' + \
            '+rsa_fips_des_sha,+rsa_3des_sha,+rsa_fips_3des_sha,' + \
            '+tls_rsa_export1024_with_rc4_56_sha,+tls_rsa_export1024_with_des_cbc_sha'
        mod = [(ldap.MOD_REPLACE, 'nsSSL3', secargs.get('nsSSL3', 'on')),
               (ldap.MOD_REPLACE, 'nsSSLClientAuth',
                secargs.get('nsSSLClientAuth', 'allowed')),
               (ldap.MOD_REPLACE, 'nsSSL3Ciphers', secargs.get('nsSSL3Ciphers', ciphers))]
        self.conn.modify_s(dn_enc, mod)

        dn_rsa = 'cn=RSA,cn=encryption,cn=config'
        e_rsa = Entry(dn_rsa)
        e_rsa.update({
            'objectclass': ['top', 'nsEncryptionModule'],
            'nsSSLPersonalitySSL': secargs.get('nsSSLPersonalitySSL', 'Server-Cert'),
            'nsSSLToken': secargs.get('nsSSLToken', 'internal (software)'),
            'nsSSLActivation': secargs.get('nsSSLActivation', 'on')
        })
        try:
            self.conn.add_s(e_rsa)
        except ldap.ALREADY_EXISTS:
            pass

        mod = [
            (ldap.MOD_REPLACE,
                'nsslapd-security', secargs.get('nsslapd-security', 'on')),
            (ldap.MOD_REPLACE,
                'nsslapd-ssl-check-hostname', secargs.get('nsslapd-ssl-check-hostname', 'off')),
            (ldap.MOD_REPLACE,
                'nsslapd-secureport', str(secport))
        ]
        self.log.debug("trying to modify %r with %r" % (DN_CONFIG, mod))
        self.conn.modify_s(DN_CONFIG, mod)

        fields = 'nsslapd-security nsslapd-ssl-check-hostname'.split()
        return self.conn.getEntry(DN_CONFIG, attrlist=fields)
Пример #9
0
    def changelog(self, dbname='changelogdb'):
        """Add and return the replication changelog entry.

            If dbname starts with "/" then it's considered a full path,
            otherwise it's relative to self.dbdir
        """
        dn = DN_CHANGELOG
        dirpath = os.path.join(self.conn.dbdir, dbname)
        entry = Entry(dn)
        entry.update({
            'objectclass': ("top", "extensibleobject"),
            'cn': "changelog5",
            'nsslapd-changelogdir': dirpath
        })
        self.log.debug("adding changelog entry: %r" % entry)
        try:
            self.conn.add_s(entry)
        except ldap.ALREADY_EXISTS:
            self.log.warn("entry %s already exists" % dn)

        return self.conn._test_entry(dn, ldap.SCOPE_BASE)
Пример #10
0
    def changelog(self, dbname='changelogdb'):
        """Add and return the replication changelog entry.

            If dbname starts with "/" then it's considered a full path,
            otherwise it's relative to self.dbdir
        """
        dn = DN_CHANGELOG
        dirpath = os.path.join(self.conn.dbdir, dbname)
        entry = Entry(dn)
        entry.update({
            'objectclass': ("top", "extensibleobject"),
            'cn': "changelog5",
            'nsslapd-changelogdir': dirpath
        })
        self.log.debug("adding changelog entry: %r" % entry)
        try:
            self.conn.add_s(entry)
        except ldap.ALREADY_EXISTS:
            self.log.warn("entry %s already exists" % dn)

        return self.conn._test_entry(dn, ldap.SCOPE_BASE)
Пример #11
0
def setup():
    # uses an existing 389 instance
    # add a suffix
    # add an agreement
    # This setup is quite verbose but to test dsadmin method we should
    # do things manually. A better solution would be to use an LDIF.
    global conn
    conn = DSAdmin(**config.auth)
    conn.verbose = True
    conn.added_entries = []
    conn.added_backends = set(['o=mockbe1'])
    conn.added_replicas = []

    # add a backend for testing ruv and agreements
    addbackend_harn(conn, 'testReplica')

    # add another backend for testing replica.add()
    addbackend_harn(conn, 'testReplicaCreation')

    # replication needs changelog
    conn.replica.changelog()
    # add rmanager entry
    try:
        conn.add_s(Entry((DN_RMANAGER, {
            'objectclass': "top person inetOrgPerson".split(),
            'sn': ["bind dn pseudo user"],
            'cn': 'replication manager',
            'uid': 'rmanager'
        }))
        )
        conn.added_entries.append(DN_RMANAGER)
    except ldap.ALREADY_EXISTS:
        pass

    # add a master replica entry
    # to test ruv and agreements
    replica_dn = ','.join(
        ['cn=replica', 'cn="o=testReplica"', DN_MAPPING_TREE])
    replica_e = Entry(replica_dn)
    replica_e.update({
                     'objectclass': ["top", "nsds5replica", "extensibleobject"],
                     'cn': "replica",
                     'nsds5replicaroot': 'o=testReplica',
                     'nsds5replicaid': MOCK_REPLICA_ID,
                     'nsds5replicatype': '3',
                     'nsds5flags': '1',
                     'nsds5replicabinddn': DN_RMANAGER
                     })
    try:
        conn.add_s(replica_e)
    except ldap.ALREADY_EXISTS:
        pass
    conn.added_entries.append(replica_dn)

    agreement_dn = ','.join(('cn=testAgreement', replica_dn))
    agreement_e = Entry(agreement_dn)
    agreement_e.update({
                       'objectclass': ["top", "nsds5replicationagreement"],
                       'cn': 'testAgreement',
                       'nsds5replicahost': 'localhost',
                       'nsds5replicaport': '22389',
                       'nsds5replicatimeout': '120',
                       'nsds5replicabinddn': DN_RMANAGER,
                       'nsds5replicacredentials': 'password',
                       'nsds5replicabindmethod': 'simple',
                       'nsds5replicaroot': 'o=testReplica',
                       'nsds5replicaupdateschedule': '0000-2359 0123456',
                       'description': 'testAgreement'
                       })
    try:
        conn.add_s(agreement_e)
    except ldap.ALREADY_EXISTS:
        pass
    conn.added_entries.append(agreement_dn)
    conn.agreement_dn = agreement_dn
Пример #12
0
    def add(self, suffix, binddn=None, bindpw=None, urls=None, attrvals=None, benamebase='localdb', setupmt=False, parent=None):
        """Setup a backend and return its dn. Blank on error XXX should RAISE!
            @param suffix
            @param benamebase   -   the backend common name
            @param setupmt  - eventually setup Mapping Tree entry
            @param urls - a string of ldapurl - create a chaining backend
            @oaram binddn   -   chaining proxy user
            @param bindpw   -   chaining proxy password
            @param attrvals: a dict with further params like
                for ldbm    {
                                'nsslapd-cachememsize': '1073741824',
                                'nsslapd-cachesize': '-1',
                            }
                for chain   {
                                'nsmaxresponsedelay': '60',
                                'nsslapd-sizelimit': '-1'
                            }
                            
            ex.
            benamebase="Isle0-0"
            urls=[ 
                "ldaps://f0-ldap-vip.example.it:636/",
                "ldaps://ldap-18.example.it:636/",
                "ldaps://ldap-19.example.it:636/"
                ]
            
            NOTE: The suffix attribute is a mere string for the backend.
                the following action will work nicely:
                c.backend.add(suffix="foo=example,dc=com",benamebase="db1")
                    
            TODO: rename benamebase with cn
            TODO: split CHAIN and LDBM ? eg. backend.add_chain
        """
        attrvals = attrvals or {}
        dnbase = ""

        # figure out what type of be based on args
        if binddn and bindpw and urls:  # its a chaining be
            dnbase = DN_CHAIN
        else:  # its a ldbm be
            dnbase = DN_LDBM

        nsuffix = normalizeDN(suffix)
        try:
            cn = benamebase 
            self.log.debug("create backend with cn: %s" % cn)
            dn = "cn=" + cn + "," + dnbase
            entry = Entry(dn)
            entry.update({
                'objectclass': ['top', 'extensibleObject', 'nsBackendInstance'],
                'cn': cn,
                'nsslapd-suffix': nsuffix
            })

            if binddn and bindpw and urls:  # its a chaining be
                entry.update({
                             'nsfarmserverurl': urls,
                             'nsmultiplexorbinddn': binddn,
                             'nsmultiplexorcredentials': bindpw
                             })

            # set attrvals (but not cn, because it's in dn)
            # TODO do it in Entry
            if attrvals:
                entry.update(attrvals)

            self.log.debug("adding entry: %r" % entry)
            self.conn.add_s(entry)
        except ldap.ALREADY_EXISTS, e:
            self.log.error("Entry already exists: %r" % dn)
            raise ldap.ALREADY_EXISTS("%s : %r" % (e, dn))
Пример #13
0
    def agreement_add(self, consumer, suffix=None, binddn=None, bindpw=None, cn_format=r'meTo_$host:$port', description_format=r'me to $host:$port', timeout=120, auto_init=False, bindmethod='simple', starttls=False, schedule=ALWAYS, args=None):
        """Create (and return) a replication agreement from self to consumer.
            - self is the supplier,

            @param consumer: one of the following (consumer can be a master)
                    * a DSAdmin object if chaining
                    * an object with attributes: host, port, sslport, __str__
            @param suffix    - eg. 'dc=babel,dc=it'
            @param binddn    - 
            @param bindpw    -
            @param cn_format - string.Template to format the agreement name
            @param timeout   - replica timeout in seconds
            @param auto_init - start replication immediately
            @param bindmethod-  'simple'
            @param starttls  - True or False
            @param schedule  - when to schedule the replication. default: ALWAYS 
            @param args      - further args dict. Allowed keys:
                    'fractional',
                    'stripattrs',
                    'winsync'
                    
            @raise NosuchEntryError    - if a replica doesn't exist for that suffix
            @raise ALREADY_EXISTS
            @raise UNWILLING_TO_PERFORM if the database was previously
                    in read-only state. To create new agreements you
                    need to *restart* the directory server
            
            NOTE: this method doesn't cache connection entries
            
            TODO: test winsync 
            TODO: test chain
            
        """
        import string
        assert binddn and bindpw and suffix
        args = args or {}

        othhost, othport, othsslport = (
            consumer.host, consumer.port, consumer.sslport)
        othport = othsslport or othport
        nsuffix = normalizeDN(suffix)

        # adding agreement to previously created replica
        replica_entries = self.list(suffix)
        if not replica_entries:
            raise NoSuchEntryError(
                "Error: no replica set up for suffix " + suffix)
        replica = replica_entries[0]

        # define agreement entry
        cn = string.Template(cn_format).substitute({'host': othhost, 'port': othport})
        dn_agreement = ','.join(["cn=%s" % cn, replica.dn])

        # This is probably unnecessary because
        # we can just raise ALREADY_EXISTS
        try:
            entry = self.conn.getEntry(dn_agreement, ldap.SCOPE_BASE)
            self.log.warn("Agreement exists: %r" % dn_agreement)
            raise ldap.ALREADY_EXISTS
        except ldap.NO_SUCH_OBJECT:
            entry = None

        # In a separate function in this scope?
        entry = Entry(dn_agreement)
        entry.update({
            'objectclass': ["top", "nsds5replicationagreement"],
            'cn': cn,
            'nsds5replicahost': consumer.host,
            'nsds5replicatimeout': str(timeout),
            'nsds5replicabinddn': binddn,
            'nsds5replicacredentials': bindpw,
            'nsds5replicabindmethod': bindmethod,
            'nsds5replicaroot': nsuffix,
            'description': string.Template(description_format).substitute({'host': othhost, 'port': othport})
        })
        if schedule:
            if not re.match(r'\d{4}-\d{4} [0-6]{1,7}', schedule): # TODO put the regexp in a separate variable
                raise ValueError("Bad schedule format %r" % schedule)
            entry.update({'nsds5replicaupdateschedule': schedule})
        if starttls:
            entry.setValues('nsds5replicatransportinfo', 'TLS')
            entry.setValues('nsds5replicaport', str(othport))
        elif othsslport:
            entry.setValues('nsds5replicatransportinfo', 'SSL')
            entry.setValues('nsds5replicaport', str(othsslport))
        else:
            entry.setValues('nsds5replicatransportinfo', 'LDAP')
            entry.setValues('nsds5replicaport', str(othport))
            
        if auto_init:
            entry.setValues('nsds5BeginReplicaRefresh', 'start')
            
        # further arguments
        if 'fractional' in args:
            entry.setValues('nsDS5ReplicatedAttributeList', args['fractional'])
        if 'stripattrs' in args:
            entry.setValues('nsds5ReplicaStripAttrs', args['stripattrs'])
        if 'winsync' in args:  # state it clearly!
            self.conn.setupWinSyncAgmt(args, entry)

        try:
            self.log.debug("Adding replica agreement: [%s]" % entry)
            self.conn.add_s(entry)
        except:
            #  FIXME check please!
            raise

        entry = self.conn.waitForEntry(dn_agreement)
        if entry:
            # More verbose but shows what's going on
            if 'chain' in args:
                chain_args = {
                    'suffix': suffix,
                    'binddn': binddn,
                    'bindpw': bindpw
                }
                # Work on `self` aka producer
                if replica.nsds5replicatype == MASTER_TYPE:
                    self.setupChainingFarm(**chain_args)
                # Work on `consumer`
                # TODO - is it really required?
                if replica.nsds5replicatype == LEAF_TYPE:
                    chain_args.update({
                        'isIntermediate': 0,
                        'urls': self.conn.toLDAPURL(),
                        'args': args['chainargs']
                    })
                    consumer.setupConsumerChainOnUpdate(**chain_args)
                elif replica.nsds5replicatype == HUB_TYPE:
                    chain_args.update({
                        'isIntermediate': 1,
                        'urls': self.conn.toLDAPURL(),
                        'args': args['chainargs']
                    })
                    consumer.setupConsumerChainOnUpdate(**chain_args)

        return dn_agreement
Пример #14
0
    def add(self, suffix, binddn, bindpw, rtype=MASTER_TYPE, rid=None, tombstone_purgedelay=None, purgedelay=None, referrals=None, legacy=False):
        """Setup a replica entry on an existing suffix.
            @param suffix - dn of suffix
            @param binddn - the replication bind dn for this replica
                            can also be a list ["cn=r1,cn=config","cn=r2,cn=config"]
            @param bindpw - used to eventually provision the replication entry

            @param rtype - master, hub, leaf (see above for values) - default is master
            @param rid - replica id or - if not given - an internal sequence number will be assigned

            # further args
            @param legacy - true or false - for legacy consumer
            @param tombstone_purgedelay
            @param purgedelay - changelog expiration time in seconds
            @param referrals

            Ex. replica.add(**{
                    'suffix': "dc=example,dc=com",
                    'type'  : dsadmin.MASTER_TYPE,
                    'binddn': "cn=replication manager,cn=config"
              })
             binddn
            TODO: this method does not update replica type
        """
        # set default values
        if rtype == MASTER_TYPE:
            rtype = REPLICA_RDWR_TYPE
        else:
            rtype = REPLICA_RDONLY_TYPE

        if legacy:
            legacy = 'on'
        else:
            legacy = 'off'

        # create replica entry in mapping-tree
        nsuffix = normalizeDN(suffix)
        mtent = self.conn.getMTEntry(suffix)
        dn_replica = ','.join(("cn=replica", mtent.dn))
        try:
            entry = self.conn.getEntry(dn_replica, ldap.SCOPE_BASE)
            self.log.warn("Already setup replica for suffix %r" % suffix)
            rec = self.conn.suffixes.setdefault(nsuffix, {})
            rec.update({'dn': dn_replica, 'type': rtype})
            return rec
        except ldap.NO_SUCH_OBJECT:
            entry = None

        # If a replica does not exist
        binddnlist = []
        if hasattr(binddn, '__iter__'):
            binddnlist = binddn
        else:
            binddnlist.append(binddn)

        entry = Entry(dn_replica)
        entry.update({
            'objectclass': ("top", "nsds5replica", "extensibleobject"),
            'cn': "replica",
            'nsds5replicaroot': nsuffix,
            'nsds5replicaid': str(rid),
            'nsds5replicatype': str(rtype),
            'nsds5replicalegacyconsumer': legacy,
            'nsds5replicabinddn': binddnlist
        })
        if rtype != LEAF_TYPE:
            entry.setValues('nsds5flags', "1")

        # other args
        if tombstone_purgedelay is not None:
            entry.setValues(
                'nsds5replicatombstonepurgeinterval', str(tombstone_purgedelay))
        if purgedelay is not None:
            entry.setValues('nsds5ReplicaPurgeDelay', str(purgedelay))
        if referrals:
            entry.setValues('nsds5ReplicaReferral', referrals)

        self.conn.add_s(entry)

        # check if the entry exists TODO better to raise!
        self.conn._test_entry(dn_replica, ldap.SCOPE_BASE)

        self.conn.suffixes[nsuffix] = {'dn': dn_replica, 'type': rtype}
        return {'dn': dn_replica, 'type': rtype}
Пример #15
0
def setup():
    # uses an existing 389 instance
    # add a suffix
    # add an agreement
    # This setup is quite verbose but to test dsadmin method we should
    # do things manually. A better solution would be to use an LDIF.
    global conn
    conn = DSAdmin(**config.auth)
    conn.verbose = True
    conn.added_entries = []
    conn.added_backends = set(['o=mockbe1'])
    conn.added_replicas = []

    # add a backend for testing ruv and agreements
    addbackend_harn(conn, 'testReplica')

    # add another backend for testing replica.add()
    addbackend_harn(conn, 'testReplicaCreation')

    # replication needs changelog
    conn.replica.changelog()
    # add rmanager entry
    try:
        conn.add_s(
            Entry((DN_RMANAGER, {
                'objectclass': "top person inetOrgPerson".split(),
                'sn': ["bind dn pseudo user"],
                'cn': 'replication manager',
                'uid': 'rmanager'
            })))
        conn.added_entries.append(DN_RMANAGER)
    except ldap.ALREADY_EXISTS:
        pass

    # add a master replica entry
    # to test ruv and agreements
    replica_dn = ','.join(
        ['cn=replica', 'cn="o=testReplica"', DN_MAPPING_TREE])
    replica_e = Entry(replica_dn)
    replica_e.update({
        'objectclass': ["top", "nsds5replica", "extensibleobject"],
        'cn': "replica",
        'nsds5replicaroot': 'o=testReplica',
        'nsds5replicaid': MOCK_REPLICA_ID,
        'nsds5replicatype': '3',
        'nsds5flags': '1',
        'nsds5replicabinddn': DN_RMANAGER
    })
    try:
        conn.add_s(replica_e)
    except ldap.ALREADY_EXISTS:
        pass
    conn.added_entries.append(replica_dn)

    agreement_dn = ','.join(('cn=testAgreement', replica_dn))
    agreement_e = Entry(agreement_dn)
    agreement_e.update({
        'objectclass': ["top", "nsds5replicationagreement"],
        'cn': 'testAgreement',
        'nsds5replicahost': 'localhost',
        'nsds5replicaport': '22389',
        'nsds5replicatimeout': '120',
        'nsds5replicabinddn': DN_RMANAGER,
        'nsds5replicacredentials': 'password',
        'nsds5replicabindmethod': 'simple',
        'nsds5replicaroot': 'o=testReplica',
        'nsds5replicaupdateschedule': '0000-2359 0123456',
        'description': 'testAgreement'
    })
    try:
        conn.add_s(agreement_e)
    except ldap.ALREADY_EXISTS:
        pass
    conn.added_entries.append(agreement_dn)
    conn.agreement_dn = agreement_dn
Пример #16
0
    def add(self, suffix, binddn=None, bindpw=None, urls=None, attrvals=None, benamebase='localdb', setupmt=False, parent=None):
        """Setup a backend and return its dn. Blank on error XXX should RAISE!
            @param suffix
            @param benamebase   -   the backend common name
            @param setupmt  - eventually setup Mapping Tree entry
            @param urls - a string of ldapurl - create a chaining backend
            @oaram binddn   -   chaining proxy user
            @param bindpw   -   chaining proxy password
            @param attrvals: a dict with further params like
                for ldbm    {
                                'nsslapd-cachememsize': '1073741824',
                                'nsslapd-cachesize': '-1',
                            }
                for chain   {
                                'nsmaxresponsedelay': '60',
                                'nsslapd-sizelimit': '-1'
                            }
                            
            ex.
            benamebase="Isle0-0"
            urls=[ 
                "ldaps://f0-ldap-vip.example.it:636/",
                "ldaps://ldap-18.example.it:636/",
                "ldaps://ldap-19.example.it:636/"
                ]
            
            NOTE: The suffix attribute is a mere string for the backend.
                the following action will work nicely:
                c.backend.add(suffix="foo=example,dc=com",benamebase="db1")
                    
            TODO: rename benamebase with cn
            TODO: split CHAIN and LDBM ? eg. backend.add_chain
        """
        attrvals = attrvals or {}
        dnbase = ""

        # figure out what type of be based on args
        if binddn and bindpw and urls:  # its a chaining be
            dnbase = DN_CHAIN
        else:  # its a ldbm be
            dnbase = DN_LDBM

        nsuffix = normalizeDN(suffix)
        try:
            cn = benamebase 
            self.log.debug("create backend with cn: %s" % cn)
            dn = "cn=" + cn + "," + dnbase
            entry = Entry(dn)
            entry.update({
                'objectclass': ['top', 'extensibleObject', 'nsBackendInstance'],
                'cn': cn,
                'nsslapd-suffix': nsuffix
            })

            if binddn and bindpw and urls:  # its a chaining be
                entry.update({
                             'nsfarmserverurl': urls,
                             'nsmultiplexorbinddn': binddn,
                             'nsmultiplexorcredentials': bindpw
                             })

            # set attrvals (but not cn, because it's in dn)
            # TODO do it in Entry
            if attrvals:
                entry.update(attrvals)

            self.log.debug("adding entry: %r" % entry)
            self.conn.add_s(entry)
        except ldap.ALREADY_EXISTS, e:
            self.log.error("Entry already exists: %r" % dn)
            raise ldap.ALREADY_EXISTS("%s : %r" % (e, dn))
Пример #17
0
    def agreement_add(self, consumer, suffix=None, binddn=None, bindpw=None, cn_format=r'meTo_$host:$port', description_format=r'me to $host:$port', timeout=120, auto_init=False, bindmethod='simple', starttls=False, schedule=ALWAYS, args=None):
        """Create (and return) a replication agreement from self to consumer.
            - self is the supplier,

            @param consumer: one of the following (consumer can be a master)
                    * a DSAdmin object if chaining
                    * an object with attributes: host, port, sslport, __str__
            @param suffix    - eg. 'dc=babel,dc=it'
            @param binddn    - 
            @param bindpw    -
            @param cn_format - string.Template to format the agreement name
            @param timeout   - replica timeout in seconds
            @param auto_init - start replication immediately
            @param bindmethod-  'simple'
            @param starttls  - True or False
            @param schedule  - when to schedule the replication. default: ALWAYS 
            @param args      - further args dict. Allowed keys:
                    'fractional',
                    'stripattrs',
                    'winsync'
                    
            @raise NosuchEntryError    - if a replica doesn't exist for that suffix
            @raise ALREADY_EXISTS
            @raise UNWILLING_TO_PERFORM if the database was previously
                    in read-only state. To create new agreements you
                    need to *restart* the directory server
            
            NOTE: this method doesn't cache connection entries
            
            TODO: test winsync 
            TODO: test chain
            
        """
        import string
        assert binddn and bindpw and suffix
        args = args or {}

        othhost, othport, othsslport = (
            consumer.host, consumer.port, consumer.sslport)
        othport = othsslport or othport
        nsuffix = normalizeDN(suffix)

        # adding agreement to previously created replica
        replica_entries = self.list(suffix)
        if not replica_entries:
            raise NoSuchEntryError(
                "Error: no replica set up for suffix " + suffix)
        replica = replica_entries[0]

        # define agreement entry
        cn = string.Template(cn_format).substitute({'host': othhost, 'port': othport})
        dn_agreement = ','.join(["cn=%s" % cn, replica.dn])

        # This is probably unnecessary because
        # we can just raise ALREADY_EXISTS
        try:
            entry = self.conn.getEntry(dn_agreement, ldap.SCOPE_BASE)
            self.log.warn("Agreement exists: %r" % dn_agreement)
            raise ldap.ALREADY_EXISTS
        except ldap.NO_SUCH_OBJECT:
            entry = None

        # In a separate function in this scope?
        entry = Entry(dn_agreement)
        entry.update({
            'objectclass': ["top", "nsds5replicationagreement"],
            'cn': cn,
            'nsds5replicahost': consumer.host,
            'nsds5replicatimeout': str(timeout),
            'nsds5replicabinddn': binddn,
            'nsds5replicacredentials': bindpw,
            'nsds5replicabindmethod': bindmethod,
            'nsds5replicaroot': nsuffix,
            'description': string.Template(description_format).substitute({'host': othhost, 'port': othport})
        })
        if schedule:
            if not re.match(r'\d{4}-\d{4} [0-6]{1,7}', schedule): # TODO put the regexp in a separate variable
                raise ValueError("Bad schedule format %r" % schedule)
            entry.update({'nsds5replicaupdateschedule': schedule})
        if starttls:
            entry.setValues('nsds5replicatransportinfo', 'TLS')
            entry.setValues('nsds5replicaport', str(othport))
        elif othsslport:
            entry.setValues('nsds5replicatransportinfo', 'SSL')
            entry.setValues('nsds5replicaport', str(othsslport))
        else:
            entry.setValues('nsds5replicatransportinfo', 'LDAP')
            entry.setValues('nsds5replicaport', str(othport))
            
        if auto_init:
            entry.setValues('nsds5BeginReplicaRefresh', 'start')
            
        # further arguments
        if 'fractional' in args:
            entry.setValues('nsDS5ReplicatedAttributeList', args['fractional'])
        # use the specified fractional total - if not specified, use the
        # specified fractional - if not specified, skip
        frac_total = args.get('fractional_total', args.get('fractional', None))
        if frac_total:
            entry.setValues('nsDS5ReplicatedAttributeListTotal', frac_total)
        if 'stripattrs' in args:
            entry.setValues('nsds5ReplicaStripAttrs', args['stripattrs'])
        if 'winsync' in args:  # state it clearly!
            self.conn.setupWinSyncAgmt(args, entry)

        try:
            self.log.debug("Adding replica agreement: [%s]" % entry)
            self.conn.add_s(entry)
        except:
            #  FIXME check please!
            raise

        entry = self.conn.waitForEntry(dn_agreement)
        if entry:
            # More verbose but shows what's going on
            if 'chain' in args:
                chain_args = {
                    'suffix': suffix,
                    'binddn': binddn,
                    'bindpw': bindpw
                }
                # Work on `self` aka producer
                if replica.nsds5replicatype == MASTER_TYPE:
                    self.setupChainingFarm(**chain_args)
                # Work on `consumer`
                # TODO - is it really required?
                if replica.nsds5replicatype == LEAF_TYPE:
                    chain_args.update({
                        'isIntermediate': 0,
                        'urls': self.conn.toLDAPURL(),
                        'args': args['chainargs']
                    })
                    consumer.setupConsumerChainOnUpdate(**chain_args)
                elif replica.nsds5replicatype == HUB_TYPE:
                    chain_args.update({
                        'isIntermediate': 1,
                        'urls': self.conn.toLDAPURL(),
                        'args': args['chainargs']
                    })
                    consumer.setupConsumerChainOnUpdate(**chain_args)

        return dn_agreement
Пример #18
0
    def add(self, suffix, binddn, bindpw, rtype=MASTER_TYPE, rid=None, tombstone_purgedelay=None, purgedelay=None, referrals=None, legacy=False):
        """Setup a replica entry on an existing suffix.
            @param suffix - dn of suffix
            @param binddn - the replication bind dn for this replica
                            can also be a list ["cn=r1,cn=config","cn=r2,cn=config"]
            @param bindpw - used to eventually provision the replication entry

            @param rtype - master, hub, leaf (see above for values) - default is master
            @param rid - replica id or - if not given - an internal sequence number will be assigned

            # further args
            @param legacy - true or false - for legacy consumer
            @param tombstone_purgedelay
            @param purgedelay - changelog expiration time in seconds
            @param referrals

            Ex. replica.add(**{
                    'suffix': "dc=example,dc=com",
                    'type'  : dsadmin.MASTER_TYPE,
                    'binddn': "cn=replication manager,cn=config"
              })
             binddn
            TODO: this method does not update replica type
        """
        # set default values
        if rtype == MASTER_TYPE:
            rtype = REPLICA_RDWR_TYPE
        else:
            rtype = REPLICA_RDONLY_TYPE

        if legacy:
            legacy = 'on'
        else:
            legacy = 'off'

        # create replica entry in mapping-tree
        nsuffix = normalizeDN(suffix)
        mtent = self.conn.getMTEntry(suffix)
        dn_replica = ','.join(("cn=replica", mtent.dn))
        try:
            entry = self.conn.getEntry(dn_replica, ldap.SCOPE_BASE)
            self.log.warn("Already setup replica for suffix %r" % suffix)
            rec = self.conn.suffixes.setdefault(nsuffix, {})
            rec.update({'dn': dn_replica, 'type': rtype})
            return rec
        except ldap.NO_SUCH_OBJECT:
            entry = None

        # If a replica does not exist
        binddnlist = []
        if hasattr(binddn, '__iter__'):
            binddnlist = binddn
        else:
            binddnlist.append(binddn)

        entry = Entry(dn_replica)
        entry.update({
            'objectclass': ("top", "nsds5replica", "extensibleobject"),
            'cn': "replica",
            'nsds5replicaroot': nsuffix,
            'nsds5replicaid': str(rid),
            'nsds5replicatype': str(rtype),
            'nsds5replicalegacyconsumer': legacy,
            'nsds5replicabinddn': binddnlist
        })
        if rtype != LEAF_TYPE:
            entry.setValues('nsds5flags', "1")

        # other args
        if tombstone_purgedelay is not None:
            entry.setValues(
                'nsds5replicatombstonepurgeinterval', str(tombstone_purgedelay))
        if purgedelay is not None:
            entry.setValues('nsds5ReplicaPurgeDelay', str(purgedelay))
        if referrals:
            entry.setValues('nsds5ReplicaReferral', referrals)

        self.conn.add_s(entry)

        # check if the entry exists TODO better to raise!
        self.conn._test_entry(dn_replica, ldap.SCOPE_BASE)

        self.conn.suffixes[nsuffix] = {'dn': dn_replica, 'type': rtype}
        return {'dn': dn_replica, 'type': rtype}