Beispiel #1
0
    def changes(self, agmnt_dn):
        """Get a number of changes sent by this agreement.

        :param agmtdn: agreement dn
        :type agmtdn: str

        :returns: Number of changes
        :raises: NoSuchEntryError - if agreement entry with changes
                  attribute is not found
        """

        retval = 0
        try:
            ent = self.conn.getEntry(ensure_str(agmnt_dn), ldap.SCOPE_BASE,
                                     "(objectclass=*)",
                                     [RA_PROPNAME_TO_ATTRNAME[RA_CHANGES]])
        except:
            raise NoSuchEntryError("Error reading status from agreement",
                                   agmnt_dn)

        if ent.nsds5replicaChangesSentSinceStartup:
            val = ent.nsds5replicaChangesSentSinceStartup
            items = val.split(ensure_bytes(' '))
            if len(items) == 1:
                retval = int(items[0])
            else:
                for item in items:
                    ary = item.split(ensure_bytes(":"))
                    if ary and len(ary) > 1:
                        retval = retval + int(ary[1].split(
                            ensure_bytes("/"))[0])
        return retval
Beispiel #2
0
    def list(self,
             suffix=None,
             consumer_host=None,
             consumer_port=None,
             agmtdn=None):
        '''
            Returns the search result of the replica agreement(s) under the replica (replicaRoot is 'suffix').

            Either 'suffix' or 'agmtdn' need to be specfied.
            'consumer_host' and 'consumer_port' are either not specified or specified both.

            If 'agmtdn' is specified, it returns the search result entry of that replication agreement.
            else if consumer host/port are specified it returns the replica agreements toward
            that consumer host:port.
            Finally if neither 'agmtdn' nor 'consumser host/port' are specifies it returns
            all the replica agreements under the replica (replicaRoot is 'suffix').

            @param - suffix is the suffix targeted by the total update
            @param - consumer_host hostname of the consumer
            @param - consumer_port port of the consumer
            @param - agmtdn DN of the replica agreement

            @return - search result of the replica agreements

            @raise - InvalidArgument: if missing mandatory argument (agmtdn or suffix, then host and port)
                   - ValueError - if some properties are not valid
                   - NoSuchEntryError - If no replica defined for the suffix
        '''
        if not suffix and not agmtdn:
            raise InvalidArgumentError("suffix or agmtdn are required")

        if (consumer_host and not consumer_port) or (not consumer_host
                                                     and consumer_port):
            raise InvalidArgumentError(
                "consumer host/port are required together")

        if agmtdn:
            # easy case, just return the RA
            filt = "objectclass=*"
            return self.conn.search_s(agmtdn, ldap.SCOPE_BASE, filt)
        else:
            # Retrieve the replica
            replica_entries = self.conn.replica.list(suffix)
            if not replica_entries:
                raise NoSuchEntryError("Error: no replica set up for suffix " +
                                       suffix)
            replica_entry = replica_entries[0]

            # Now returns the replica agreement for that suffix that replicates to
            # consumer host/port
            if consumer_host and consumer_port:
                filt = "(&(|(objectclass=%s)(objectclass=%s))(%s=%s)(%s=%d))" % (
                    RA_OBJECTCLASS_VALUE, RA_WINDOWS_OBJECTCLASS_VALUE,
                    RA_PROPNAME_TO_ATTRNAME[RA_CONSUMER_HOST], consumer_host,
                    RA_PROPNAME_TO_ATTRNAME[RA_CONSUMER_PORT], consumer_port)
            else:
                filt = "(|(objectclass=%s)(objectclass=%s))" % (
                    RA_OBJECTCLASS_VALUE, RA_WINDOWS_OBJECTCLASS_VALUE)
            return self.conn.search_s(replica_entry.dn, ldap.SCOPE_ONELEVEL,
                                      filt)
Beispiel #3
0
    def ruv(self, suffix, tryrepl=False):
        """return a replica update vector for the given suffix.

            @param suffix - eg. 'o=netscapeRoot'

            @raises NoSuchEntryError if missing
        """
        filt = "(&(nsUniqueID=%s)(objectclass=%s))" % (REPLICA_RUV_UUID,
                                                       REPLICA_OC_TOMBSTONE)
        attrs = ['nsds50ruv', 'nsruvReplicaLastModified']
        ents = self.conn.search_s(suffix, ldap.SCOPE_SUBTREE, filt, attrs)
        ent = None
        if ents and (len(ents) > 0):
            ent = ents[0]
        elif tryrepl:
            self.log.warn(
                "Could not get RUV from %r entry - trying cn=replica" % suffix)
            ensuffix = escapeDNValue(normalizeDN(suffix))
            dn = ','.join(("cn=replica", "cn=%s" % ensuffix, DN_MAPPING_TREE))
            ents = self.conn.search_s(dn, ldap.SCOPE_BASE, "objectclass=*",
                                      attrs)

        if ents and (len(ents) > 0):
            ent = ents[0]
            self.log.debug("RUV entry is %r" % ent)
            return RUV(ent)

        raise NoSuchEntryError("RUV not found: suffix: %r" % suffix)
Beispiel #4
0
    def init(self, suffix=None, consumer_host=None, consumer_port=None):
        """Trigger a total update of the consumer replica
            - self is the supplier,
            - consumer is a DirSrv object (consumer can be a master)
            - cn_format - use this string to format the agreement name
            @param - suffix is the suffix targeted by the total update [mandatory]
            @param - consumer_host hostname of the consumer [mandatory]
            @param - consumer_port port of the consumer [mandatory]

            @raise InvalidArgument: if missing mandatory argurment (suffix/host/port)
        """
        #
        # check the required parameters are set
        #
        if not suffix:
            self.log.fatal("initAgreement: suffix is missing")
            raise InvalidArgumentError('suffix is mandatory argument')

        nsuffix = normalizeDN(suffix)

        if not consumer_host:
            self.log.fatal("initAgreement: host is missing")
            raise InvalidArgumentError('host is mandatory argument')

        if not consumer_port:
            self.log.fatal("initAgreement: port is missing")
            raise InvalidArgumentError('port is mandatory argument')
        #
        # check the replica agreement already exist
        #
        replica_entries = self.conn.replica.list(suffix)
        if not replica_entries:
            raise NoSuchEntryError("Error: no replica set up for suffix " +
                                   suffix)
        replica_entry = replica_entries[0]
        self.log.debug(
            "initAgreement: looking for replica agreements under %s" %
            replica_entry.dn)
        try:
            filt = (
                "(&(objectclass=nsds5replicationagreement)(nsds5replicahost=" +
                "%s)(nsds5replicaport=%d)(nsds5replicaroot=%s))" %
                (consumer_host, consumer_port, nsuffix))
            entry = self.conn.getEntry(replica_entry.dn, ldap.SCOPE_ONELEVEL,
                                       filt)
        except ldap.NO_SUCH_OBJECT:
            self.log.fatal(
                "initAgreement: No replica agreement to %s:%d for suffix %s" %
                (consumer_host, consumer_port, nsuffix))
            raise

        #
        # trigger the total init
        #
        self.log.info("Starting total init %s" % entry.dn)
        mod = [(ldap.MOD_ADD, 'nsds5BeginReplicaRefresh', 'start')]
        self.conn.modify_s(entry.dn, mod)
Beispiel #5
0
    def delete(self,
               suffix=None,
               consumer_host=None,
               consumer_port=None,
               agmtdn=None):
        """Delete a replication agreement

        :param suffix: The suffix that the agreement is configured for
        :type suffix: str
        :param consumer_host: Host of the server that the agreement points to
        :type consumer_host: str
        :param consumer_port: Port of the server that the agreement points to
        :type consumer_port: int
        :param agmtdn: DN of the replica agreement
        :type agmtdn: str

        :returns: None
        :raises: - ldap.LDAPError - for ldap operation failures
                  - TypeError - if too many agreements were found
                  - NoSuchEntryError - if no agreements were found
        """

        if not (suffix and consumer_host and consumer_port) and not agmtdn:
            raise InvalidArgumentError(
                "Suffix with consumer_host and consumer_port" +
                " or agmtdn are required")

        agmts = self.list(suffix, consumer_host, consumer_port, agmtdn)

        if agmts:
            if len(agmts) > 1:
                raise TypeError('Too many agreements were found')
            else:
                # Delete the agreement
                try:
                    agmt_dn = agmts[0].dn
                    self.conn.delete_s(agmt_dn)
                    self.log.info('Agreement (%s) was successfully removed',
                                  agmt_dn)
                except ldap.LDAPError as e:
                    self.log.error(
                        'Failed to delete agreement (%s), '
                        'error: %s', agmt_dn, e)
                    raise
        else:
            raise NoSuchEntryError('No agreements were found')
Beispiel #6
0
    def changes(self, agmnt_dn):
        """Return a list of changes sent by this agreement."""
        retval = 0
        try:
            ent = self.conn.getEntry(agmnt_dn, ldap.SCOPE_BASE,
                                     "(objectclass=*)",
                                     [RA_PROPNAME_TO_ATTRNAME[RA_CHANGES]])
        except:
            raise NoSuchEntryError("Error reading status from agreement",
                                   agmnt_dn)

        if ent.nsds5replicaChangesSentSinceStartup:
            val = ent.nsds5replicaChangesSentSinceStartup
            items = val.split(' ')
            if len(items) == 1:
                retval = int(items[0])
            else:
                for item in items:
                    ary = item.split(":")
                    if ary and len(ary) > 1:
                        retval = retval + int(ary[1].split("/")[0])
        return retval
Beispiel #7
0
    def init(self, suffix=None, consumer_host=None, consumer_port=None):
        """Trigger a total update of the consumer replica
        - self is the supplier,
        - consumer is a DirSrv object (consumer can be a master)
        - cn_format - use this string to format the agreement name

        :param suffix: The suffix targeted by the total update [mandatory]
        :type suffix: str
        :param consumer_host: Hostname of the consumer [mandatory]
        :type consumer_host: str
        :param consumer_port: Port of the consumer [mandatory]
        :type consumer_port: int

        :returns: None
        :raises: InvalidArgument - if missing mandatory argument
        """

        #
        # check the required parameters are set
        #
        if not suffix:
            self.log.fatal("initAgreement: suffix is missing")
            raise InvalidArgumentError('suffix is mandatory argument')

        nsuffix = normalizeDN(suffix)

        if not consumer_host:
            self.log.fatal("initAgreement: host is missing")
            raise InvalidArgumentError('host is mandatory argument')

        if not consumer_port:
            self.log.fatal("initAgreement: port is missing")
            raise InvalidArgumentError('port is mandatory argument')
        #
        # check the replica agreement already exist
        #
        replica_entries = self.conn.replica.list(suffix)
        if not replica_entries:
            raise NoSuchEntryError("Error: no replica set up for suffix " +
                                   suffix)
        replica_entry = replica_entries[0]
        self.log.debug("initAgreement: looking for replica agreements " +
                       "under %s" % replica_entry.dn)
        try:
            '''
            Currently python does not like long continuous lines when it
            comes to string formatting, so we need to separate each line
            like this.
            '''
            filt = "(&(objectclass=nsds5replicationagreement)"
            filt += "(nsds5replicahost=%s)" % consumer_host
            filt += "(nsds5replicaport=%d)" % consumer_port
            filt += "(nsds5replicaroot=%s))" % nsuffix

            entry = self.conn.getEntry(replica_entry.dn, ldap.SCOPE_ONELEVEL,
                                       filt)
        except ldap.NO_SUCH_OBJECT:
            msg = ('initAgreement: No replica agreement to ' +
                   '{host}:{port} for suffix {suffix}'.format(
                       host=consumer_host, port=consumer_port, suffix=nsuffix))
            self.log.fatal(msg)
            raise

        #
        # trigger the total init
        #
        self.log.info("Starting total init %s" % entry.dn)
        mod = [(ldap.MOD_ADD, 'nsds5BeginReplicaRefresh',
                ensure_bytes('start'))]
        self.conn.modify_s(entry.dn, mod)
Beispiel #8
0
    def create(self,
               suffix=None,
               host=None,
               port=None,
               properties=None,
               winsync=False):
        """Create (and return) a replication agreement from self to consumer.
        Self is the supplier.

        :param suffix: Replication Root
        :type suffix: str
        :param host: Consumer host
        :type host: str
        :param port: Consumer port
        :type port: int
        :param winsync: Identifies the agree as a WinSync agreement
        :type winsync: bool
        :param properties: Agreement properties
        :type properties: dict

        :returns: DN of the created agreement
        :raises: - InvalidArgumentError - If the suffix is missing
                 - NoSuchEntryError - if a replica doesn't exist for that suffix
                 - ldap.LDAPError - ldap error
        """

        # Check we have a suffix [ mandatory ]
        if not suffix:
            self.log.warning("create: suffix is missing")
            raise InvalidArgumentError('suffix is mandatory')

        if not properties:
            properties = {}

        # Compute the normalized suffix to be set in RA entry
        properties[RA_SUFFIX] = normalizeDN(suffix)

        # Adding agreement under the replica entry
        replica_entries = self.conn.replica.list(suffix)
        if not replica_entries:
            raise NoSuchEntryError("Error: no replica set up for suffix: %s" %
                                   suffix)
        replica = replica_entries[0]

        # Define agreement entry
        if RA_NAME not in properties:
            properties[RA_NAME] = 'meTo_%s:%s' % (host, port)
        dn_agreement = ','.join(["cn=%s" % properties[RA_NAME], replica.dn])

        # Set the required properties(if not already set)
        if RA_BINDDN not in properties:
            properties[RA_BINDDN] = defaultProperties[REPLICATION_BIND_DN]
        if RA_BINDPW not in properties:
            properties[RA_BINDPW] = defaultProperties[REPLICATION_BIND_PW]
        if RA_METHOD not in properties:
            properties[RA_METHOD] = defaultProperties[REPLICATION_BIND_METHOD]
        if RA_TRANSPORT_PROT not in properties:
            properties[RA_TRANSPORT_PROT] = \
                defaultProperties[REPLICATION_TRANSPORT]
        if RA_TIMEOUT not in properties:
            properties[RA_TIMEOUT] = defaultProperties[REPLICATION_TIMEOUT]
        if RA_DESCRIPTION not in properties:
            properties[RA_DESCRIPTION] = properties[RA_NAME]
        if RA_CONSUMER_HOST not in properties:
            properties[RA_CONSUMER_HOST] = host
        if RA_CONSUMER_PORT not in properties:
            properties[RA_CONSUMER_PORT] = str(port)

        # 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 already exists: %r" % dn_agreement)
            return dn_agreement
        except ldap.NO_SUCH_OBJECT:
            entry = None

        # Iterate over the properties, adding them to the entry
        entry = Entry(dn_agreement)
        entry.update({'objectclass': ["top", RA_OBJECTCLASS_VALUE]})
        for prop in properties:
            entry.update({RA_PROPNAME_TO_ATTRNAME[prop]: properties[prop]})

        # we make a copy here because we cannot change
        # the passed in properties dict
        propertiescopy = {}
        if properties:
            import copy
            propertiescopy = copy.deepcopy(properties)

        # Check if this a Winsync Agreement
        if winsync:
            self.conn.setupWinSyncAgmt(propertiescopy, entry)

        try:
            self.log.debug("Adding replica agreement: [%r]" % entry)
            self.conn.add_s(entry)
        except ldap.LDAPError as e:
            self.log.fatal('Failed to add replication agreement: %s' % str(e))
            raise e

        entry = self.conn.waitForEntry(dn_agreement)
        if entry:
            # More verbose but shows what's going on
            if 'chain' in propertiescopy:
                raise NotImplementedError
                chain_args = {
                    'suffix': suffix,
                    'binddn': binddn,
                    'bindpw': bindpw
                }
                # Work on `self` aka producer
                if replica.nsds5replicatype == MASTER_TYPE:
                    self.conn.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': propertiescopy['chainargs']
                    })
                    consumer.setupConsumerChainOnUpdate(**chain_args)
                elif replica.nsds5replicatype == HUB_TYPE:
                    chain_args.update({
                        'isIntermediate': 1,
                        'urls': self.conn.toLDAPURL(),
                        'args': propertiescopy['chainargs']
                    })
                    consumer.setupConsumerChainOnUpdate(**chain_args)

        return dn_agreement
Beispiel #9
0
    def list(self,
             suffix=None,
             consumer_host=None,
             consumer_port=None,
             agmtdn=None):
        """Returns the search result of the replica agreement(s) under the
        replica (replicaRoot is 'suffix').

        Either 'suffix' or 'agmtdn' need to be specfied.
        'consumer_host' and 'consumer_port' are either not specified or
        specified both.

        If 'agmtdn' is specified, it returns the search result entry of
        that replication agreement, else if consumer host/port are specified
        it returns the replica agreements toward that consumer host:port.

        Finally if neither 'agmtdn' nor 'consumser host/port' are
        specifies it returns all the replica agreements under the replica
        (replicaRoot is 'suffix').

        :param suffix: The suffix targeted by the total update
        :type suffix: str
        :param consumer_host: Hostname of the consumer
        :type consumer_host: str
        :param consumer_port: Port of the consumer
        :type consumer_port: int
        :param agmtdn: DN of the replica agreement
        :type agmtdn: str

        :returns: Search result of the replica agreements
        :raises: - InvalidArgument - if missing mandatory argument
                    (agmtdn or suffix, then host and port)
                  - ValueError - if some properties are not valid
                  - NoSuchEntryError - If no replica defined for the suffix
        """

        if not suffix and not agmtdn:
            raise InvalidArgumentError("suffix or agmtdn are required")

        if (consumer_host and not consumer_port) or (not consumer_host
                                                     and consumer_port):
            raise InvalidArgumentError(
                "consumer host/port are required together")

        if agmtdn:
            # easy case, just return the RA
            filt = "objectclass=*"
            return self.conn.search_s(agmtdn, ldap.SCOPE_BASE, filt)
        else:
            # Retrieve the replica
            replica_entries = self.conn.replica.list(suffix)
            if not replica_entries:
                raise NoSuchEntryError("Error: no replica set up for suffix "
                                       "(%s)" % suffix)
            replica_entry = replica_entries[0]

            # Now returns the replica agreement for that suffix that replicates
            # to consumer host/port
            if consumer_host and consumer_port:
                '''
                Currently python does not like long continuous lines when it
                comes to string formatting, so we need to separate each line
                like this.
                '''
                filt = "(&(|(objectclass=%s)" % RA_OBJECTCLASS_VALUE
                filt += "(objectclass=%s))" % RA_WINDOWS_OBJECTCLASS_VALUE
                filt += "(%s=%s)" % (RA_PROPNAME_TO_ATTRNAME[RA_CONSUMER_HOST],
                                     consumer_host)
                filt += "(%s=%d))" % (
                    RA_PROPNAME_TO_ATTRNAME[RA_CONSUMER_PORT], consumer_port)
            else:
                filt = ("(|(objectclass=%s)(objectclass=%s))" %
                        (RA_OBJECTCLASS_VALUE, RA_WINDOWS_OBJECTCLASS_VALUE))

            return self.conn.search_s(replica_entry.dn, ldap.SCOPE_ONELEVEL,
                                      filt)
Beispiel #10
0
    def status(self, agreement_dn, just_status=False):
        """Get a formatted string with the replica status

        :param agreement_dn: DN of the replica agreement
        :type agreement_dn: str
        :param just_status: If True, returns just status
        :type just_status: bool

        :returns: str -- See below
        :raises: NoSuchEntryError - if agreement_dn is an unknown entry

        :example:
            ::

                Status for meTo_localhost.localdomain:50389 agmt
                    localhost.localdomain:50389
                Update in progress: TRUE
                Last Update Start: 20131121132756Z
                Last Update End: 0
                Num. Changes Sent: 1:10/0
                Num. changes Skipped: None
                Last update Status: 0 Replica acquired successfully:
                    Incremental update started
                Init in progress: None
                Last Init Start: 0
                Last Init End: 0
                Last Init Status: None
                Reap Active: 0
        """

        attrlist = [
            'cn', 'nsds5BeginReplicaRefresh', 'nsds5ReplicaRoot',
            'nsds5replicaUpdateInProgress', 'nsds5ReplicaLastInitStatus',
            'nsds5ReplicaLastInitStart', 'nsds5ReplicaLastInitEnd',
            'nsds5replicaReapActive', 'nsds5replicaLastUpdateStart',
            'nsds5replicaLastUpdateEnd', 'nsds5replicaChangesSentSinceStartup',
            'nsds5replicaLastUpdateStatus',
            'nsds5replicaChangesSkippedSinceStartup', 'nsds5ReplicaHost',
            'nsds5ReplicaPort', 'nsds5ReplicaEnabled', 'nsds5AgmtMaxCSN'
        ]
        try:
            ent = self.conn.getEntry(agreement_dn, ldap.SCOPE_BASE,
                                     "(objectclass=*)", attrlist)
        except NoSuchEntryError:
            raise NoSuchEntryError("Error reading status from agreement",
                                   agreement_dn)
        else:
            status = self.conn.getReplAgmtStatus(ent)
            if just_status:
                return status

            retstr = (
                "Status for %(cn)s agmt %(nsDS5ReplicaHost)s:"
                "%(nsDS5ReplicaPort)s"
                "\n"
                "Replica Enabled: %(nsds5ReplicaEnabled)s"
                "\n"
                "Agreement maxCSN: %(nsds5AgmtMaxCSN)s"
                "\n"
                "Update in progress: %(nsds5replicaUpdateInProgress)s"
                "\n"
                "Last Update Start: %(nsds5replicaLastUpdateStart)s"
                "\n"
                "Last Update End: %(nsds5replicaLastUpdateEnd)s"
                "\n"
                "Num. Changes Sent: %(nsds5replicaChangesSentSinceStartup)s"
                "\n"
                "Num. changes Skipped: %(nsds5replicaChangesSkippedSince"
                "Startup)s"
                "\n"
                "Last update Status: %(nsds5replicaLastUpdateStatus)s"
                "\n"
                "Init in progress: %(nsds5BeginReplicaRefresh)s"
                "\n"
                "Last Init Start: %(nsds5ReplicaLastInitStart)s"
                "\n"
                "Last Init End: %(nsds5ReplicaLastInitEnd)s"
                "\n"
                "Last Init Status: %(nsds5ReplicaLastInitStatus)s"
                "\n"
                "Reap Active: %(nsds5ReplicaReapActive)s"
                "\n")
            # FormatDict manages missing fields in string formatting
            entry_data = ensure_dict_str(ent.data)
            result = retstr % FormatDict(entry_data)
            result += "Replication Status: %s\n" % status
            return result
Beispiel #11
0
    def create(self, suffix=None, host=None, port=None, properties=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 DirSrv object if chaining
                    * an object with attributes: host, port, sslport, __str__
            @param suffix    - eg. 'dc=babel,dc=it'
            @param properties      - further properties dict.
            Support properties
                RA_NAME
                RA_SUFFIX
                RA_BINDDN
                RA_BINDPW
                RA_METHOD
                RA_DESCRIPTION
                RA_SCHEDULE
                RA_TRANSPORT_PROT
                RA_FRAC_EXCLUDE
                RA_FRAC_EXCLUDE_TOTAL_UPDATE
                RA_FRAC_STRIP
                RA_CONSUMER_PORT
                RA_CONSUMER_HOST
                RA_CONSUMER_TOTAL_INIT
                RA_TIMEOUT
                RA_CHANGES


            @return dn_agreement - DN of the created agreement

            @raise InvalidArgumentError - If the suffix is missing
            @raise NosuchEntryError     - if a replica doesn't exist for that suffix
            @raise UNWILLING_TO_PERFORM if the database was previously
                    in read-only state. To create new agreements you
                    need to *restart* the directory server

        """
        import string

        # Check we have a suffix [ mandatory ]
        if not suffix:
            self.log.warning("create: suffix is missing")
            raise InvalidArgumentError('suffix is mandatory')

        if properties:
            binddn = properties.get(
                RA_BINDDN) or defaultProperties[REPLICATION_BIND_DN]
            bindpw = properties.get(
                RA_BINDPW) or defaultProperties[REPLICATION_BIND_PW]
            bindmethod = properties.get(
                RA_METHOD) or defaultProperties[REPLICATION_BIND_METHOD]
            format = properties.get(RA_NAME) or r'meTo_$host:$port'
            description = properties.get(RA_DESCRIPTION) or format
            transport = properties.get(
                RA_TRANSPORT_PROT) or defaultProperties[REPLICATION_TRANSPORT]
            timeout = properties.get(
                RA_TIMEOUT) or defaultProperties[REPLICATION_TIMEOUT]
        else:
            binddn = defaultProperties[REPLICATION_BIND_DN]
            bindpw = defaultProperties[REPLICATION_BIND_PW]
            bindmethod = defaultProperties[REPLICATION_BIND_METHOD]
            format = r'meTo_$host:$port'
            description = format
            transport = defaultProperties[REPLICATION_TRANSPORT]
            timeout = defaultProperties[REPLICATION_TIMEOUT]

        # Compute the normalized suffix to be set in RA entry
        nsuffix = normalizeDN(suffix)

        # adding agreement under the replica entry
        replica_entries = self.conn.replica.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(format).substitute({'host': host, 'port': port})
        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 already exists: %r" % dn_agreement)
            return dn_agreement
        except ldap.NO_SUCH_OBJECT:
            entry = None

        # In a separate function in this scope?
        entry = Entry(dn_agreement)
        entry.update({
            'objectclass': ["top", RA_OBJECTCLASS_VALUE],
            RA_PROPNAME_TO_ATTRNAME[RA_NAME]:
            cn,
            RA_PROPNAME_TO_ATTRNAME[RA_SUFFIX]:
            nsuffix,
            RA_PROPNAME_TO_ATTRNAME[RA_CONSUMER_HOST]:
            host,
            RA_PROPNAME_TO_ATTRNAME[RA_CONSUMER_PORT]:
            str(port),
            RA_PROPNAME_TO_ATTRNAME[RA_TRANSPORT_PROT]:
            transport,
            RA_PROPNAME_TO_ATTRNAME[RA_TIMEOUT]:
            str(timeout),
            RA_PROPNAME_TO_ATTRNAME[RA_BINDDN]:
            binddn,
            RA_PROPNAME_TO_ATTRNAME[RA_BINDPW]:
            bindpw,
            RA_PROPNAME_TO_ATTRNAME[RA_METHOD]:
            bindmethod,
            RA_PROPNAME_TO_ATTRNAME[RA_DESCRIPTION]:
            string.Template(description).substitute({
                'host': host,
                'port': port
            })
        })

        # we make a copy here because we cannot change
        # the passed in properties dict
        propertiescopy = {}
        if properties:
            import copy
            propertiescopy = copy.deepcopy(properties)

        # further arguments
        if 'winsync' in propertiescopy:  # state it clearly!
            self.conn.setupWinSyncAgmt(propertiescopy, entry)

        try:
            self.log.debug("Adding replica agreement: [%r]" % 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 propertiescopy:
                raise NotImplementedError
                chain_args = {
                    'suffix': suffix,
                    'binddn': binddn,
                    'bindpw': bindpw
                }
                # Work on `self` aka producer
                if replica.nsds5replicatype == MASTER_TYPE:
                    self.conn.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': propertiescopy['chainargs']
                    })
                    consumer.setupConsumerChainOnUpdate(**chain_args)
                elif replica.nsds5replicatype == HUB_TYPE:
                    chain_args.update({
                        'isIntermediate': 1,
                        'urls': self.conn.toLDAPURL(),
                        'args': propertiescopy['chainargs']
                    })
                    consumer.setupConsumerChainOnUpdate(**chain_args)

        return dn_agreement
Beispiel #12
0
    def status(self, agreement_dn):
        """Return a formatted string with the replica status. Looking like:
            Status for meTo_localhost.localdomain:50389 agmt localhost.localdomain:50389
            Update in progress: TRUE
            Last Update Start: 20131121132756Z
            Last Update End: 0
            Num. Changes Sent: 1:10/0
            Num. changes Skipped: None
            Last update Status: 0 Replica acquired successfully: Incremental update started
            Init in progress: None
            Last Init Start: 0
            Last Init End: 0
            Last Init Status: None
            Reap Active: 0
            @param agreement_dn - DN of the replication agreement

            @returns string containing the status of the replica agreement

            @raise NoSuchEntryError - if agreement_dn is an unknown entry
        """

        attrlist = [
            'cn', 'nsds5BeginReplicaRefresh', 'nsds5replicaUpdateInProgress',
            'nsds5ReplicaLastInitStatus', 'nsds5ReplicaLastInitStart',
            'nsds5ReplicaLastInitEnd', 'nsds5replicaReapActive',
            'nsds5replicaLastUpdateStart', 'nsds5replicaLastUpdateEnd',
            'nsds5replicaChangesSentSinceStartup',
            'nsds5replicaLastUpdateStatus',
            'nsds5replicaChangesSkippedSinceStartup', 'nsds5ReplicaHost',
            'nsds5ReplicaPort'
        ]
        try:
            ent = self.conn.getEntry(agreement_dn, ldap.SCOPE_BASE,
                                     "(objectclass=*)", attrlist)
        except NoSuchEntryError:
            raise NoSuchEntryError("Error reading status from agreement",
                                   agreement_dn)
        else:
            retstr = (
                "Status for %(cn)s agmt %(nsDS5ReplicaHost)s:%(nsDS5ReplicaPort)s"
                "\n"
                "Update in progress: %(nsds5replicaUpdateInProgress)s"
                "\n"
                "Last Update Start: %(nsds5replicaLastUpdateStart)s"
                "\n"
                "Last Update End: %(nsds5replicaLastUpdateEnd)s"
                "\n"
                "Num. Changes Sent: %(nsds5replicaChangesSentSinceStartup)s"
                "\n"
                "Num. changes Skipped: %(nsds5replicaChangesSkippedSinceStartup)s"
                "\n"
                "Last update Status: %(nsds5replicaLastUpdateStatus)s"
                "\n"
                "Init in progress: %(nsds5BeginReplicaRefresh)s"
                "\n"
                "Last Init Start: %(nsds5ReplicaLastInitStart)s"
                "\n"
                "Last Init End: %(nsds5ReplicaLastInitEnd)s"
                "\n"
                "Last Init Status: %(nsds5ReplicaLastInitStatus)s"
                "\n"
                "Reap Active: %(nsds5ReplicaReapActive)s"
                "\n")
            # FormatDict manages missing fields in string formatting
            return retstr % FormatDict(ent.data)