class cmd_drs_replicate(Command): """Replicate a naming context between two DCs.""" synopsis = "%prog <destinationDC> <sourceDC> <NC> [options]" takes_optiongroups = { "sambaopts": options.SambaOptions, "versionopts": options.VersionOptions, "credopts": options.CredentialsOptions, } takes_args = ["DEST_DC", "SOURCE_DC", "NC"] takes_options = [ Option("--add-ref", help="use ADD_REF to add to repsTo on source", action="store_true"), Option("--sync-forced", help="use SYNC_FORCED to force inbound replication", action="store_true"), Option("--sync-all", help="use SYNC_ALL to replicate from all DCs", action="store_true"), Option("--full-sync", help="resync all objects", action="store_true"), Option("--local", help="pull changes directly into the local database (destination DC is ignored)", action="store_true"), Option("--local-online", help="pull changes into the local database (destination DC is ignored) as a normal online replication", action="store_true"), Option("--async-op", help="use ASYNC_OP for the replication", action="store_true"), Option("--single-object", help="Replicate only the object specified, instead of the whole Naming Context (only with --local)", action="store_true"), ] def drs_local_replicate(self, SOURCE_DC, NC, full_sync=False, single_object=False, sync_forced=False): '''replicate from a source DC to the local SAM''' self.server = SOURCE_DC drsuapi_connect(self) self.local_samdb = SamDB(session_info=system_session(), url=None, credentials=self.creds, lp=self.lp) self.samdb = SamDB(url="ldap://%s" % self.server, session_info=system_session(), credentials=self.creds, lp=self.lp) # work out the source and destination GUIDs res = self.local_samdb.search(base="", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"]) self.ntds_dn = res[0]["dsServiceName"][0] res = self.local_samdb.search(base=self.ntds_dn, scope=ldb.SCOPE_BASE, attrs=["objectGUID"]) self.ntds_guid = misc.GUID( self.samdb.schema_format_value("objectGUID", res[0]["objectGUID"][0])) source_dsa_invocation_id = misc.GUID(self.samdb.get_invocation_id()) dest_dsa_invocation_id = misc.GUID(self.local_samdb.get_invocation_id()) destination_dsa_guid = self.ntds_guid exop = drsuapi.DRSUAPI_EXOP_NONE if single_object: exop = drsuapi.DRSUAPI_EXOP_REPL_OBJ full_sync = True self.samdb.transaction_start() repl = drs_utils.drs_Replicate("ncacn_ip_tcp:%s[seal]" % self.server, self.lp, self.creds, self.local_samdb, dest_dsa_invocation_id) # Work out if we are an RODC, so that a forced local replicate # with the admin pw does not sync passwords rodc = self.local_samdb.am_rodc() try: (num_objects, num_links) = repl.replicate(NC, source_dsa_invocation_id, destination_dsa_guid, rodc=rodc, full_sync=full_sync, exop=exop, sync_forced=sync_forced) except Exception as e: raise CommandError("Error replicating DN %s" % NC, e) self.samdb.transaction_commit() if full_sync: self.message("Full Replication of all %d objects and %d links " "from %s to %s was successful." % (num_objects, num_links, SOURCE_DC, self.local_samdb.url)) else: self.message("Incremental replication of %d objects and %d links " "from %s to %s was successful." % (num_objects, num_links, SOURCE_DC, self.local_samdb.url)) def run(self, DEST_DC, SOURCE_DC, NC, add_ref=False, sync_forced=False, sync_all=False, full_sync=False, local=False, local_online=False, async_op=False, single_object=False, sambaopts=None, credopts=None, versionopts=None): self.server = DEST_DC self.lp = sambaopts.get_loadparm() self.creds = credopts.get_credentials(self.lp, fallback_machine=True) if local: self.drs_local_replicate(SOURCE_DC, NC, full_sync=full_sync, single_object=single_object, sync_forced=sync_forced) return if local_online: server_bind = drsuapi.drsuapi("irpc:dreplsrv", lp_ctx=self.lp) server_bind_handle = misc.policy_handle() else: drsuapi_connect(self) server_bind = self.drsuapi server_bind_handle = self.drsuapi_handle if not async_op: # Give the sync replication 5 minutes time server_bind.request_timeout = 5 * 60 samdb_connect(self) # we need to find the NTDS GUID of the source DC msg = self.samdb.search(base=self.samdb.get_config_basedn(), expression="(&(objectCategory=server)(|(name=%s)(dNSHostName=%s)))" % ( ldb.binary_encode(SOURCE_DC), ldb.binary_encode(SOURCE_DC)), attrs=[]) if len(msg) == 0: raise CommandError("Failed to find source DC %s" % SOURCE_DC) server_dn = msg[0]['dn'] msg = self.samdb.search(base=server_dn, scope=ldb.SCOPE_ONELEVEL, expression="(|(objectCategory=nTDSDSA)(objectCategory=nTDSDSARO))", attrs=['objectGUID', 'options']) if len(msg) == 0: raise CommandError("Failed to find source NTDS DN %s" % SOURCE_DC) source_dsa_guid = msg[0]['objectGUID'][0] dsa_options = int(attr_default(msg, 'options', 0)) req_options = 0 if not (dsa_options & dsdb.DS_NTDSDSA_OPT_DISABLE_OUTBOUND_REPL): req_options |= drsuapi.DRSUAPI_DRS_WRIT_REP if add_ref: req_options |= drsuapi.DRSUAPI_DRS_ADD_REF if sync_forced: req_options |= drsuapi.DRSUAPI_DRS_SYNC_FORCED if sync_all: req_options |= drsuapi.DRSUAPI_DRS_SYNC_ALL if full_sync: req_options |= drsuapi.DRSUAPI_DRS_FULL_SYNC_NOW if async_op: req_options |= drsuapi.DRSUAPI_DRS_ASYNC_OP try: drs_utils.sendDsReplicaSync(server_bind, server_bind_handle, source_dsa_guid, NC, req_options) except drs_utils.drsException as estr: raise CommandError("DsReplicaSync failed", estr) if async_op: self.message("Replicate from %s to %s was started." % (SOURCE_DC, DEST_DC)) else: self.message("Replicate from %s to %s was successful." % (SOURCE_DC, DEST_DC))