mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
drs_utils: Add infrastructure to support 'clone with rename'
Our end goal is to create a backup clone of a DB, but rename the domain/realm so we can startup the backup DC without interferring with the existing Samba network. The basic strategy to do this is to leverage DRS replication - by renaming the first object in the partition, all subsequent objects will automatically be renamed. This patch adds the infrastructure to do this. I've used object inheritance to handle the special case of renaming the partition objects. This means the domain-rename special case doesn't really pollute the existing DRS replication code. All it needs is a small refactor to create a new 'process_chunk()' function that the new sub-class can then override. Signed-off-by: Tim Beale <timbeale@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org> Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
This commit is contained in:
parent
00d22122e5
commit
417fe47a91
@ -24,7 +24,8 @@ from samba import dsdb
|
||||
from samba import werror
|
||||
from samba import WERRORError
|
||||
import samba, ldb
|
||||
|
||||
from samba.dcerpc.drsuapi import DRSUAPI_ATTID_name
|
||||
import re
|
||||
|
||||
class drsException(Exception):
|
||||
"""Base element for drs errors"""
|
||||
@ -213,6 +214,12 @@ class drs_Replicate(object):
|
||||
(req.more_flags & drsuapi.DRSUAPI_DRS_GET_TGT) == 0 and
|
||||
self.supported_extensions & drsuapi.DRSUAPI_SUPPORTED_EXTENSION_GETCHGREQ_V10)
|
||||
|
||||
def process_chunk(self, level, ctr, schema, req_level, req, first_chunk):
|
||||
'''Processes a single chunk of received replication data'''
|
||||
# pass the replication into the py_net.c python bindings for processing
|
||||
self.net.replicate_chunk(self.replication_state, level, ctr,
|
||||
schema=schema, req_level=req_level, req=req)
|
||||
|
||||
def replicate(self, dn, source_dsa_invocation_id, destination_dsa_guid,
|
||||
schema=False, exop=drsuapi.DRSUAPI_EXOP_NONE, rodc=False,
|
||||
replica_flags=None, full_sync=True, sync_forced=False, more_flags=0):
|
||||
@ -309,14 +316,15 @@ class drs_Replicate(object):
|
||||
|
||||
num_objects = 0
|
||||
num_links = 0
|
||||
first_chunk = True
|
||||
|
||||
while True:
|
||||
(level, ctr) = self.drs.DsGetNCChanges(self.drs_handle, req_level, req)
|
||||
if ctr.first_object is None and ctr.object_count != 0:
|
||||
raise RuntimeError("DsGetNCChanges: NULL first_object with object_count=%u" % (ctr.object_count))
|
||||
|
||||
try:
|
||||
self.net.replicate_chunk(self.replication_state, level, ctr,
|
||||
schema=schema, req_level=req_level, req=req)
|
||||
self.process_chunk(level, ctr, schema, req_level, req, first_chunk)
|
||||
except WERRORError as e:
|
||||
# Check if retrying with the GET_TGT flag set might resolve this error
|
||||
if self._should_retry_with_get_tgt(e.args[0], req):
|
||||
@ -324,11 +332,14 @@ class drs_Replicate(object):
|
||||
print("Missing target object - retrying with DRS_GET_TGT")
|
||||
req.more_flags |= drsuapi.DRSUAPI_DRS_GET_TGT
|
||||
|
||||
# try sending the request again
|
||||
# try sending the request again (this has the side-effect
|
||||
# of causing the DC to restart the replication from scratch)
|
||||
first_chunk = True
|
||||
continue
|
||||
else:
|
||||
raise e
|
||||
|
||||
first_chunk = False
|
||||
num_objects += ctr.object_count
|
||||
|
||||
# Cope with servers that do not return level 6, so do not return any links
|
||||
@ -342,3 +353,58 @@ class drs_Replicate(object):
|
||||
req.highwatermark = ctr.new_highwatermark
|
||||
|
||||
return (num_objects, num_links)
|
||||
|
||||
|
||||
# Handles the special case of creating a new clone of a DB, while also renaming
|
||||
# the entire DB's objects on the way through
|
||||
class drs_ReplicateRenamer(drs_Replicate):
|
||||
'''Uses DRS replication to rename the entire DB'''
|
||||
|
||||
def __init__(self, binding_string, lp, creds, samdb, invocation_id,
|
||||
old_base_dn, new_base_dn):
|
||||
super(drs_ReplicateRenamer, self).__init__(binding_string, lp, creds,
|
||||
samdb, invocation_id)
|
||||
self.old_base_dn = old_base_dn
|
||||
self.new_base_dn = new_base_dn
|
||||
|
||||
def rename_dn(self, dn_str):
|
||||
'''Uses string substitution to replace the base DN'''
|
||||
return re.sub('%s$' % self.old_base_dn, self.new_base_dn, dn_str)
|
||||
|
||||
def update_name_attr(self, base_obj):
|
||||
'''Updates the 'name' attribute for the base DN object'''
|
||||
for attr in base_obj.attribute_ctr.attributes:
|
||||
if attr.attid == DRSUAPI_ATTID_name:
|
||||
base_dn = ldb.Dn(self.samdb, base_obj.identifier.dn)
|
||||
new_name = base_dn.get_rdn_value()
|
||||
attr.value_ctr.values[0].blob = new_name.encode('utf-16-le')
|
||||
|
||||
def rename_top_level_object(self, first_obj):
|
||||
'''Renames the first/top-level object in a partition'''
|
||||
old_dn = first_obj.identifier.dn
|
||||
first_obj.identifier.dn = self.rename_dn(first_obj.identifier.dn)
|
||||
print("Renaming partition %s --> %s" % (old_dn,
|
||||
first_obj.identifier.dn))
|
||||
|
||||
# we also need to fix up the 'name' attribute for the base DN,
|
||||
# otherwise the RDNs won't match
|
||||
if first_obj.identifier.dn == self.new_base_dn:
|
||||
self.update_name_attr(first_obj)
|
||||
|
||||
def process_chunk(self, level, ctr, schema, req_level, req, first_chunk):
|
||||
'''Processes a single chunk of received replication data'''
|
||||
|
||||
# we need to rename the NC in every chunk - this gets used in searches
|
||||
# when applying the chunk
|
||||
if ctr.naming_context:
|
||||
ctr.naming_context.dn = self.rename_dn(ctr.naming_context.dn)
|
||||
|
||||
# rename the first object in each partition. This will cause every
|
||||
# subsequent object in the partiton to be renamed as a side-effect
|
||||
if first_chunk and ctr.object_count != 0:
|
||||
self.rename_top_level_object(ctr.first_object.object)
|
||||
|
||||
# then do the normal repl processing to apply this chunk to our DB
|
||||
super(drs_ReplicateRenamer, self).process_chunk(level, ctr, schema,
|
||||
req_level, req,
|
||||
first_chunk)
|
||||
|
Loading…
Reference in New Issue
Block a user