mirror of
https://github.com/samba-team/samba.git
synced 2025-08-03 04:22:09 +03:00
netcmd: Extend 'backup restore' command to handle renamed domains
When restoring a renamed domain backup, we need to register the new realm's DNS zone. We do this in the restore step because we don't know the new server's IP/hostname in the backup step. Because we may have removed the old realm's DNS entries in the rename step, the remove_dc() code may fail to find the expected DNS entries for the DC's domain (the DCs' dnsHostname still maps to the old DNS realm). We just needed to adjust remove_dns_references() as it was getting a slightly different error code. Signed-off-by: Tim Beale <timbeale@catalyst.net.nz> Reviewed-by: Gary Lockyer <gary@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
committed by
Douglas Bagnall
parent
ab65647a8b
commit
6681f904aa
@ -41,7 +41,10 @@ from samba.remove_dc import remove_dc
|
|||||||
from samba.provision import secretsdb_self_join
|
from samba.provision import secretsdb_self_join
|
||||||
from samba.dbchecker import dbcheck
|
from samba.dbchecker import dbcheck
|
||||||
import re
|
import re
|
||||||
|
from samba.provision import guess_names, determine_host_ip, determine_host_ip6
|
||||||
|
from samba.provision.sambadns import (fill_dns_data_partitions,
|
||||||
|
get_dnsadmins_sid,
|
||||||
|
get_domainguid)
|
||||||
|
|
||||||
|
|
||||||
# work out a SID (based on a free RID) to use when the domain gets restored.
|
# work out a SID (based on a free RID) to use when the domain gets restored.
|
||||||
@ -235,6 +238,10 @@ class cmd_domain_backup_restore(cmd_fsmo_seize):
|
|||||||
Option("--backup-file", help="Path to backup file", type=str),
|
Option("--backup-file", help="Path to backup file", type=str),
|
||||||
Option("--targetdir", help="Path to write to", type=str),
|
Option("--targetdir", help="Path to write to", type=str),
|
||||||
Option("--newservername", help="Name for new server", type=str),
|
Option("--newservername", help="Name for new server", type=str),
|
||||||
|
Option("--host-ip", type="string", metavar="IPADDRESS",
|
||||||
|
help="set IPv4 ipaddress"),
|
||||||
|
Option("--host-ip6", type="string", metavar="IP6ADDRESS",
|
||||||
|
help="set IPv6 ipaddress"),
|
||||||
]
|
]
|
||||||
|
|
||||||
takes_optiongroups = {
|
takes_optiongroups = {
|
||||||
@ -242,8 +249,41 @@ class cmd_domain_backup_restore(cmd_fsmo_seize):
|
|||||||
"credopts": options.CredentialsOptions,
|
"credopts": options.CredentialsOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def register_dns_zone(self, logger, samdb, lp, ntdsguid, host_ip,
|
||||||
|
host_ip6):
|
||||||
|
'''
|
||||||
|
Registers the new realm's DNS objects when a renamed domain backup
|
||||||
|
is restored.
|
||||||
|
'''
|
||||||
|
names = guess_names(lp)
|
||||||
|
domaindn = names.domaindn
|
||||||
|
forestdn = samdb.get_root_basedn().get_linearized()
|
||||||
|
dnsdomain = names.dnsdomain.lower()
|
||||||
|
dnsforest = dnsdomain
|
||||||
|
hostname = names.netbiosname.lower()
|
||||||
|
domainsid = dom_sid(samdb.get_domain_sid())
|
||||||
|
dnsadmins_sid = get_dnsadmins_sid(samdb, domaindn)
|
||||||
|
domainguid = get_domainguid(samdb, domaindn)
|
||||||
|
|
||||||
|
# work out the IP address to use for the new DC's DNS records
|
||||||
|
host_ip = determine_host_ip(logger, lp, host_ip)
|
||||||
|
host_ip6 = determine_host_ip6(logger, lp, host_ip6)
|
||||||
|
|
||||||
|
if host_ip is None and host_ip6 is None:
|
||||||
|
raise CommandError('Please specify a host-ip for the new server')
|
||||||
|
|
||||||
|
logger.info("DNS realm was renamed to %s" % dnsdomain)
|
||||||
|
logger.info("Populating DNS partitions for new realm...")
|
||||||
|
|
||||||
|
# Add the DNS objects for the new realm (note: the backup clone already
|
||||||
|
# has the root server objects, so don't add them again)
|
||||||
|
fill_dns_data_partitions(samdb, domainsid, names.sitename, domaindn,
|
||||||
|
forestdn, dnsdomain, dnsforest, hostname,
|
||||||
|
host_ip, host_ip6, domainguid, ntdsguid,
|
||||||
|
dnsadmins_sid, add_root=False)
|
||||||
|
|
||||||
def run(self, sambaopts=None, credopts=None, backup_file=None,
|
def run(self, sambaopts=None, credopts=None, backup_file=None,
|
||||||
targetdir=None, newservername=None):
|
targetdir=None, newservername=None, host_ip=None, host_ip6=None):
|
||||||
if not (backup_file and os.path.exists(backup_file)):
|
if not (backup_file and os.path.exists(backup_file)):
|
||||||
raise CommandError('Backup file not found.')
|
raise CommandError('Backup file not found.')
|
||||||
if targetdir is None:
|
if targetdir is None:
|
||||||
@ -314,7 +354,8 @@ class cmd_domain_backup_restore(cmd_fsmo_seize):
|
|||||||
# Get the SID saved by the backup process and create account
|
# Get the SID saved by the backup process and create account
|
||||||
res = samdb.search(base=ldb.Dn(samdb, "@SAMBA_DSDB"),
|
res = samdb.search(base=ldb.Dn(samdb, "@SAMBA_DSDB"),
|
||||||
scope=ldb.SCOPE_BASE,
|
scope=ldb.SCOPE_BASE,
|
||||||
attrs=['sidForRestore'])
|
attrs=['sidForRestore', 'backupRename'])
|
||||||
|
is_rename = True if 'backupRename' in res[0] else False
|
||||||
sid = res[0].get('sidForRestore')[0]
|
sid = res[0].get('sidForRestore')[0]
|
||||||
logger.info('Creating account with SID: ' + str(sid))
|
logger.info('Creating account with SID: ' + str(sid))
|
||||||
ctx.join_add_objects(specified_sid=dom_sid(sid))
|
ctx.join_add_objects(specified_sid=dom_sid(sid))
|
||||||
@ -327,6 +368,13 @@ class cmd_domain_backup_restore(cmd_fsmo_seize):
|
|||||||
"dsServiceName")
|
"dsServiceName")
|
||||||
samdb.modify(m)
|
samdb.modify(m)
|
||||||
|
|
||||||
|
# if we renamed the backed-up domain, then we need to add the DNS
|
||||||
|
# objects for the new realm (we do this in the restore, now that we
|
||||||
|
# know the new DC's IP address)
|
||||||
|
if is_rename:
|
||||||
|
self.register_dns_zone(logger, samdb, lp, ctx.ntds_guid,
|
||||||
|
host_ip, host_ip6)
|
||||||
|
|
||||||
secrets_path = os.path.join(private_dir, 'secrets.ldb')
|
secrets_path = os.path.join(private_dir, 'secrets.ldb')
|
||||||
secrets_ldb = Ldb(secrets_path, session_info=system_session(), lp=lp)
|
secrets_ldb = Ldb(secrets_path, session_info=system_session(), lp=lp)
|
||||||
secretsdb_self_join(secrets_ldb, domain=ctx.domain_name,
|
secretsdb_self_join(secrets_ldb, domain=ctx.domain_name,
|
||||||
@ -401,6 +449,9 @@ class cmd_domain_backup_restore(cmd_fsmo_seize):
|
|||||||
"backupDate")
|
"backupDate")
|
||||||
m["sidForRestore"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE,
|
m["sidForRestore"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE,
|
||||||
"sidForRestore")
|
"sidForRestore")
|
||||||
|
if is_rename:
|
||||||
|
m["backupRename"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE,
|
||||||
|
"backupRename")
|
||||||
samdb.modify(m)
|
samdb.modify(m)
|
||||||
|
|
||||||
logger.info("Backup file successfully restored to %s" % targetdir)
|
logger.info("Backup file successfully restored to %s" % targetdir)
|
||||||
|
@ -1039,7 +1039,7 @@ def create_dns_partitions(samdb, domainsid, names, domaindn, forestdn,
|
|||||||
def fill_dns_data_partitions(samdb, domainsid, site, domaindn, forestdn,
|
def fill_dns_data_partitions(samdb, domainsid, site, domaindn, forestdn,
|
||||||
dnsdomain, dnsforest, hostname, hostip, hostip6,
|
dnsdomain, dnsforest, hostname, hostip, hostip6,
|
||||||
domainguid, ntdsguid, dnsadmins_sid, autofill=True,
|
domainguid, ntdsguid, dnsadmins_sid, autofill=True,
|
||||||
fill_level=FILL_FULL):
|
fill_level=FILL_FULL, add_root=True):
|
||||||
"""Fill data in various AD partitions
|
"""Fill data in various AD partitions
|
||||||
|
|
||||||
:param samdb: LDB object connected to sam.ldb file
|
:param samdb: LDB object connected to sam.ldb file
|
||||||
@ -1060,6 +1060,7 @@ def fill_dns_data_partitions(samdb, domainsid, site, domaindn, forestdn,
|
|||||||
|
|
||||||
##### Set up DC=DomainDnsZones,<DOMAINDN>
|
##### Set up DC=DomainDnsZones,<DOMAINDN>
|
||||||
# Add rootserver records
|
# Add rootserver records
|
||||||
|
if add_root:
|
||||||
add_rootservers(samdb, domaindn, "DC=DomainDnsZones")
|
add_rootservers(samdb, domaindn, "DC=DomainDnsZones")
|
||||||
|
|
||||||
# Add domain record
|
# Add domain record
|
||||||
|
@ -102,7 +102,8 @@ def remove_dns_references(samdb, logger, dnsHostName, ignore_no_name=False):
|
|||||||
(dn, primary_recs) = samdb.dns_lookup(dnsHostName)
|
(dn, primary_recs) = samdb.dns_lookup(dnsHostName)
|
||||||
except RuntimeError as e4:
|
except RuntimeError as e4:
|
||||||
(enum, estr) = e4.args
|
(enum, estr) = e4.args
|
||||||
if enum == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST:
|
if (enum == werror.WERR_DNS_ERROR_NAME_DOES_NOT_EXIST or
|
||||||
|
enum == werror.WERR_DNS_ERROR_RCODE_NAME_ERROR):
|
||||||
if ignore_no_name:
|
if ignore_no_name:
|
||||||
remove_hanging_dns_references(samdb, logger,
|
remove_hanging_dns_references(samdb, logger,
|
||||||
dnsHostNameUpper,
|
dnsHostNameUpper,
|
||||||
|
Reference in New Issue
Block a user