s4: Improve updateprovision
authorMatthieu Patou <mat@matws.net>
Wed, 25 Nov 2009 13:26:35 +0000 (16:26 +0300)
committerAndrew Tridgell <tridge@samba.org>
Fri, 27 Nov 2009 05:05:04 +0000 (16:05 +1100)
* Define a simple upgrade process mode (module storage change, file name change, copy of new file)
  * Move the schema, configuration and current object upgrade into full upgrade mode
  * Added the --full switch to select the full upgrade mode, and made simple upgrade mode the default
  * Make updateprovision works without any switch (update the provision in the default location)
  * Cleanup the messages
  * Create the reference provision in a subdirectory of the updated provision

source4/scripting/bin/upgradeprovision

index 054b47343868115fb19ea565ce8545c86fa3b410..74d9829376acbcf48a24b51644b48d54620ebd1b 100755 (executable)
@@ -53,10 +53,14 @@ replace=2^ldb.FLAG_MOD_REPLACE
 add=2^ldb.FLAG_MOD_ADD
 delete=2^ldb.FLAG_MOD_DELETE
 
-CHANGE = 0x01
-CHANGESD = 0x02
-GUESS = 0x04
-CHANGEALL = 0xff
+#Errors are always logged
+ERROR =        -1
+SIMPLE =       0x00
+CHANGE =       0x01
+CHANGESD =     0x02
+GUESS =        0x04
+PROVISION =    0x08
+CHANGEALL =    0xff
 
 # Attributes that not copied from the reference provision even if they do not exists in the destination object
 # This is most probably because they are populated automatcally when object is created
@@ -82,12 +86,13 @@ def define_what_to_log(opts):
                what = what | CHANGESD
        if opts.debugguess:
                what = what | GUESS
+       if opts.debugprovision:
+               what = what | PROVISION
        if opts.debugall:
                what = what | CHANGEALL
        return what
 
 
-
 parser = optparse.OptionParser("provision [options]")
 sambaopts = options.SambaOptions(parser)
 parser.add_option_group(sambaopts)
@@ -101,6 +106,7 @@ parser.add_option("--debugguess", help="Print information on what is different b
 parser.add_option("--debugchange", help="Print information on what is different but won't be changed", action="store_true")
 parser.add_option("--debugchangesd", help="Print information security descriptors differences", action="store_true")
 parser.add_option("--debugall", help="Print all available information (very verbose)", action="store_true")
+parser.add_option("--full", help="Perform full upgrade of the samdb (schema, configuration, new objects, ...", action="store_true")
 parser.add_option("--targetdir", type="string", metavar="DIR",
                                        help="Set target directory")
 
@@ -115,7 +121,7 @@ def messageprovision(text):
 
 def message(what,text):
        """print a message if quiet is not set."""
-       if whatToLog & what:
+       if (whatToLog & what) or (what <= 0 ):
                print text
 
 if len(sys.argv) == 1:
@@ -146,7 +152,7 @@ def get_paths(targetdir=None,smbconf=None):
                        smbconf = param.default_path()
 
        if not os.path.exists(smbconf):
-               print >>sys.stderr, "Unable to find smb.conf .."
+               message(ERROR,"Unable to find smb.conf ..")
                parser.print_usage()
                sys.exit(1)
 
@@ -179,7 +185,7 @@ def guess_names_from_current_provision(credentials,session_info,paths):
        names.smbconf = smbconf
        #It's important here to let ldb load with the old module or it's quite certain that the LDB won't load ...
        samdb = Ldb(paths.samdb, session_info=session_info,
-                   credentials=credentials, lp=lp)
+                   credentials=credentials, lp=lp, options=["modules:samba_dsdb"])
 
        # That's a bit simplistic but it's ok as long as we have only 3 partitions
        attrs2 = ["schemaNamingContext","configurationNamingContext","rootDomainNamingContext"]
@@ -189,7 +195,7 @@ def guess_names_from_current_provision(credentials,session_info,paths):
        configdn = str(names.configdn)
        names.schemadn = res2[0]["schemaNamingContext"]
        if not (rootdn == str(res2[0]["rootDomainNamingContext"])):
-               print >>sys.stderr, "rootdn in sam.ldb and smb.conf is not the same ..."
+               message(ERROR, "rootdn in sam.ldb and smb.conf is not the same ...")
        else:
                names.rootdn=res2[0]["rootDomainNamingContext"]
        # default site name
@@ -234,7 +240,6 @@ def guess_names_from_current_provision(credentials,session_info,paths):
        # ntds guid
        attrs9 = ["objectGUID" ]
        exp = "(dn=CN=NTDS Settings,%s)"%(names.serverdn)
-       print exp
        res9 = samdb.search(expression="(dn=CN=NTDS Settings,%s)"%(names.serverdn),base=str(names.configdn), scope=SCOPE_SUBTREE, attrs=attrs9)
        names.ntdsguid = str(ndr_unpack( misc.GUID,res9[0]["objectGUID"][0]))
 
@@ -264,12 +269,18 @@ def print_names(names):
 # This provision will be the reference for knowing what has changed in the
 # since the latest upgrade in the current provision
 def newprovision(names,setup_dir,creds,session,smbconf):
-       random.seed()
-       provdir=os.path.join(os.environ["HOME"],"provision"+str(int(100000*random.random())))
+       message(SIMPLE, "Creating a reference provision")
+       provdir=os.path.join(paths.private_dir,"referenceprovision")
+       if os.path.isdir(provdir):
+               rmall(provdir)
        logstd=os.path.join(provdir,"log.std")
        os.chdir(os.path.join(setup_dir,".."))
        os.mkdir(provdir)
-
+       os.close(2)
+       sys.stderr = open("%s/provision.log"%provdir, "w")
+       message(PROVISION, "Reference provision stored in %s"%provdir)
+       message(PROVISION, "STDERR message of provision will be logged in %s/provision.log"%provdir)
+       sys.stderr = open("/dev/stdout", "w")
        provision(setup_dir, messageprovision,
                session, creds, smbconf=smbconf, targetdir=provdir,
                samdb_fill=FILL_FULL, realm=names.realm, domain=names.domain,
@@ -289,7 +300,6 @@ def newprovision(names,setup_dir,creds,session,smbconf):
                setup_ds_path=None,
                nosync=None,
                ldap_dryrun_mode=None)
-       print >>sys.stderr, "provisiondir: "+provdir
        return provdir
 
 # This function sorts two dn in the lexicographical order and put higher level DN before
@@ -317,7 +327,7 @@ def dn_sort(x,y):
                else:
                        if(i==min-1):
                                if(len1==len2):
-                                       print >>sys.stderr,"PB PB PB"+space.join(tab1)+" / "+space.join(tab2)
+                                       message(ERROR,"PB PB PB"+space.join(tab1)+" / "+space.join(tab2))
                                if(len1>len2):
                                        return 1
                                else:
@@ -390,6 +400,7 @@ def handle_special_case(att,delta,new,old,ischema):
        return 0
 
 def update_secrets(newpaths,paths,creds,session):
+       message(SIMPLE,"update secrets.ldb")
        newsecrets_ldb = Ldb(newpaths.secrets, session_info=session, credentials=creds,lp=lp)
        secrets_ldb = Ldb(paths.secrets, session_info=session, credentials=creds,lp=lp, options=["modules:samba_secrets"])
        res = newsecrets_ldb.search(expression="dn=@MODULES",base="", scope=SCOPE_SUBTREE)
@@ -505,7 +516,7 @@ def check_diff_name(newpaths,paths,creds,session,basedn,names,ischema):
                sam_ldb.transaction_start()
 
        empty = ldb.Message()
-       print "There are %d missing objects"%(len(listMissing))
+       message(SIMPLE,"There are %d missing objects"%(len(listMissing)))
        for dn in listMissing:
                res = newsam_ldb.search(expression="dn=%s"%(str(dn)),base=basedn, scope=SCOPE_SUBTREE,controls=["search_options:1:2"])
                delta = sam_ldb.msg_diff(empty,res[0])
@@ -555,7 +566,7 @@ def check_diff_name(newpaths,paths,creds,session,basedn,names,ischema):
                        sam_ldb.modify(delta)
 
        sam_ldb.transaction_commit()
-       print "There are %d changed objects"%(changed)
+       message(SIMPLE,"There are %d changed objects"%(changed))
        return hashallSD
 
 
@@ -594,9 +605,9 @@ def update_sds(diffDefSD,diffSD,paths,creds,session,rootdn,domSIDTxt):
                        session = system_session_info
                descr = security.descriptor.ntsd_from_defaultsd(defSD, domSID,session)
                if descr.as_sddl(domSID) != oldSD:
-                       print "nTSecurity Descriptor for %s do not directly inherit from the defaultSecurityDescriptor and is different from the one of the reference provision, therefor I can't upgrade i"
-                       print "Old Descriptor: %s"%(oldSD)
-                       print "New Descriptor: %s"%(newSD)
+                       message(SIMPLE, "nTSecurity Descriptor for %s do not directly inherit from the defaultSecurityDescriptor and is different from the one of the reference provision, therefor I can't upgrade i")
+                       message(SIMPLE,"Old Descriptor: %s"%(oldSD))
+                       message(SIMPLE,"New Descriptor: %s"%(newSD))
                        if diffDefSD.has_key(classObj):
                                # We have a pending modification for the defaultSecurityDescriptor of the class Object of the currently inspected object
                                # and we have a conflict so write down that we won't upgrade this defaultSD for this class object
@@ -611,7 +622,7 @@ def update_sds(diffDefSD,diffSD,paths,creds,session,rootdn,domSIDTxt):
                sam_ldb.modify(delta)
 
        sam_ldb.transaction_commit()
-       print "%d nTSecurityDescriptor attribute(s) have been updated"%(upgrade)
+       message(SIMPLE,"%d nTSecurityDescriptor attribute(s) have been updated"%(upgrade))
        sam_ldb.transaction_start()
        upgrade = 0
        for dn in diffDefSD:
@@ -626,7 +637,7 @@ def update_sds(diffDefSD,diffSD,paths,creds,session,rootdn,domSIDTxt):
                        message(CHANGESD,"Not updating the defaultSecurityDescriptor for class object %s as one or more dependant object hasn't been upgraded"%(dn))
 
        sam_ldb.transaction_commit()
-       print "%d defaultSecurityDescriptor attribute(s) have been updated"%(upgrade)
+       message(SIMPLE,"%d defaultSecurityDescriptor attribute(s) have been updated"%(upgrade))
 
 def rmall(topdir):
        for root, dirs, files in os.walk(topdir, topdown=False):
@@ -636,13 +647,12 @@ def rmall(topdir):
                        os.rmdir(os.path.join(root, name))
        os.rmdir(topdir)
 
-# For each partition check the differences
 
-def check_diff(newpaths,paths,creds,session,names):
-       print "Copy samdb"
+def update_basesamdb(newpaths,paths,names):
+       message(SIMPLE,"Copy samdb")
        shutil.copy(newpaths.samdb,paths.samdb)
 
-       print "Update ldb names if needed"
+       message(SIMPLE,"Update partitions filename if needed")
        schemaldb=os.path.join(paths.private_dir,"schema.ldb")
        configldb=os.path.join(paths.private_dir,"configuration.ldb")
        usersldb=os.path.join(paths.private_dir,"users.ldb")
@@ -660,16 +670,20 @@ def check_diff(newpaths,paths,creds,session,names):
        if os.path.isfile(configldb):
                shutil.copy(configldb,os.path.join(samldbdir,"%s.ldb"%str(names.configdn).upper()))
                os.remove(configldb)
+
+def update_privilege(newpaths,paths):
+       message(SIMPLE,"Copy privilege")
        shutil.copy(os.path.join(newpaths.private_dir,"privilege.ldb"),os.path.join(paths.private_dir,"privilege.ldb"))
 
-       print "Doing schema update"
+# For each partition check the differences
+def update_samdb(newpaths,paths,creds,session,names):
+
+       message(SIMPLE, "Doing schema update")
        hashdef = check_diff_name(newpaths,paths,creds,session,str(names.schemadn),names,1)
-       print "Done with schema update"
-       print "Scanning whole provision for updates and additions"
+       message(SIMPLE,"Done with schema update")
+       message(SIMPLE,"Scanning whole provision for updates and additions")
        hashSD = check_diff_name(newpaths,paths,creds,session,str(names.rootdn),names,0)
-       print "Done with scanning"
-       print "Updating secrets"
-       update_secrets(newpaths,paths,creds,session)
+       message(SIMPLE,"Done with scanning")
 #      update_sds(hashdef,hashSD,paths,creds,session,str(names.rootdn),names.domainsid)
 
 # From here start the big steps of the program
@@ -689,6 +703,11 @@ provisiondir = newprovision(names,setup_dir,creds,session,smbconf)
 newpaths = get_paths(targetdir=provisiondir)
 populate_backlink(newpaths,creds,session,names.schemadn)
 # Check the difference
-check_diff(newpaths,paths,creds,session,names)
+update_basesamdb(newpaths,paths,names)
+update_secrets(newpaths,paths,creds,session)
+update_privilege(newpaths,paths)
+if opts.full:
+       update_samdb(newpaths,paths,creds,session,names)
+message(SIMPLE,"Upgrade finished !")
 # remove reference provision now that everything is done !
 rmall(provisiondir)