[PATCH] samba-tool throws error if there is an empty FSMO role

Jeremy Allison jra at samba.org
Tue Apr 5 18:18:15 UTC 2016


On Thu, Mar 31, 2016 at 01:23:43PM +0100, Rowland Penny wrote:
> 
> Hi, if there are any empty FSMO roles in AD, 'samba-tool fsmo'
> throws an error, this happens if you try to show or transfer an
> empty role.
> 
> The attached patch will cure this, if you run 'samba-tool fsmo show'
> any empty roles will be shown similar to this:
> 
>  DomainDnsZonesMasterRole owner: * The 'domaindns' role does not
> have an FSMO roleowner
> 
> If you try to transfer an empty role, rather than throwing an error,
> it will now tell you the role as no owner i.e.
> 
> samba-tool fsmo transfer --role=pdc
> * The 'pdc' role does not have an FSMO roleowner
> 
> You will then be able to seize the role.

Ping. Can we get a second Team reviewer for Rowland for this ?

My python is too weak to do a good job here, so someone with
more python experience would be appreciated.

Cheers,

	Jeremy.

> From 74d677fbaa6ae882558d50b43914c6ef4e51f276 Mon Sep 17 00:00:00 2001
> From: Rowland Penny <rpenny at samba.org>
> Date: Thu, 31 Mar 2016 13:07:00 +0100
> Subject: [PATCH] Stop 'samba-tool fsmo' erroring out if there is an empty
> FSMO role.
> 
> Signed-off-by: Rowland Penny <rpenny at samba.org>
> ---
>  python/samba/netcmd/fsmo.py |  224 ++++++++++++++++++++++++-------------------
>  1 file changed, 128 insertions(+), 96 deletions(-)
> 
> diff --git a/python/samba/netcmd/fsmo.py b/python/samba/netcmd/fsmo.py
> index 3904bcb..42f7f5b 100644
> --- a/python/samba/netcmd/fsmo.py
> +++ b/python/samba/netcmd/fsmo.py
> @@ -31,17 +31,21 @@ from samba.netcmd import (
>      )
>  from samba.samdb import SamDB
>  
> -def get_fsmo_roleowner(samdb, roledn):
> +def get_fsmo_roleowner(samdb, roledn, role):
>      """Gets the owner of an FSMO role
>  
>      :param roledn: The DN of the FSMO role
> +    :param role: The FSMO role
>      """
>      res = samdb.search(roledn,
>                         scope=ldb.SCOPE_BASE, attrs=["fSMORoleOwner"])
> -    if len(res) == 0:
> -        raise CommandError('"%s" does not have a FSMO roleowner' % roledn)
> -    master_owner = res[0]["fSMORoleOwner"][0]
> -    return master_owner
> +
> +    if 'fSMORoleOwner' in res[0]:
> +        master_owner = res[0]["fSMORoleOwner"][0]
> +        return master_owner
> +    else:
> +        master_owner = "* The '%s' role does not have an FSMO roleowner" % role
> +        return master_owner
>  
>  
>  def transfer_dns_role(outf, sambaopts, credopts, role, samdb):
> @@ -54,23 +58,23 @@ def transfer_dns_role(outf, sambaopts, credopts, role, samdb):
>          forest_dn = samba.dn_from_dns_name(samdb.forest_dns_name())
>          role_object = "CN=Infrastructure,DC=ForestDnsZones," + forest_dn
>  
> -    try:
> -        res = samdb.search(role_object,
> -                           attrs=["fSMORoleOwner"],
> -                           scope=ldb.SCOPE_BASE,
> -                           controls=["extended_dn:1:1"])
> +    res = samdb.search(role_object,
> +                       attrs=["fSMORoleOwner"],
> +                       scope=ldb.SCOPE_BASE,
> +                       controls=["extended_dn:1:1"])
>  
> -        if 'fSMORoleOwner' in res[0]:
> -            try:
> -                master_guid = str(misc.GUID(ldb.Dn(samdb,
> -                                  res[0]['fSMORoleOwner'][0])
> -                                  .get_extended_component('GUID')))
> -                master_owner = str(ldb.Dn(samdb, res[0]['fSMORoleOwner'][0]))
> -            except LdbError, (num, msg):
> -                raise CommandError("GUID not found in partition naming master DN %s : %s \n" %
> -                                   (res[0]['fSMORoleOwner'][0], msg))
> -    except LdbError, (num, msg):
> -        raise CommandError("DNS partion %s not found : %s" % (role, msg))
> +    if 'fSMORoleOwner' in res[0]:
> +        try:
> +            master_guid = str(misc.GUID(ldb.Dn(samdb,
> +                              res[0]['fSMORoleOwner'][0])
> +                              .get_extended_component('GUID')))
> +            master_owner = str(ldb.Dn(samdb, res[0]['fSMORoleOwner'][0]))
> +        except LdbError, (num, msg):
> +            raise CommandError("No GUID found in naming master DN %s : %s \n" %
> +                               (res[0]['fSMORoleOwner'][0], msg))
> +    else:
> +        outf.write("* The '%s' role does not have an FSMO roleowner\n" % role)
> +        return False
>  
>      if role == "domaindns":
>          master_dns_name = '%s._msdcs.%s' % (master_guid,
> @@ -150,12 +154,12 @@ def transfer_role(outf, role, samdb):
>      m = ldb.Message()
>      m.dn = ldb.Dn(samdb, "")
>      if role == "rid":
> -        master_owner = get_fsmo_roleowner(samdb, rid_dn)
> +        master_owner = get_fsmo_roleowner(samdb, rid_dn, role)
>          m["becomeRidMaster"]= ldb.MessageElement(
>              "1", ldb.FLAG_MOD_REPLACE,
>              "becomeRidMaster")
>      elif role == "pdc":
> -        master_owner = get_fsmo_roleowner(samdb, domain_dn)
> +        master_owner = get_fsmo_roleowner(samdb, domain_dn, role)
>  
>          res = samdb.search(domain_dn,
>                             scope=ldb.SCOPE_BASE, attrs=["objectSid"])
> @@ -165,34 +169,38 @@ def transfer_role(outf, role, samdb):
>              sid, ldb.FLAG_MOD_REPLACE,
>              "becomePdc")
>      elif role == "naming":
> -        master_owner = get_fsmo_roleowner(samdb, naming_dn)
> +        master_owner = get_fsmo_roleowner(samdb, naming_dn, role)
>          m["becomeDomainMaster"]= ldb.MessageElement(
>              "1", ldb.FLAG_MOD_REPLACE,
>              "becomeDomainMaster")
>      elif role == "infrastructure":
> -        master_owner = get_fsmo_roleowner(samdb, infrastructure_dn)
> +        master_owner = get_fsmo_roleowner(samdb, infrastructure_dn, role)
>          m["becomeInfrastructureMaster"]= ldb.MessageElement(
>              "1", ldb.FLAG_MOD_REPLACE,
>              "becomeInfrastructureMaster")
>      elif role == "schema":
> -        master_owner = get_fsmo_roleowner(samdb, schema_dn)
> +        master_owner = get_fsmo_roleowner(samdb, schema_dn, role)
>          m["becomeSchemaMaster"]= ldb.MessageElement(
>              "1", ldb.FLAG_MOD_REPLACE,
>              "becomeSchemaMaster")
>      else:
>          raise CommandError("Invalid FSMO role.")
>  
> -    if master_owner != new_owner:
> -        try:
> -            samdb.modify(m)
> -        except LdbError, (num, msg):
> -            raise CommandError("Transfer of '%s' role failed: %s" %
> -                               (role, msg))
> +    if not '*' in master_owner:
> +        if master_owner != new_owner:
> +            try:
> +                samdb.modify(m)
> +            except LdbError, (num, msg):
> +                raise CommandError("Transfer of '%s' role failed: %s" %
> +                                   (role, msg))
>  
> -        outf.write("FSMO transfer of '%s' role successful\n" % role)
> -        return True
> +            outf.write("FSMO transfer of '%s' role successful\n" % role)
> +            return True
> +        else:
> +            outf.write("This DC already has the '%s' FSMO role\n" % role)
> +            return False
>      else:
> -        outf.write("This DC already has the '%s' FSMO role\n" % role)
> +        outf.write("%s\n" % master_owner)
>          return False
>  
>  class cmd_fsmo_seize(Command):
> @@ -210,7 +218,7 @@ class cmd_fsmo_seize(Command):
>          Option("-H", "--URL", help="LDB URL for database or target server",
>                 type=str, metavar="URL", dest="H"),
>          Option("--force",
> -               help="Force seizing of the role without attempting to transfer first.",
> +               help="Force seizing of role without attempting to transfer.",
>                 action="store_true"),
>          Option("--role", type="choice", choices=["rid", "pdc", "infrastructure",
>                 "schema", "naming", "domaindns", "forestdns", "all"],
> @@ -253,32 +261,41 @@ You must provide an Admin user and password."""),
>              raise CommandError("Invalid FSMO role.")
>          #first try to transfer to avoid problem if the owner is still active
>          seize = False
> -        master_owner = get_fsmo_roleowner(samdb, m.dn)
> -        if master_owner != serviceName:
> -            if force is None:
> -                self.message("Attempting transfer...")
> -                if not transfer_role(self.outf, role, samdb):
> -                    #transfer failed, use the big axe...
> -                    seize = True
> -                    self.message("Transfer unsuccessful, seizing...")
> -                else:
> -                    self.message("Not seizing role as transfer was successful")
> -
> -            if force is not None or seize == True:
> -                self.message("Seizing %s FSMO role..." % role)
> -                m["fSMORoleOwner"]= ldb.MessageElement(
> -                    serviceName, ldb.FLAG_MOD_REPLACE,
> -                    "fSMORoleOwner")
> -                try:
> -                    samdb.modify(m)
> -                except LdbError, (num, msg):
> -                    raise CommandError("Failed to seize '%s' role: %s" %
> -                                       (role, msg))
> -                self.outf.write("FSMO seize of '%s' role successful\n" % role)
> -                return True
> +        master_owner = get_fsmo_roleowner(samdb, m.dn, role)
> +        if not '*' in master_owner:
> +            # if there is a different owner
> +            if master_owner != serviceName:
> +                # if --force isn't given, attempt transfer
> +                if force is None:
> +                    self.message("Attempting transfer...")
> +                    try:
> +                        transfer_role(self.outf, role, samdb):
> +                    except:
> +                        #transfer failed, use the big axe...
> +                        seize = True
> +                        self.message("Transfer unsuccessful, seizing...")
> +                    else:
> +                        self.message("Transfer successful, not seizing role")
> +                        return True
> +            else:
> +                self.outf.write("This DC already has the '%s' FSMO role\n" %
> +                                role)
> +                return False
>          else:
> -            self.outf.write("This DC already has the '%s' FSMO role\n" % role)
> -            return False
> +            seize = True
> +
> +        if force is not None or seize == True:
> +            self.message("Seizing %s FSMO role..." % role)
> +            m["fSMORoleOwner"]= ldb.MessageElement(
> +                serviceName, ldb.FLAG_MOD_REPLACE,
> +                "fSMORoleOwner")
> +            try:
> +                samdb.modify(m)
> +            except LdbError, (num, msg):
> +                raise CommandError("Failed to seize '%s' role: %s" %
> +                                   (role, msg))
> +            self.outf.write("FSMO seize of '%s' role successful\n" % role)
> +            return True
>  
>      def seize_dns_role(self, role, samdb, credopts, sambaopts,
>                         versionopts, force):
> @@ -299,33 +316,43 @@ You must provide an Admin user and password."""),
>              raise CommandError("Invalid FSMO role.")
>          #first try to transfer to avoid problem if the owner is still active
>          seize = False
> -        master_owner = get_fsmo_roleowner(samdb, m.dn)
> -        if master_owner != serviceName:
> -            if force is None:
> -                self.message("Attempting transfer...")
> -                if not transfer_dns_role(self.outf, sambaopts, credopts, role,
> -                                      samdb):
> -                    #transfer failed, use the big axe...
> -                    seize = True
> -                    self.message("Transfer unsuccessful, seizing...")
> -                else:
> -                    self.message("Not seizing role as transfer was successful\n")
> -
> -            if force is not None or seize == True:
> -                self.message("Seizing %s FSMO role..." % role)
> -                m["fSMORoleOwner"]= ldb.MessageElement(
> -                    serviceName, ldb.FLAG_MOD_REPLACE,
> -                    "fSMORoleOwner")
> -                try:
> -                    samdb.modify(m)
> -                except LdbError, (num, msg):
> -                    raise CommandError("Failed to seize '%s' role: %s" %
> -                                       (role, msg))
> -                self.outf.write("FSMO seize of '%s' role successful\n" % role)
> -                return True
> +        master_owner = get_fsmo_roleowner(samdb, m.dn, role)
> +        if not '*' in master_owner:
> +            # if there is a different owner
> +            if master_owner != serviceName:
> +                # if --force isn't given, attempt transfer
> +                if force is None:
> +                    self.message("Attempting transfer...")
> +                    try:
> +                        transfer_dns_role(self.outf, sambaopts, credopts, role,
> +                                          samdb)
> +                    except:
> +                        #transfer failed, use the big axe...
> +                        seize = True
> +                        self.message("Transfer unsuccessful, seizing...")
> +                    else:
> +                        self.message("Transfer successful, not seizing role\n")
> +                        return True
> +            else:
> +                self.outf.write("This DC already has the '%s' FSMO role\n" %
> +                                role)
> +                return False
>          else:
> -            self.outf.write("This DC already has the '%s' FSMO role\n" % role)
> -            return False
> +            seize = True
> +
> +        if force is not None or seize == True:
> +            self.message("Seizing %s FSMO role..." % role)
> +            m["fSMORoleOwner"]= ldb.MessageElement(
> +                serviceName, ldb.FLAG_MOD_REPLACE,
> +                "fSMORoleOwner")
> +            try:
> +                samdb.modify(m)
> +            except LdbError, (num, msg):
> +                raise CommandError("Failed to seize '%s' role: %s" %
> +                                   (role, msg))
> +            self.outf.write("FSMO seize of '%s' role successful\n" % role)
> +            return True
> +
>  
>      def run(self, force=None, H=None, role=None,
>              credopts=None, sambaopts=None, versionopts=None):
> @@ -388,13 +415,16 @@ class cmd_fsmo_show(Command):
>          domaindns_dn = "CN=Infrastructure,DC=DomainDnsZones," + domain_dn
>          forestdns_dn = "CN=Infrastructure,DC=ForestDnsZones," + forest_dn
>  
> -        infrastructureMaster = get_fsmo_roleowner(samdb, infrastructure_dn)
> -        pdcEmulator = get_fsmo_roleowner(samdb, domain_dn)
> -        namingMaster = get_fsmo_roleowner(samdb, naming_dn)
> -        schemaMaster = get_fsmo_roleowner(samdb, schema_dn)
> -        ridMaster = get_fsmo_roleowner(samdb, rid_dn)
> -        domaindnszonesMaster = get_fsmo_roleowner(samdb, domaindns_dn)
> -        forestdnszonesMaster = get_fsmo_roleowner(samdb, forestdns_dn)
> +        infrastructureMaster = get_fsmo_roleowner(samdb, infrastructure_dn,
> +                                                  "infrastructure")
> +        pdcEmulator = get_fsmo_roleowner(samdb, domain_dn, "pdc")
> +        namingMaster = get_fsmo_roleowner(samdb, naming_dn, "naming")
> +        schemaMaster = get_fsmo_roleowner(samdb, schema_dn, "schema")
> +        ridMaster = get_fsmo_roleowner(samdb, rid_dn, "rid")
> +        domaindnszonesMaster = get_fsmo_roleowner(samdb, domaindns_dn,
> +                                                  "domaindns")
> +        forestdnszonesMaster = get_fsmo_roleowner(samdb, forestdns_dn,
> +                                                  "forestdns")
>  
>          self.message("SchemaMasterRole owner: " + schemaMaster)
>          self.message("InfrastructureMasterRole owner: " + infrastructureMaster)
> @@ -449,8 +479,10 @@ You must provide an Admin user and password."""),
>              transfer_role(self.outf, "naming", samdb)
>              transfer_role(self.outf, "infrastructure", samdb)
>              transfer_role(self.outf, "schema", samdb)
> -            transfer_dns_role(self.outf, sambaopts, credopts, "domaindns", samdb)
> -            transfer_dns_role(self.outf, sambaopts, credopts, "forestdns", samdb)
> +            transfer_dns_role(self.outf, sambaopts, credopts,
> +                              "domaindns", samdb)
> +            transfer_dns_role(self.outf, sambaopts, credopts, "forestdns",
> +                              samdb)
>          else:
>              if role == "domaindns" or role == "forestdns":
>                  transfer_dns_role(self.outf, sambaopts, credopts, role, samdb)
> -- 
> 1.7.10.4
> 




More information about the samba-technical mailing list