class CSShellCmd(cmd.Cmd): def __init__(self): cmd.Cmd.__init__(self) self.serverURL = "" self.serverName = "" self.modificator = None self.connected = False self.dirty = False self.root = "/" self.do_connect("") def update_prompt(self): if self.connected: self.prompt = "[" + colorize(self.serverName, "green") + ":" + self.root + " ]% " else: self.prompt = "[" + colorize("disconnected", "red") + ":" + self.root + " ]% " def do_connect(self, line): """connect Connect to the CS Usage: connect <URL> (Connect to the CS at the specified URL) connect (Connect to the default CS URL of your config) """ if line == "": self.serverURL = gConfigurationData.getMasterServer() self.serverName = gConfigurationData.getName() else: self.serverURL = self.serverName = line if self.serverURL == None: print "Unable to connect to the default server. Maybe you don't have a proxy ?" return self.do_disconnect("") print "Trying to connect to " + self.serverURL + "...", self.modificator = Modificator(RPCClient(self.serverURL)) rv = self.modificator.loadFromRemote() rv2 = self.modificator.loadCredentials() if rv['OK'] == False or rv2['OK'] == False: print "failed: ", if rv['OK'] == False: print rv['Message'] else: print rv2['Message'] self.connected = False self.update_prompt() else:
class CSShellCmd(cmd.Cmd): def __init__(self): cmd.Cmd.__init__(self) self.serverURL = "" self.serverName = "" self.modificator = None self.connected = False self.dirty = False self.root = "/" self.do_connect("") def update_prompt(self): if self.connected: self.prompt = "[" + colorize(self.serverName, "green") + ":" + self.root + " ]% " else: self.prompt = "[" + colorize("disconnected", "red") + ":" + self.root + " ]% " def do_connect(self, line): """connect Connect to the CS Usage: connect <URL> (Connect to the CS at the specified URL) connect (Connect to the default CS URL of your config) """ if line == "": self.serverURL = gConfigurationData.getMasterServer() self.serverName = gConfigurationData.getName() else: self.serverURL = self.serverName = line if self.serverURL == None: print "Unable to connect to the default server. Maybe you don't have a proxy ?" return self.do_disconnect("") print "Trying to connect to " + self.serverURL + "...", self.modificator = Modificator(RPCClient(self.serverURL)) rv = self.modificator.loadFromRemote() rv2 = self.modificator.loadCredentials() if rv['OK'] == False or rv2['OK'] == False: print "failed: ", if rv['OK'] == False: print rv['Message'] else: print rv2['Message'] self.connected = False self.update_prompt() else:
class CSCLI(CLI): def __init__(self): CLI.__init__(self) self.connected = False self.masterURL = "unset" self.writeEnabled = False self.modifiedData = False self.rpcClient = None self.do_connect() if self.connected: self.modificator = Modificator(self.rpcClient) else: self.modificator = Modificator() self.indentSpace = 20 self.backupFilename = "dataChanges" # store history histfilename = os.path.basename(sys.argv[0]) historyFile = os.path.expanduser("~/.dirac/%s.history" % histfilename[0:-3]) mkDir(os.path.dirname(historyFile)) if os.path.isfile(historyFile): readline.read_history_file(historyFile) readline.set_history_length(1000) atexit.register(readline.write_history_file, historyFile) def start(self): if self.connected: self.modificator.loadFromRemote() retVal = self.modificator.loadCredentials() if not retVal['OK']: print("There was an error gathering your credentials") print(retVal['Message']) self._setStatus(False) try: self.cmdloop() except KeyboardInterrupt: gLogger.warn("Received a keyboard interrupt.") self.do_quit("") def _setConnected(self, connected, writeEnabled): self.connected = connected self.modifiedData = False self.writeEnabled = writeEnabled if connected: if writeEnabled: self.prompt = "(%s)-%s> " % (self.masterURL, colorize("Connected", "green")) else: self.prompt = "(%s)-%s> " % ( self.masterURL, colorize("Connected (RO)", "yellow")) else: self.prompt = "(%s)-%s> " % (self.masterURL, colorize("Disconnected", "red")) def do_quit(self, dummy): """ Exits the application without sending changes to server Usage: quit """ print() if self.modifiedData: print("Changes are about to be written to file for later use.") self.do_writeToFile(self.backupFilename) print("Changes written to %s.cfg" % self.backupFilename) sys.exit(0) def _setStatus(self, connected=True): if not connected: self.masterURL = "unset" self._setConnected(False, False) else: retVal = self.rpcClient.writeEnabled() if retVal['OK']: if retVal['Value']: self._setConnected(True, True) else: self._setConnected(True, False) else: print("Server returned an error: %s" % retVal['Message']) self._setConnected(True, False) def _tryConnection(self): print("Trying connection to %s" % self.masterURL) try: self.rpcClient = ConfigurationClient(url=self.masterURL) self._setStatus() except Exception as x: gLogger.error("Couldn't connect to master CS server", "%s (%s)" % (self.masterURL, str(x))) self._setStatus(False) def do_connect(self, args=''): """ Connects to configuration master server (in specified url if provided). Usage: connect <url> """ if not args or not isinstance(args, six.string_types): self.masterURL = gConfigurationData.getMasterServer() if self.masterURL != "unknown" and self.masterURL: self._tryConnection() else: self._setStatus(False) else: splitted = args.split() if len(splitted) == 0: print("Must specify witch url to connect") self._setStatus(False) else: self.masterURL = splitted[0].strip() self._tryConnection() def do_sections(self, args): """ Shows all sections with their comments. If no section is specified, root is taken. Usage: sections <section> """ try: argList = args.split() if argList: baseSection = argList[0].strip() else: baseSection = "/" if not self.modificator.existsSection(baseSection): print("Section %s does not exist" % baseSection) return sectionList = self.modificator.getSections(baseSection) if not sectionList: print("Section %s is empty" % baseSection) return for section in sectionList: section = "%s/%s" % (baseSection, section) self.printPair(section, self.modificator.getComment(section), " #") except Exception: _showTraceback() def do_options(self, args): """ Shows all options and values of a specified section Usage: options <section> """ try: argList = args.split() if argList: section = argList[0].strip() else: print("Which section?") return if not self.modificator.existsSection(section): print("Section %s does not exist" % section) return optionsList = self.modificator.getOptions(section) if not optionsList: print("Section %s has no options" % section) return for option in optionsList: _printComment( self.modificator.getComment("%s/%s" % (section, option))) self.printPair( option, self.modificator.getValue("%s/%s" % (section, option)), "=") except Exception: _showTraceback() def do_get(self, args): """ Shows value and comment for specified option in section Usage: get <path to option> """ try: argList = args.split() if argList: optionPath = argList[0].strip() else: print("Which option?") return if self.modificator.existsOption(optionPath): option = optionPath.split("/")[-1] _printComment(self.modificator.getComment(optionPath)) self.printPair(option, self.modificator.getValue(optionPath), "=") else: print("Option %s does not exist" % optionPath) except Exception: _showTraceback() def do_writeToServer(self, dummy): """ Sends changes to server. Usage: writeToServer """ if not self.connected: print("You are not connected!") return try: if not self.writeEnabled: print("This server can't receive data modifications") return if not self.modifiedData: while True: choice = six.moves.input( "Data has not been modified, do you still want to upload changes? yes/no [no]: " ) choice = choice.lower() if choice in ("yes", "y"): break else: print("Commit aborted") return choice = six.moves.input( "Do you really want to send changes to server? yes/no [no]: ") choice = choice.lower() if choice in ("yes", "y"): print("Uploading changes to %s (It may take some seconds)..." % self.masterURL) response = self.modificator.commit() if response['OK']: self.modifiedData = False print("Data sent to server.") self.modificator.loadFromRemote() else: print("Error sending data, server said: %s" % response['Message']) return else: print("Commit aborted") except Exception as x: _showTraceback() print("Could not upload changes. ", str(x)) def do_set(self, args): """ Sets option's value Usage: set <optionPath> <value>... From second argument until the last one is considered option's value NOTE: If specified section does not exist it is created. """ try: argsList = args.split() if len(argsList) < 2: print("Must specify option and value to use") return optionPath = argsList[0].strip() value = " ".join(argsList[1:]).strip() self.modificator.setOptionValue(optionPath, value) self.modifiedData = True except Exception as x: print("Cannot insert value: ", str(x)) def do_removeOption(self, args): """ Removes an option. Usage: removeOption <option> There can be empty sections. """ try: argsList = args.split() if len(argsList) < 1: print("Must specify option to delete") return optionPath = argsList[0].strip() choice = six.moves.input( "Are you sure you want to delete %s? yes/no [no]: " % optionPath) choice = choice.lower() if choice in ("yes", "y", "true"): if self.modificator.removeOption(optionPath): self.modifiedData = True else: print("Can't be deleted") else: print("Aborting removal.") except Exception as x: print("Error removing option, %s" % str(x)) def do_removeSection(self, args): """ Removes a section. Usage: removeSection <section> """ try: argsList = args.split() if len(argsList) < 1: print("Must specify section to delete") return section = argsList[0].strip() choice = six.moves.input( "Are you sure you want to delete %s? yes/no [no]: " % section) choice = choice.lower() if choice in ("yes", "y", "true"): if self.modificator.removeSection(section): self.modifiedData = True else: print("Can't be deleted") else: print("Aborting removal.") except Exception as x: print("Error removing section, %s" % str(x)) def do_setComment(self, args): """ Sets option or section's comment. Requested entry MUST exist. Usage: set <option/section> <comment>... From third argument until the last one is considered option's comment. """ try: argsList = args.split() if len(argsList) < 2: print("Must specify option and value to use") return entryPath = argsList[0].strip() value = " ".join(argsList[1:]).strip() self.modificator.setComment(entryPath, value) self.modifiedData = True except Exception as x: print("Cannot insert comment: ", str(x)) def do_writeToFile(self, args): """ Writes modification to file for later use. Usage: writeToFile <filename>.cfg Note that if a file extension is specified, it is replaced by .cfg suffix. If not it is added automatically """ try: if len(args) == 0: print("Filename to write must be specified!") return filename = args.split()[0].strip() filename = _appendExtensionIfMissing(filename) self.modificator.dumpToFile(filename) except Exception as x: print("Couldn't write to file %s: %s" % (filename, str(x))) def do_readFromFile(self, args): """ Reads data from filename to be used. Actual data will be replaced! Usage: readFromFile <filename>.cfg Note that if a file extension is specified, it is replaced by .cfg suffix. If not it is added automatically """ try: if len(args) == 0: print("Filename to read must be specified!") return filename = args.split()[0].strip() filename = _appendExtensionIfMissing(filename) self.modificator.loadFromFile(filename) self.modifiedData = True except Exception as x: print("Couldn't read from file %s: %s" % (filename, str(x))) def do_mergeFromFile(self, args): """ Reads data from filename and merges it with current data. Data read from file has more precedence that current one. Usage: mergeFromFile <filename>.cfg Note that if a file extension is specified, it is replaced by .cfg suffix. If not it is added automatically """ try: if len(args) == 0: print("Filename to read must be specified!") return filename = args.split()[0].strip() filename = _appendExtensionIfMissing(filename) self.modificator.mergeFromFile(filename) self.modifiedData = True except Exception as x: _showTraceback() print("Couldn't read from file %s: %s" % (filename, str(x))) def do_showData(self, dummy): """ Shows the current modified configuration Usage: showData """ print(self.modificator) def do_showHistory(self, args): """ Shows the last commit history Usage: showHistory <update limit> """ try: argsList = args.split() limit = 100 if len(argsList) > 0: limit = int(argsList[0]) history = self.modificator.getHistory(limit) print("%s recent commits:" % limit) for entry in history: self.printPair(entry[0], entry[1], "@") except Exception: _showTraceback() def do_showDiffWithServer(self, dummy): """ Shows diff with lastest version in server Usage: showDiffWithServer """ try: diffData = self.modificator.showCurrentDiff() print("Diff with latest from server ( + local - remote )") for line in diffData: if line[0] in ('-'): print(colorize(line, "red")) elif line[0] in ('+'): print(colorize(line, "green")) elif line[0] in ('?'): print(colorize(line, "yellow"), end=' ') except Exception: _showTraceback() def do_showDiffBetweenVersions(self, args): """ Shows diff between two versions Usage: showDiffBetweenVersions <version 1 with spaces> <version 2 with spaces> """ try: argsList = args.split() if len(argsList) < 4: print("What are the two versions to compare?") return v1 = " ".join(argsList[0:2]) v2 = " ".join(argsList[2:4]) print("Comparing '%s' with '%s' " % (v1, v2)) diffData = self.modificator.getVersionDiff(v1, v2) print("Diff with latest from server ( + %s - %s )" % (v2, v1)) for line in diffData: if line[0] in ('-'): print(colorize(line, "red")) elif line[0] in ('+'): print(colorize(line, "green")) elif line[0] in ('?'): print(colorize(line, "yellow"), end=' ') else: print(line) except Exception: _showTraceback() def do_rollbackToVersion(self, args): """ rolls back to user selected version of the configuration Usage: rollbackToVersion <version with spaces>> """ try: argsList = args.split() if len(argsList) < 2: print("What version to rollback?") return version = " ".join(argsList[0:2]) choice = six.moves.input( "Do you really want to rollback to version %s? yes/no [no]: " % version) choice = choice.lower() if choice in ("yes", "y"): response = self.modificator.rollbackToVersion(version) if response['OK']: self.modifiedData = False print("Rolled back.") self.modificator.loadFromRemote() else: print("Error sending data, server said: %s" % response['Message']) except Exception: _showTraceback() def do_mergeWithServer(self, dummy): """ Shows diff with lastest version in server Usage: diffWithServer """ try: choice = six.moves.input( "Do you want to merge with server configuration? yes/no [no]: " ) choice = choice.lower() if choice in ("yes", "y"): retVal = self.modificator.mergeWithServer() if retVal['OK']: print("Merged") else: print("There was an error: ", retVal['Message']) else: print("Merge aborted") except Exception: _showTraceback()
class CSCLI(cmd.Cmd): def __init__(self): cmd.Cmd.__init__(self) self.connected = False self.masterURL = "unset" self.writeEnabled = False self.modifiedData = False self.rpcClient = None self.do_connect() if self.connected: self.modificator = Modificator(self.rpcClient) else: self.modificator = Modificator() self.identSpace = 20 self.backupFilename = "dataChanges" self._initSignals() #User friendly hack self.do_exit = self.do_quit def start(self): if self.connected: self.modificator.loadFromRemote() retVal = self.modificator.loadCredentials() if not retVal['OK']: print "There was an error gathering your credentials" print retVal['Message'] self._setStatus(False) try: self.cmdloop() except KeyboardInterrupt: gLogger.warn("Received a keyboard interrupt.") self.do_quit("") def _initSignals(self): """ Registers signal handlers """ for sigNum in (signal.SIGINT, signal.SIGQUIT, signal.SIGKILL, signal.SIGTERM): try: signal.signal(sigNum, self.do_quit) except: pass def _setConnected(self, connected, writeEnabled): self.connected = connected self.modifiedData = False self.writeEnabled = writeEnabled if connected: if writeEnabled: self.prompt = "(%s)-%s> " % (self.masterURL, colorize("Connected", "green")) else: self.prompt = "(%s)-%s> " % ( self.masterURL, colorize("Connected (RO)", "yellow")) else: self.prompt = "(%s)-%s> " % (self.masterURL, colorize("Disconnected", "red")) def _printPair(self, key, value, separator=":"): valueList = value.split("\n") print "%s%s%s %s" % (key, " " * (self.identSpace - len(key)), separator, valueList[0].strip()) for valueLine in valueList[1:-1]: print "%s %s" % (" " * self.identSpace, valueLine.strip()) def do_quit(self, dummy): """ Exits the application without sending changes to server Usage: quit """ if self.modifiedData: print "Changes are about to be written to file for later use." self.do_writeToFile(self.backupFilename) print "Changes written to %s.cfg" % self.backupFilename sys.exit(0) def do_EOF(self, args): """ Accepts ctrl^D to quit CLI """ print "" self.do_quit(args) def do_help(self, args): """ Shows help information Usage: help <command> If no command is specified all commands are shown """ if len(args) == 0: print "\nAvailable commands:\n" attrList = dir(self) attrList.sort() for attribute in attrList: if attribute.find("do_") == 0: self._printPair(attribute[3:], getattr(self, attribute).__doc__[1:]) print "" else: command = args.split()[0].strip() try: obj = getattr(self, "do_%s" % command) except: print "There's no such %s command" % command return self._printPair(command, obj.__doc__[1:]) # def retrieveData( self ): # if not self.connected: # return False # response = self.rpcClient.dumpCompressed() # if response[ 'Status' ] == 'OK': # self.cDataHolder.loadFromCompressedSource( response[ 'Value' ] ) # gLogger.info( "Data retrieved from server." ) # return True # else: # gLogger.error( "Can't retrieve updated data from server." ) # return False def _setStatus(self, connected=True): if not connected: self.masterURL = "unset" self._setConnected(False, False) else: retVal = self.rpcClient.writeEnabled() if retVal['OK']: if retVal['Value'] == True: self._setConnected(True, True) else: self._setConnected(True, False) else: print "Server returned an error: %s" % retVal['Message'] self._setConnected(True, False) def _tryConnection(self): print "Trying connection to %s" % self.masterURL try: self.rpcClient = RPCClient(self.masterURL) self._setStatus() except Exception, x: gLogger.error("Couldn't connect to %s (%s)" % (self.masterURL, str(x))) self._setStatus(False)
class CSCLI( cmd.Cmd ): def __init__( self ): cmd.Cmd.__init__( self ) self.connected = False self.masterURL = "unset" self.writeEnabled = False self.modifiedData = False self.rpcClient = None self.do_connect() if self.connected: self.modificator = Modificator ( self.rpcClient ) else: self.modificator = Modificator() self.identSpace = 20 self.backupFilename = "dataChanges" self._initSignals() #User friendly hack self.do_exit = self.do_quit # store history histfilename = os.path.basename(sys.argv[0]) historyFile = os.path.expanduser( "~/.dirac/%s.history" % histfilename[0:-3]) if not os.path.exists( os.path.dirname(historyFile) ): os.makedirs( os.path.dirname(historyFile) ) if os.path.isfile( historyFile ): readline.read_history_file( historyFile ) readline.set_history_length(1000) atexit.register( readline.write_history_file, historyFile ) def start( self ): if self.connected: self.modificator.loadFromRemote() retVal = self.modificator.loadCredentials() if not retVal[ 'OK' ]: print "There was an error gathering your credentials" print retVal[ 'Message' ] self._setStatus( False ) try: self.cmdloop() except KeyboardInterrupt: gLogger.warn( "Received a keyboard interrupt." ) self.do_quit( "" ) def _initSignals( self ): """ Registers signal handlers """ for sigNum in ( signal.SIGINT, signal.SIGQUIT, signal.SIGKILL, signal.SIGTERM ): try: signal.signal( sigNum, self.do_quit ) except: pass def _setConnected( self, connected, writeEnabled ): self.connected = connected self.modifiedData = False self.writeEnabled = writeEnabled if connected: if writeEnabled: self.prompt = "(%s)-%s> " % ( self.masterURL, colorize( "Connected", "green" ) ) else: self.prompt = "(%s)-%s> " % ( self.masterURL, colorize( "Connected (RO)", "yellow" ) ) else: self.prompt = "(%s)-%s> " % ( self.masterURL, colorize( "Disconnected", "red" ) ) def _printPair( self, key, value, separator = ":" ): valueList = value.split( "\n" ) print "%s%s%s %s" % ( key, " " * ( self.identSpace - len( key ) ), separator, valueList[0].strip() ) for valueLine in valueList[ 1:-1 ]: print "%s %s" % ( " " * self.identSpace, valueLine.strip() ) def do_quit( self, dummy ): """ Exits the application without sending changes to server Usage: quit """ if self.modifiedData: print "Changes are about to be written to file for later use." self.do_writeToFile( self.backupFilename ) print "Changes written to %s.cfg" % self.backupFilename sys.exit( 0 ) def do_EOF( self, args ): """ Accepts ctrl^D to quit CLI """ print "" self.do_quit( args ) def do_help( self, args ): """ Shows help information Usage: help <command> If no command is specified all commands are shown """ if len( args ) == 0: print "\nAvailable commands:\n" attrList = dir( self ) attrList.sort() for attribute in attrList: if attribute.find( "do_" ) == 0: self._printPair( attribute[ 3: ], getattr( self, attribute ).__doc__[ 1: ] ) print "" else: command = args.split()[0].strip() try: obj = getattr( self, "do_%s" % command ) except: print "There's no such %s command" % command return self._printPair( command, obj.__doc__[1:] ) # def retrieveData( self ): # if not self.connected: # return False # response = self.rpcClient.dumpCompressed() # if response[ 'Status' ] == 'OK': # self.cDataHolder.loadFromCompressedSource( response[ 'Value' ] ) # gLogger.info( "Data retrieved from server." ) # return True # else: # gLogger.error( "Can't retrieve updated data from server." ) # return False def _setStatus( self, connected = True ): if not connected: self.masterURL = "unset" self._setConnected( False, False ) else: retVal = self.rpcClient.writeEnabled() if retVal[ 'OK' ]: if retVal[ 'Value' ] == True: self._setConnected( True, True ) else: self._setConnected( True, False ) else: print "Server returned an error: %s" % retVal[ 'Message' ] self._setConnected( True, False ) def _tryConnection( self ): print "Trying connection to %s" % self.masterURL try: self.rpcClient = RPCClient( self.masterURL ) self._setStatus() except Exception, x: gLogger.error( "Couldn't connect to master CS server", "%s (%s)" % ( self.masterURL, str( x ) ) ) self._setStatus( False )
class CSShellCLI(CLI): def __init__(self): CLI.__init__(self) self.serverURL = "" self.serverName = "" self.modificator = None self.connected = False self.dirty = False self.root = "/" self.do_connect("") def update_prompt(self): if self.connected: self.prompt = "[" + colorize(self.serverName, "green") + ":" + self.root + " ]% " else: self.prompt = "[" + colorize("disconnected", "red") + ":" + self.root + " ]% " def do_connect(self, line): """connect Connect to the CS Usage: connect <URL> (Connect to the CS at the specified URL) connect (Connect to the default CS URL of your config) """ if line == "": self.serverURL = gConfigurationData.getMasterServer() self.serverName = gConfigurationData.getName() else: self.serverURL = self.serverName = line if self.serverURL == None: print "Unable to connect to the default server. Maybe you don't have a proxy ?" return self.do_disconnect("") print "Trying to connect to " + self.serverURL + "...", self.modificator = Modificator(RPCClient(self.serverURL)) rv = self.modificator.loadFromRemote() rv2 = self.modificator.loadCredentials() if rv["OK"] == False or rv2["OK"] == False: print "failed: ", if rv["OK"] == False: print rv["Message"] else: print rv2["Message"] self.connected = False self.update_prompt() else: self.connected = True self.update_prompt() print "done." def do_disconnect(self, _line): """Disconnect from CS""" if self.connected and self.dirty: res = raw_input("Do you want to commit your changes into the CS ? [y/N] ") if res.lower() in ["y", "yes"]: self.do_commit("") self.serverURL = self.serverName = "" self.modificator = None self.connected = False self.update_prompt() def do_ls(self, line): """ls List the sections and options of CS of the current root""" if self.connected: secs = self.modificator.getSections(self.root) opts = self.modificator.getOptions(self.root) if line.startswith("-") and "l" in line: for i in secs: print colorize(i, "blue") + " " for i in opts: print i + " " else: for i in secs: print colorize(i, "blue") + " ", for i in opts: print i + " ", print "" def do_cd(self, line): """cd Go one directory deeper in the CS""" # Check if invariant holds if self.connected: assert self.root == "/" or not self.root.endswith("/") assert self.root.startswith("/") secs = self.modificator.getSections(self.root) if line == "..": self.root = os.path.dirname(self.root) self.update_prompt() else: if os.path.normpath(line) in secs: if self.root == "/": self.root = self.root + os.path.normpath(line) else: self.root = self.root + "/" + os.path.normpath(line) self.update_prompt() else: print "cd: no such section: " + line def complete_cd(self, text, _line, _begidx, _endidx): secs = self.modificator.getSections(self.root) return [(s + "/") for s in secs if s.startswith(text)] def do_cat(self, line): """cat Read the content of an option in the CS""" if self.connected: opts = self.modificator.getOptionsDict(self.root) if line in opts.keys(): print opts[line] else: print "cat: No such option" def complete_cat(self, text, _line, _begidx, _endidx): opts = self.modificator.getOptions(self.root) return [o for o in opts if o.startswith(text)] do_less = do_cat complete_less = complete_cat def do_mkdir(self, line): """mkdir Create a new section in the CS""" if self.connected: self.modificator.createSection(self.root + "/" + line) self.dirty = True complete_mkdir = complete_cd def do_rmdir(self, line): """rmdir Delete a section in the CS""" if self.connected: self.modificator.removeSection(self.root + "/" + line) self.dirty = True complete_rmdir = complete_cd def do_rm(self, line): """rm Delete an option in the CS""" if self.connected: self.modificator.removeOption(self.root + "/" + line) self.dirty = True complete_rm = complete_cat def do_set(self, line): """set Set an option in the CS (or create it if it does not exists) Usage: set <str> to set a string option (will be stored as a string in CS) set <str>,<str>,... to set a list option (will be stored as a list in CS) """ if self.connected: line = line.split(" ", 2) if len(line) != 2: print "Usage: set <key> <value>" else: self.modificator.setOptionValue(self.root + "/" + line[0], line[1]) self.dirty = True complete_set = complete_cat def do_unset(self, line): """unset Unset an option in the CS: Making the option equal to the empty string.""" if self.connected: self.modificator.setOptionValue(self.root + "/" + line, "") self.dirty = True complete_unset = complete_cat def do_commit(self, _line): """commit Commit the modifications to the CS""" if self.connected and self.dirty: self.modificator.commit() def default(self, line): """Override [Cmd.default(line)] function.""" if line == "EOF": if self.prompt: print return self.do_quit(line) else: cmd.Cmd.default(self, line) def do_quit(self, _line): """quit Quit""" self.do_disconnect("") CLI.do_quit(self, _line)
class CSShellCLI(CLI): def __init__(self): CLI.__init__(self) self.serverURL = "" self.serverName = "" self.modificator = None self.connected = False self.dirty = False self.root = "/" self.do_connect("") def update_prompt(self): if self.connected: self.prompt = "[" + colorize(self.serverName, "green") + ":" + self.root + " ]% " else: self.prompt = "[" + colorize("disconnected", "red") + ":" + self.root + " ]% " def do_connect(self, line): """connect Connect to the CS Usage: connect <URL> (Connect to the CS at the specified URL) connect (Connect to the default CS URL of your config) """ if line == "": self.serverURL = gConfigurationData.getMasterServer() self.serverName = gConfigurationData.getName() else: self.serverURL = self.serverName = line if self.serverURL == None: print( "Unable to connect to the default server. Maybe you don't have a proxy ?" ) return self.do_disconnect("") print("Trying to connect to " + self.serverURL + "...", end=' ') self.modificator = Modificator(RPCClient(self.serverURL)) rv = self.modificator.loadFromRemote() rv2 = self.modificator.loadCredentials() if rv['OK'] == False or rv2['OK'] == False: print("failed: ", end=' ') if rv['OK'] is False: print(rv['Message']) else: print(rv2['Message']) self.connected = False self.update_prompt() else: self.connected = True self.update_prompt() print("done.") def do_disconnect(self, _line): """Disconnect from CS""" if self.connected and self.dirty: res = raw_input( "Do you want to commit your changes into the CS ? [y/N] ") if res.lower() in ["y", "yes"]: self.do_commit("") self.serverURL = self.serverName = "" self.modificator = None self.connected = False self.update_prompt() def do_ls(self, line): """ls List the sections and options of CS of the current root""" if self.connected: secs = self.modificator.getSections(self.root) opts = self.modificator.getOptions(self.root) if line.startswith("-") and "l" in line: for i in secs: print(colorize(i, "blue") + " ") for i in opts: print(i + " ") else: for i in secs: print(colorize(i, "blue") + " ", end=' ') for i in opts: print(i + " ", end=' ') print("") def do_cd(self, line): """cd Go one directory deeper in the CS""" # Check if invariant holds if self.connected: assert (self.root == "/" or not self.root.endswith("/")) assert (self.root.startswith("/")) secs = self.modificator.getSections(self.root) if line == "..": self.root = os.path.dirname(self.root) self.update_prompt() else: if os.path.normpath(line) in secs: if self.root == "/": self.root = self.root + os.path.normpath(line) else: self.root = self.root + "/" + os.path.normpath(line) self.update_prompt() else: print("cd: no such section: " + line) def complete_cd(self, text, _line, _begidx, _endidx): secs = self.modificator.getSections(self.root) return [(s + "/") for s in secs if s.startswith(text)] def do_cat(self, line): """cat Read the content of an option in the CS""" if self.connected: opts = self.modificator.getOptionsDict(self.root) if line in opts.keys(): print(opts[line]) else: print("cat: No such option") def complete_cat(self, text, _line, _begidx, _endidx): opts = self.modificator.getOptions(self.root) return [o for o in opts if o.startswith(text)] do_less = do_cat complete_less = complete_cat def do_mkdir(self, line): """mkdir Create a new section in the CS""" if self.connected: self.modificator.createSection(self.root + "/" + line) self.dirty = True complete_mkdir = complete_cd def do_rmdir(self, line): """rmdir Delete a section in the CS""" if self.connected: self.modificator.removeSection(self.root + "/" + line) self.dirty = True complete_rmdir = complete_cd def do_rm(self, line): """rm Delete an option in the CS""" if self.connected: self.modificator.removeOption(self.root + "/" + line) self.dirty = True complete_rm = complete_cat def do_set(self, line): """set Set an option in the CS (or create it if it does not exists) Usage: set <str> to set a string option (will be stored as a string in CS) set <str>,<str>,... to set a list option (will be stored as a list in CS) """ if self.connected: line = line.split(" ", 2) if len(line) != 2: print("Usage: set <key> <value>") else: self.modificator.setOptionValue(self.root + "/" + line[0], line[1]) self.dirty = True complete_set = complete_cat def do_unset(self, line): """unset Unset an option in the CS: Making the option equal to the empty string.""" if self.connected: self.modificator.setOptionValue(self.root + "/" + line, "") self.dirty = True complete_unset = complete_cat def do_commit(self, _line): """commit Commit the modifications to the CS""" if self.connected and self.dirty: self.modificator.commit() def default(self, line): """Override [Cmd.default(line)] function.""" if line == "EOF": if self.prompt: print() return self.do_quit(line) else: cmd.Cmd.default(self, line) def do_quit(self, _line): """quit Quit""" self.do_disconnect("") CLI.do_quit(self, _line)
class CSCLI( CLI ): def __init__( self ): CLI.__init__( self ) self.connected = False self.masterURL = "unset" self.writeEnabled = False self.modifiedData = False self.rpcClient = None self.do_connect() if self.connected: self.modificator = Modificator ( self.rpcClient ) else: self.modificator = Modificator() self.indentSpace = 20 self.backupFilename = "dataChanges" # store history histfilename = os.path.basename(sys.argv[0]) historyFile = os.path.expanduser( "~/.dirac/%s.history" % histfilename[0:-3]) if not os.path.exists( os.path.dirname(historyFile) ): os.makedirs( os.path.dirname(historyFile) ) if os.path.isfile( historyFile ): readline.read_history_file( historyFile ) readline.set_history_length(1000) atexit.register( readline.write_history_file, historyFile ) def start( self ): if self.connected: self.modificator.loadFromRemote() retVal = self.modificator.loadCredentials() if not retVal[ 'OK' ]: print "There was an error gathering your credentials" print retVal[ 'Message' ] self._setStatus( False ) try: self.cmdloop() except KeyboardInterrupt: gLogger.warn( "Received a keyboard interrupt." ) self.do_quit( "" ) def _setConnected( self, connected, writeEnabled ): self.connected = connected self.modifiedData = False self.writeEnabled = writeEnabled if connected: if writeEnabled: self.prompt = "(%s)-%s> " % ( self.masterURL, colorize( "Connected", "green" ) ) else: self.prompt = "(%s)-%s> " % ( self.masterURL, colorize( "Connected (RO)", "yellow" ) ) else: self.prompt = "(%s)-%s> " % ( self.masterURL, colorize( "Disconnected", "red" ) ) def do_quit( self, dummy ): """ Exits the application without sending changes to server Usage: quit """ print if self.modifiedData: print "Changes are about to be written to file for later use." self.do_writeToFile( self.backupFilename ) print "Changes written to %s.cfg" % self.backupFilename sys.exit( 0 ) # def retrieveData( self ): # if not self.connected: # return False # response = self.rpcClient.dumpCompressed() # if response[ 'Status' ] == 'OK': # self.cDataHolder.loadFromCompressedSource( response[ 'Value' ] ) # gLogger.info( "Data retrieved from server." ) # return True # else: # gLogger.error( "Can't retrieve updated data from server." ) # return False def _setStatus( self, connected = True ): if not connected: self.masterURL = "unset" self._setConnected( False, False ) else: retVal = self.rpcClient.writeEnabled() if retVal[ 'OK' ]: if retVal[ 'Value' ] == True: self._setConnected( True, True ) else: self._setConnected( True, False ) else: print "Server returned an error: %s" % retVal[ 'Message' ] self._setConnected( True, False ) def _tryConnection( self ): print "Trying connection to %s" % self.masterURL try: self.rpcClient = RPCClient( self.masterURL ) self._setStatus() except Exception, x: gLogger.error( "Couldn't connect to master CS server", "%s (%s)" % ( self.masterURL, str( x ) ) ) self._setStatus( False )
class CSCLI( CLI ): def __init__( self ): CLI.__init__( self ) self.connected = False self.masterURL = "unset" self.writeEnabled = False self.modifiedData = False self.rpcClient = None self.do_connect() if self.connected: self.modificator = Modificator ( self.rpcClient ) else: self.modificator = Modificator() self.indentSpace = 20 self.backupFilename = "dataChanges" # store history histfilename = os.path.basename(sys.argv[0]) historyFile = os.path.expanduser( "~/.dirac/%s.history" % histfilename[0:-3]) mkDir(os.path.dirname(historyFile)) if os.path.isfile( historyFile ): readline.read_history_file( historyFile ) readline.set_history_length(1000) atexit.register( readline.write_history_file, historyFile ) def start( self ): if self.connected: self.modificator.loadFromRemote() retVal = self.modificator.loadCredentials() if not retVal[ 'OK' ]: print "There was an error gathering your credentials" print retVal[ 'Message' ] self._setStatus( False ) try: self.cmdloop() except KeyboardInterrupt: gLogger.warn( "Received a keyboard interrupt." ) self.do_quit( "" ) def _setConnected( self, connected, writeEnabled ): self.connected = connected self.modifiedData = False self.writeEnabled = writeEnabled if connected: if writeEnabled: self.prompt = "(%s)-%s> " % ( self.masterURL, colorize( "Connected", "green" ) ) else: self.prompt = "(%s)-%s> " % ( self.masterURL, colorize( "Connected (RO)", "yellow" ) ) else: self.prompt = "(%s)-%s> " % ( self.masterURL, colorize( "Disconnected", "red" ) ) def do_quit( self, dummy ): """ Exits the application without sending changes to server Usage: quit """ print if self.modifiedData: print "Changes are about to be written to file for later use." self.do_writeToFile( self.backupFilename ) print "Changes written to %s.cfg" % self.backupFilename sys.exit( 0 ) # def retrieveData( self ): # if not self.connected: # return False # response = self.rpcClient.dumpCompressed() # if response[ 'Status' ] == 'OK': # self.cDataHolder.loadFromCompressedSource( response[ 'Value' ] ) # gLogger.info( "Data retrieved from server." ) # return True # else: # gLogger.error( "Can't retrieve updated data from server." ) # return False def _setStatus( self, connected = True ): if not connected: self.masterURL = "unset" self._setConnected( False, False ) else: retVal = self.rpcClient.writeEnabled() if retVal[ 'OK' ]: if retVal[ 'Value' ] == True: self._setConnected( True, True ) else: self._setConnected( True, False ) else: print "Server returned an error: %s" % retVal[ 'Message' ] self._setConnected( True, False ) def _tryConnection( self ): print "Trying connection to %s" % self.masterURL try: self.rpcClient = RPCClient( self.masterURL ) self._setStatus() except Exception as x: gLogger.error( "Couldn't connect to master CS server", "%s (%s)" % ( self.masterURL, str( x ) ) ) self._setStatus( False ) def do_connect( self, args = '' ): """ Connects to configuration master server (in specified url if provided). Usage: connect <url> """ if not args or type( args ) not in types.StringTypes: self.masterURL = gConfigurationData.getMasterServer() if self.masterURL != "unknown" and self.masterURL: self._tryConnection() else: self._setStatus( False ) else: splitted = args.split() if len( splitted ) == 0: print "Must specify witch url to connect" self._setStatus( False ) else: self.masterURL = splitted[0].strip() self._tryConnection() def do_sections( self, args ): """ Shows all sections with their comments. If no section is specified, root is taken. Usage: sections <section> """ try: argList = args.split() if argList: baseSection = argList[0].strip() else: baseSection = "/" if not self.modificator.existsSection( baseSection ): print "Section %s does not exist" % baseSection return sectionList = self.modificator.getSections( baseSection ) if not sectionList: print "Section %s is empty" % baseSection return for section in sectionList: section = "%s/%s" % ( baseSection, section ) self.printPair( section, self.modificator.getComment( section ) , " #" ) except: _showTraceback() def do_options( self, args ): """ Shows all options and values of a specified section Usage: options <section> """ try: argList = args.split() if argList: section = argList[0].strip() else: print "Which section?" return if not self.modificator.existsSection( section ): print "Section %s does not exist" % section return optionsList = self.modificator.getOptions( section ) if not optionsList: print "Section %s has no options" % section return for option in optionsList: _printComment( self.modificator.getComment( "%s/%s" % ( section, option ) ) ) self.printPair( option, self.modificator.getValue( "%s/%s" % ( section, option ) ), "=" ) except: _showTraceback() def do_get( self, args ): """ Shows value and comment for specified option in section Usage: get <path to option> """ try: argList = args.split() if argList: optionPath = argList[0].strip() else: print "Which option?" return if self.modificator.existsOption( optionPath ): option = optionPath.split( "/" )[-1] _printComment( self.modificator.getComment( optionPath ) ) self.printPair( option, self.modificator.getValue( optionPath ), "=" ) else: print "Option %s does not exist" % optionPath except: _showTraceback() def do_writeToServer( self, dummy ): """ Sends changes to server. Usage: writeToServer """ if not self.connected: print "You are not connected!" return try: if not self.writeEnabled: print "This server can't receive data modifications" return if not self.modifiedData: while True: choice = raw_input( "Data has not been modified, do you still want to upload changes? yes/no [no]: " ) choice = choice.lower() if choice in ( "yes", "y" ): break else: print "Commit aborted" return choice = raw_input( "Do you really want to send changes to server? yes/no [no]: " ) choice = choice.lower() if choice in ( "yes", "y" ): print "Uploading changes to %s (It may take some seconds)..." % self.masterURL response = self.modificator.commit() if response[ 'OK' ]: self.modifiedData = False print "Data sent to server." self.modificator.loadFromRemote() else: print "Error sending data, server said: %s" % response['Message'] return else: print "Commit aborted" except Exception as x: _showTraceback() print "Could not upload changes. ", str( x ) def do_set( self, args ): """ Sets option's value Usage: set <optionPath> <value>... From second argument until the last one is considered option's value NOTE: If specified section does not exist it is created. """ try: argsList = args.split() if len( argsList ) < 2: print "Must specify option and value to use" return optionPath = argsList[0].strip() value = " ".join( argsList[1:] ).strip() self.modificator.setOptionValue( optionPath, value ) self.modifiedData = True except Exception as x: print "Cannot insert value: ", str( x ) def do_removeOption( self, args ): """ Removes an option. Usage: removeOption <option> There can be empty sections. """ try: argsList = args.split() if len( argsList ) < 1: print "Must specify option to delete" return optionPath = argsList[0].strip() choice = raw_input( "Are you sure you want to delete %s? yes/no [no]: " % optionPath ) choice = choice.lower() if choice in ( "yes", "y", "true" ): if self.modificator.removeOption( optionPath ): self.modifiedData = True else: print "Can't be deleted" else: print "Aborting removal." except Exception as x: print "Error removing option, %s" % str( x ) def do_removeSection( self, args ): """ Removes a section. Usage: removeSection <section> """ try: argsList = args.split() if len( argsList ) < 1: print "Must specify section to delete" return section = argsList[0].strip() choice = raw_input( "Are you sure you want to delete %s? yes/no [no]: " % section ) choice = choice.lower() if choice in ( "yes", "y", "true" ): if self.modificator.removeSection( section ): self.modifiedData = True else: print "Can't be deleted" else: print "Aborting removal." except Exception as x: print "Error removing section, %s" % str( x ) def do_setComment( self, args ): """ Sets option or section's comment. Requested entry MUST exist. Usage: set <option/section> <comment>... From third argument until the last one is considered option's comment. """ try: argsList = args.split() if len( argsList ) < 2: print "Must specify option and value to use" return entryPath = argsList[0].strip() value = " ".join( argsList[1:] ).strip() self.modificator.setComment( entryPath, value ) self.modifiedData = True except Exception as x: print "Cannot insert comment: ", str( x ) def do_writeToFile( self, args ): """ Writes modification to file for later use. Usage: writeToFile <filename>.cfg Note that if a file extension is specified, it is replaced by .cfg suffix. If not it is added automatically """ try: if len( args ) == 0: print "Filename to write must be specified!" return filename = args.split()[0].strip() filename = _appendExtensionIfMissing( filename ) self.modificator.dumpToFile( filename ) except Exception as x: print "Couldn't write to file %s: %s" % ( filename, str( x ) ) def do_readFromFile( self, args ): """ Reads data from filename to be used. Actual data will be replaced! Usage: readFromFile <filename>.cfg Note that if a file extension is specified, it is replaced by .cfg suffix. If not it is added automatically """ try: if len( args ) == 0: print "Filename to read must be specified!" return filename = args.split()[0].strip() filename = _appendExtensionIfMissing( filename ) self.modificator.loadFromFile( filename ) self.modifiedData = True except Exception as x: print "Couldn't read from file %s: %s" % ( filename, str( x ) ) def do_mergeFromFile( self, args ): """ Reads data from filename and merges it with current data. Data read from file has more precedence that current one. Usage: mergeFromFile <filename>.cfg Note that if a file extension is specified, it is replaced by .cfg suffix. If not it is added automatically """ try: if len( args ) == 0: print "Filename to read must be specified!" return filename = args.split()[0].strip() filename = _appendExtensionIfMissing( filename ) self.modificator.mergeFromFile( filename ) self.modifiedData = True except Exception as x: _showTraceback() print "Couldn't read from file %s: %s" % ( filename, str( x ) ) def do_showData( self, dummy ): """ Shows the current modified configuration Usage: showData """ print self.modificator def do_showHistory( self, args ): """ Shows the last commit history Usage: showHistory <update limit> """ try: argsList = args.split() limit = 100 if len( argsList ) > 0: limit = int( argsList[0] ) history = self.modificator.getHistory( limit ) print "%s recent commits:" % limit for entry in history: self.printPair( entry[0], entry[1], "@" ) except: _showTraceback() def do_showDiffWithServer( self, dummy ): """ Shows diff with lastest version in server Usage: showDiffWithServer """ try: diffData = self.modificator.showCurrentDiff() print "Diff with latest from server ( + local - remote )" for line in diffData: if line[0] in ( '-' ): print colorize( line, "red" ) elif line[0] in ( '+' ): print colorize( line, "green" ) elif line[0] in ( '?' ): print colorize( line, "yellow" ), else: print line except: _showTraceback() def do_showDiffBetweenVersions( self, args ): """ Shows diff between two versions Usage: showDiffBetweenVersions <version 1 with spaces> <version 2 with spaces> """ try: argsList = args.split() if len( argsList ) < 4: print "What are the two versions to compare?" return v1 = " ".join ( argsList[0:2] ) v2 = " ".join ( argsList[2:4] ) print "Comparing '%s' with '%s' " % ( v1, v2 ) diffData = self.modificator.getVersionDiff( v1, v2 ) print "Diff with latest from server ( + %s - %s )" % ( v2, v1 ) for line in diffData: if line[0] in ( '-' ): print colorize( line, "red" ) elif line[0] in ( '+' ): print colorize( line, "green" ) elif line[0] in ( '?' ): print colorize( line, "yellow" ), else: print line except: _showTraceback() def do_rollbackToVersion( self, args ): """ rolls back to user selected version of the configuration Usage: rollbackToVersion <version with spaces>> """ try: argsList = args.split() if len( argsList ) < 2: print "What version to rollback?" return version = " ".join ( argsList[0:2] ) choice = raw_input( "Do you really want to rollback to version %s? yes/no [no]: " % version ) choice = choice.lower() if choice in ( "yes", "y" ): response = self.modificator.rollbackToVersion( version ) if response[ 'OK' ]: self.modifiedData = False print "Rolled back." self.modificator.loadFromRemote() else: print "Error sending data, server said: %s" % response['Message'] except: _showTraceback() def do_mergeWithServer( self, dummy ): """ Shows diff with lastest version in server Usage: diffWithServer """ try: choice = raw_input( "Do you want to merge with server configuration? yes/no [no]: " ) choice = choice.lower() if choice in ( "yes", "y" ): retVal = self.modificator.mergeWithServer() if retVal[ 'OK' ]: print "Merged" else: print "There was an error: ", retVal[ 'Message' ] else: print "Merge aborted" except: _showTraceback()