#!/usr/bin/python # sim-sync -- sync source trees across machines using information from the machine database # # Michael Thomas # Center for Computation & Technology # Louisiana State University # import sys, os ############################################ ## APP SPECIFIC DEFINES ## # # Usage when (-h, --help) is used, this is the first line showing how to call the app # optionGroups which option groups from etc/options to import. common is always imported. App = "sim-sync" Usage = "usage: %prog [options] machine1 machine2..." optionGroups = ['sim-sync'] Purpose = "sync cactus sourcetree to a remote machine" ############################################ ############################################ ## INIT ## if os.path.basename(sys.argv[0]) == App: rp = os.path.abspath(__file__) paths = rp.split(os.sep) paths.pop() paths.pop() global BASE_PATH BASE_PATH = os.sep.join(paths) sys.path.append(BASE_PATH) LIB_PATH = "%s%slib" % (BASE_PATH, os.sep) sys.path.append(LIB_PATH) from lib import * import libutil from libutil import dprint known_commands = [] usage_strings = {'sync': 'sync cactus sourcetree to a remote machine'} ############################### FUNCTIONS ############################### def GetRsyncInfo(machineEntry): return GetSubbedInfo(machineEntry, ['rsynccmd', 'rsyncopts']) def GetSshInfo(machineEntry): return GetSubbedInfo(machineEntry, ['sshcmd', 'sshopts']) def GetSubbedInfo(machineEntry, keys): global DefineDatabase global SimLib SimLib.VerifyKeys(machineEntry, keys) data = list() for k in keys: d = DefineDatabase.SubAll(machineEntry.GetKey(k)) data.append(d) return data def RemoteExecute(machineName): global SimEnvironment RemoteEnvironment = simremote.RemoteEnvironment(SimEnvironment) RemoteEnvironment.init(machineName) RemoteEnvironment.ExecuteSameCommand(parrotArguments=True, stripArguments=['remotemachine']) def CompileCommand(machineName, rsyncInfo): global DefineDatabase global ConfigurationDatabase global OptionsManager global SimLib global localMachineName global localMachineEntry if OptionsManager.HasOption("remotemachine"): remoteMachine = OptionsManager.GetOption("remotemachine") dprint("Syncing from remote to remote for remote machine: %s" % remoteMachine, True) RemoteExecute(remoteMachine) return # mode of operation sync_parfiles = OptionsManager.GetOption('sync-parfiles') sync_sourcetree = OptionsManager.GetOption('sync-sourcetree') (rsynccmd, rsyncopts) = rsyncInfo if machineName == localMachineName: dprint("Error, cannot sync to local machine", True) sys.exit(1) machineEntry = ConfigurationDatabase.GetMachine(machineName) # get our IO machine -- if iomachine doesn't exist, it returns itself. ioMachine = SimLib.GetIOMachine(machineName) if ioMachine != machineName: ioMachineEntry = ConfigurationDatabase.GetMachine(ioMachine) else: ioMachineEntry = machineEntry mreq_keys = ['user', 'sourcebasedir'] ioreq_keys = ['hostname', 'rsynccmd', 'rsyncopts', 'sshcmd', 'sshopts'] user = machineEntry.user sourcebasedir = machineEntry.sourcebasedir # make sure we have all the info we need. SimLib.VerifyKeys(machineEntry, mreq_keys) SimLib.VerifyKeys(ioMachineEntry, ioreq_keys) DefineDatabase.Set('USER', machineEntry.user) sourcebasedir = DefineDatabase.SubAll(machineEntry.sourcebasedir) iohostname = ioMachineEntry.hostname (iorsynccmd, iorsyncopts) = GetRsyncInfo(ioMachineEntry) (sshcmd, sshopts) = GetSshInfo(ioMachineEntry) sshcmd = "%s %s" % (sshcmd, sshopts) trampoline = SimLib.GetTrampoline(ioMachine) if trampoline != None: sshcmd = SimLib.GetSSHCommand(trampoline, sshcmd) rsyncfiles = [] rsyncexcludes = [] rsyncoptions = ['--archive', '--hard-links', '--sparse', '--verbose', '--progress', '--partial', '--stats', '--compress', "--filter 'protect *.pyc'", "--filter 'protect *.log'"] # sync source tree if sync_sourcetree: sources = ConfigurationDatabase.GetConfigOption('sync-sources') #dprint("sync-sources is: %s " % sources) if type(sources) == list: for r in sources: #dprint("checking if %s exists" % r) if os.path.exists(r): rsyncfiles.append(r) # sync par files if sync_parfiles: sources = ConfigurationDatabase.GetConfigOption('sync-parfiles') #dprint("sync-parfiles is: %s " % sources) if type(sources) == list: for r in sources: #dprint("checking if %s exists" % r) if os.path.exists(r): rsyncfiles.append(r) # build excludes sources = ConfigurationDatabase.GetConfigOption('sync-excludes') if type(sources) == list: for r in sources: r = r.strip() if len(r) > 0: rsyncoptions.append("--exclude '%s'" % r) fullpath = "%s@%s:%s%s%s" % (user, iohostname, sourcebasedir, os.sep, path) sshcmd = SimLib.QuoteSafe(sshcmd) arguments = " ".join(SimLib.GetArguments()) cmd = "%s --rsh=%s --rsync-path=%s %s %s %s" % (rsynccmd, sshcmd, iorsynccmd, rsyncopts, iorsyncopts, arguments) cmd = "%s %s" % (cmd, " ".join(rsyncoptions)) cmd = "%s %s" % (cmd, " ".join(rsyncfiles)) cmd = "%s %s" % (cmd, fullpath) mkdirssh = "%s@%s" % (user, machineEntry.hostname) mkdirpath = "%s%s%s" % (sourcebasedir, os.sep, path) mkdircmd = "ssh -Y %s mkdir -p %s" % (mkdirssh, mkdirpath) if machineEntry.HasKey("localsshsetup"): localsshsetup = DefineDatabase.SubAll(machineEntry.localsshsetup) cmd = "{ %s; } && %s && %s" % (localsshsetup, mkdircmd, cmd) return cmd ############################### MAIN ############################### def main(env): global OptionsManager global ConfigurationDatabase global SimLib global DefineDatabase global SimEnvironment SimEnvironment = env OptionsManager = SimEnvironment.OptionsManager ConfigurationDatabase = SimEnvironment.ConfigurationDatabase SimLib = SimEnvironment.SimLib DefineDatabase = SimEnvironment.DefineDatabase ############################################ ## HEADER ## SimEnvironment.RequireMachine() if os.path.basename(SimEnvironment.EXECUTABLE) == App: dprint("sim-sync: sync sourcetree/parfiles from one machine to another\n", True) dprint("defs: %s" % SimEnvironment.cdb) if SimEnvironment.udb != None: dprint("defs.local: %s" % SimEnvironment.udb) print global path global local_sourcebasedir global localMachineName global localMachineEntry machineList = OptionsManager.args if len(machineList) == 0: dprint("Error: no machines specified", True) OptionsManager.PrintHelp() sys.exit(1) if OptionsManager.args[0] == 'sync': OptionsManager.args.pop(0) dprint("using machines: %s\n" % machineList, True) dprint("current working directory: %s\n" % os.getcwd()) if SimEnvironment.APP_PATH == None: dprint("Error: cannot proceed with an unknown APP_PATH", True) sys.exit(1) cactusDir = SimEnvironment.APP_PATH dprint("Cactus Directory: %s" % cactusDir, True) if os.getcwd() != cactusDir: dprint("Current Working directory does not match Cactus sourcetree, changing to %s" % cactusDir) # make sure we're in the Cactus source directory, otherwise all of this will blow way up. os.chdir(cactusDir) # mode of operation sync_parfiles = OptionsManager.GetOption('sync-parfiles') sync_sourcetree = OptionsManager.GetOption('sync-sourcetree') if not(sync_parfiles or sync_sourcetree): dprint("Error, neither sync-parfiles or sync-sourcetree was specified. should never happen", True) sys.exit(1) dprint("Enabled Options:", True) dprint("sync-parfiles: %s" % sync_parfiles, True) dprint("sync-sourcetree: %s" % sync_sourcetree, True) print # get our local_sourcebasedir local_sourcebasedir = SimLib.GetLocalSourceBaseDir() path = SimLib.GetDirSuffix(local_sourcebasedir) dprint("local_sourcebasedir: %s" % local_sourcebasedir) dprint("path: %s" % path) localMachineName = SimEnvironment.LocalMachine localMachineEntry = SimEnvironment.LocalMachineEntry # returns list (rsynccmd, rsyncopts) rsyncInfo = GetRsyncInfo(localMachineEntry) for m in machineList: cmd = CompileCommand(m, rsyncInfo) #dprint(cmd) SimLib.ExecuteCommand(cmd) dprint("\nSync complete.", True) # only run if you are App if os.path.basename(sys.argv[0]) == App: global SimEnvironment SimEnvironment = simenv.SimEnvironment(BASE_PATH, os.path.abspath(sys.argv[0])) SimEnvironment.initApp(usageString=Usage, optionGroups=optionGroups) main(SimEnvironment)