s4-fsmo: nicer error messages on failed FSMO transfers
[samba.git] / source4 / scripting / python / samba / netcmd / fsmo.py
1 #!/usr/bin/env python
2 #
3 # Changes a FSMO role owner
4 #
5 # Copyright Nadezhda Ivanova 2009
6 # Copyright Jelmer Vernooij 2009
7 #
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21
22 import samba.getopt as options
23 import ldb
24 from ldb import LdbError
25
26 from samba.auth import system_session
27 from samba.netcmd import (
28     Command,
29     CommandError,
30     Option,
31     )
32 from samba.samdb import SamDB
33
34 class cmd_fsmo(Command):
35     """Makes the targer DC transfer or seize a fsmo role [server connection needed]"""
36
37     synopsis = "(show | transfer <options> | seize <options>)"
38
39     takes_optiongroups = {
40         "sambaopts": options.SambaOptions,
41         "credopts": options.CredentialsOptions,
42         "versionopts": options.VersionOptions,
43         }
44
45     takes_options = [
46         Option("--url", help="LDB URL for database or target server", type=str),
47         Option("--force", help="Force seizing of the role without attempting to transfer first.", action="store_true"),
48         Option("--role", type="choice", choices=["rid", "pdc", "infrastructure","schema","naming","all"],
49                help="""The FSMO role to seize or transfer.\n
50 rid=RidAllocationMasterRole\n
51 schema=SchemaMasterRole\n
52 pdc=PdcEmulationMasterRole\n
53 naming=DomainNamingMasterRole\n
54 infrastructure=InfrastructureMasterRole\n
55 all=all of the above"""),
56         ]
57
58     takes_args = ["subcommand"]
59
60     def transfer_role(self, role, samdb):
61         m = ldb.Message()
62         m.dn = ldb.Dn(samdb, "")
63         if role == "rid":
64             m["becomeRidMaster"]= ldb.MessageElement(
65                 "1", ldb.FLAG_MOD_REPLACE,
66                 "becomeRidMaster")
67         elif role == "pdc":
68             domain_dn = samdb.domain_dn()
69             res = samdb.search(domain_dn,
70                                scope=ldb.SCOPE_BASE, attrs=["objectSid"])
71             assert len(res) == 1
72             sid = res[0]["objectSid"][0]
73             m["becomePdc"]= ldb.MessageElement(
74                 sid, ldb.FLAG_MOD_REPLACE,
75                 "becomePdc")
76         elif role == "naming":
77             m["becomeDomainMaster"]= ldb.MessageElement(
78                 "1", ldb.FLAG_MOD_REPLACE,
79                 "becomeDomainMaster")
80             samdb.modify(m)
81         elif role == "infrastructure":
82             m["becomeInfrastructureMaster"]= ldb.MessageElement(
83                 "1", ldb.FLAG_MOD_REPLACE,
84                 "becomeInfrastructureMaster")
85         elif role == "schema":
86             m["becomeSchemaMaster"]= ldb.MessageElement(
87                 "1", ldb.FLAG_MOD_REPLACE,
88                 "becomeSchemaMaster")
89         else:
90             raise CommandError("Invalid FSMO role.")
91         try:
92             samdb.modify(m)
93         except LdbError, (num, msg):
94             raise CommandError("Failed to initiate transfer: %s" % msg)
95         print("Scheduled FSMO transfer - use 'fsmo show' and 'drs showrepl' for result")
96
97
98     def seize_role(self, role, samdb, force):
99         res = samdb.search("",
100                            scope=ldb.SCOPE_BASE, attrs=["dsServiceName"])
101         assert len(res) == 1
102         serviceName = res[0]["dsServiceName"][0]
103         domain_dn = samdb.domain_dn()
104         m = ldb.Message()
105         if role == "rid":
106             m.dn = ldb.Dn(samdb, self.rid_dn)
107         elif role == "pdc":
108             m.dn = ldb.Dn(samdb, domain_dn)
109         elif role == "naming":
110             m.dn = ldb.Dn(samdb, self.naming_dn)
111         elif role == "infrastructure":
112             m.dn = ldb.Dn(samdb, self.infrastructure_dn)
113         elif role == "schema":
114             m.dn = ldb.Dn(samdb, self.schema_dn)
115         else:
116             raise CommandError("Invalid FSMO role.")
117         #first try to transfer to avoid problem if the owner is still active
118         if force is None:
119             self.message("Attempting transfer...")
120             try:
121                 self.transfer_role(role, samdb)
122             except LdbError, (num, _):
123             #transfer failed, use the big axe...
124                 self.message("Transfer unsuccessful, seizing...")
125                 m["fSMORoleOwner"]= ldb.MessageElement(
126                     serviceName, ldb.FLAG_MOD_REPLACE,
127                     "fSMORoleOwner")
128         else:
129             self.message("Will not attempt transfer, seizing...")
130             m["fSMORoleOwner"]= ldb.MessageElement(
131                 serviceName, ldb.FLAG_MOD_REPLACE,
132                 "fSMORoleOwner")
133         try:
134             samdb.modify(m)
135         except LdbError, (num, msg):
136             raise CommandError("Failed to initiate role seize: %s" % msg)
137         print("Scheduled FSMO transfer - use 'fsmo show' and 'drs showrepl' for result")
138
139
140     def run(self, subcommand, force=None, url=None, role=None,
141             credopts=None, sambaopts=None, versionopts=None):
142         lp = sambaopts.get_loadparm()
143         creds = credopts.get_credentials(lp, fallback_machine=True)
144
145         samdb = SamDB(url=url, session_info=system_session(),
146             credentials=creds, lp=lp)
147
148         domain_dn = samdb.domain_dn()
149         self.infrastructure_dn = "CN=Infrastructure," + domain_dn
150         self.naming_dn = "CN=Partitions,CN=Configuration," + domain_dn
151         self.schema_dn = "CN=Schema,CN=Configuration," + domain_dn
152         self.rid_dn = "CN=RID Manager$,CN=System," + domain_dn
153
154         res = samdb.search(self.infrastructure_dn,
155                            scope=ldb.SCOPE_BASE, attrs=["fSMORoleOwner"])
156         assert len(res) == 1
157         self.infrastructureMaster = res[0]["fSMORoleOwner"][0]
158
159         res = samdb.search(domain_dn,
160                            scope=ldb.SCOPE_BASE, attrs=["fSMORoleOwner"])
161         assert len(res) == 1
162         self.pdcEmulator = res[0]["fSMORoleOwner"][0]
163
164         res = samdb.search(self.naming_dn,
165                            scope=ldb.SCOPE_BASE, attrs=["fSMORoleOwner"])
166         assert len(res) == 1
167         self.namingMaster = res[0]["fSMORoleOwner"][0]
168
169         res = samdb.search(self.schema_dn,
170                            scope=ldb.SCOPE_BASE, attrs=["fSMORoleOwner"])
171         assert len(res) == 1
172         self.schemaMaster = res[0]["fSMORoleOwner"][0]
173
174         res = samdb.search(self.rid_dn,
175                            scope=ldb.SCOPE_BASE, attrs=["fSMORoleOwner"])
176         assert len(res) == 1
177         self.ridMaster = res[0]["fSMORoleOwner"][0]
178
179         if subcommand == "show":
180             self.message("InfrastructureMasterRole owner: " + self.infrastructureMaster)
181             self.message("RidAllocationMasterRole owner: " + self.ridMaster)
182             self.message("PdcEmulationMasterRole owner: " + self.pdcEmulator)
183             self.message("DomainNamingMasterRole owner: " + self.namingMaster)
184             self.message("SchemaMasterRole owner: " + self.schemaMaster)
185         elif subcommand == "transfer":
186             if role == "all":
187                 self.transfer_role("rid", samdb)
188                 self.transfer_role("pdc", samdb)
189                 self.transfer_role("naming", samdb)
190                 self.transfer_role("infrastructure", samdb)
191                 self.transfer_role("schema", samdb)
192             else:
193                 self.transfer_role(role, samdb)
194         elif subcommand == "seize":
195             if role == "all":
196                 self.seize_role("rid", samdb, force)
197                 self.seize_role("pdc", samdb, force)
198                 self.seize_role("naming", samdb, force)
199                 self.seize_role("infrastructure", samdb, force)
200                 self.seize_role("schema", samdb, force)
201             else:
202                 self.seize_role(role, samdb, force)
203         else:
204             raise CommandError("Wrong argument '%s'!" % subcommand)