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
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)
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)
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)
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')
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
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)
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
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)
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
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
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)