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()
def __getRemoteConfiguration(self, funcName): rpcClient = RPCClient( gConfig.getValue("/DIRAC/Configuration/MasterServer", "Configuration/Server")) modCfg = Modificator(rpcClient) retVal = modCfg.loadFromRemote() if not retVal['OK']: return { "success": 0, "op": "getSubnodes", "message": "The configuration cannot be read from the remote !" } self.__configData['cfgData'] = modCfg self.__configData['strCfgData'] = str(modCfg) version = str(modCfg.getCFG()["DIRAC"]["Configuration"]["Version"]) configName = str(modCfg.getCFG()["DIRAC"]["Configuration"]["Name"]) return { "success": 1, "op": funcName, "version": version, "name": configName }
def initialize(self): """API initialization :return: S_OK()/S_ERROR() """ if self.__initialized["OK"]: return self.__initialized res = self.__getCertificateID() if gConfig.useServerCertificate( ) else self.__getProxyID() if not res["OK"]: gLogger.error(res["Message"]) self.__initialized = S_ERROR("Cannot locate client credentials") return self.__initialized retVal = gConfig.getOption("/DIRAC/Configuration/MasterServer") if not retVal["OK"]: self.__initialized = S_ERROR( "Master server is not known. Is everything initialized?") return self.__initialized self.__rpcClient = ConfigurationClient( url=gConfig.getValue("/DIRAC/Configuration/MasterServer", "")) self.__csMod = Modificator( self.__rpcClient, "%s - %s - %s" % (self.__userGroup, self.__userDN, datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")), ) retVal = self.downloadCSData() if not retVal["OK"]: self.__initialized = S_ERROR( "Can not download the remote cfg. Is everything initialized?") return self.__initialized self.__initialized = S_OK() return self.__initialized
def initialize(self): if self.__initialized['OK']: return self.__initialized if not gConfig.useServerCertificate(): res = self.__getProxyID() else: res = self.__getCertificateID() if not res: self.__initialized = S_ERROR("Cannot locate client credentials") return self.__initialized retVal = gConfig.getOption("/DIRAC/Configuration/MasterServer") if not retVal['OK']: self.__initialized = S_ERROR( "Master server is not known. Is everything initialized?") return self.__initialized self.__rpcClient = RPCClient( gConfig.getValue("/DIRAC/Configuration/MasterServer", "")) self.__csMod = Modificator( self.__rpcClient, "%s - %s - %s" % (self.__userGroup, self.__userDN, Time.dateTime().strftime("%Y-%m-%d %H:%M:%S"))) retVal = self.downloadCSData() if not retVal['OK']: self.__initialized = S_ERROR( "Can not download the remote cfg. Is everything initialized?") return self.__initialized self.__initialized = S_OK() return self.__initialized
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 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 is 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(ConfigurationClient(url=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 __getRemoteConfiguration(self): rpcClient = getRPCClient(gConfig.getValue("/DIRAC/Configuration/MasterServer", "Configuration/Server")) modCfg = Modificator(rpcClient) retVal = modCfg.loadFromRemote() if retVal["OK"]: session["cfgData"] = str(modCfg) session["csName"] = "%s Configuration" % (modCfg.getValue("/DIRAC/VirtualOrganization")) session.save() c.cfgData = modCfg.cfgData c.csName = session["csName"] return retVal
def __getRemoteConfiguration(self, funcName): rpcClient = RPCClient(gConfig.getValue("/DIRAC/Configuration/MasterServer", "Configuration/Server")) modCfg = Modificator(rpcClient) retVal = modCfg.loadFromRemote() if not retVal[ 'OK' ]: return {"success":0, "op":"getSubnodes", "message":"The configuration cannot be read from the remote !"} self.__configData[ 'cfgData' ] = modCfg self.__configData[ 'strCfgData' ] = str(modCfg) version = str(modCfg.getCFG()["DIRAC"]["Configuration"]["Version"]) configName = str(modCfg.getCFG()["DIRAC"]["Configuration"]["Name"]) return {"success":1, "op":funcName, "version":version, "name":configName}
def __getRemoteConfiguration(self): rpcClient = getRPCClient( gConfig.getValue("/DIRAC/Configuration/MasterServer", "Configuration/Server")) modCfg = Modificator(rpcClient) retVal = modCfg.loadFromRemote() if retVal['OK']: session['cfgData'] = str(modCfg) session['csName'] = "%s Configuration" % ( modCfg.getValue("/DIRAC/VirtualOrganization")) session.save() c.cfgData = modCfg.cfgData c.csName = session['csName'] return retVal
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:
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 __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 __getModificator(self): rpcClient = getRPCClient( gConfig.getValue("/DIRAC/Configuration/MasterServer", "Configuration/Server")) commiter = "%s@%s - %s" % ( credentials.getUsername(), credentials.getSelectedGroup(), Time.dateTime().strftime("%Y-%m-%d %H:%M:%S")) return Modificator(rpcClient, commiter)
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 initialize(self): if not gConfig.useServerCertificate(): res = self.__getProxyID() else: res = self.__getCertificateID() if not res: return False retVal = gConfig.getOption("/DIRAC/Configuration/MasterServer") if not retVal['OK']: gLogger.warn( "Master server is not known. Is everything initialized?") return False self.__rpcClient = RPCClient( gConfig.getValue("/DIRAC/Configuration/MasterServer", "")) self.__csMod = Modificator( self.__rpcClient, "%s - %s" % (self.__userGroup, self.__userDN)) retVal = self.downloadCSData() if not retVal['OK']: gLogger.error( "Can not download the remote cfg. Is everything initialized?") return False return True
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 initialize( self ): if not gConfig.useServerCertificate(): res = self.__getProxyID() else: res = self.__getCertificateID() if not res: return False retVal = gConfig.getOption( "/DIRAC/Configuration/MasterServer" ) if not retVal[ 'OK' ]: gLogger.warn( "Master server is not known. Is everything initialized?" ) return False self.__rpcClient = RPCClient( gConfig.getValue( "/DIRAC/Configuration/MasterServer", "" ) ) self.__csMod = Modificator( self.__rpcClient, "%s - %s" % ( self.__userGroup, self.__userDN ) ) retVal = self.downloadCSData() if not retVal[ 'OK' ]: gLogger.error( "Can not download the remote cfg. Is everything initialized?" ) return False return True
def initialize( self ): if self.__initialized[ 'OK' ]: return self.__initialized if not gConfig.useServerCertificate(): res = self.__getProxyID() else: res = self.__getCertificateID() if not res: self.__initialized = S_ERROR( "Cannot locate client credentials" ) return self.__initialized retVal = gConfig.getOption( "/DIRAC/Configuration/MasterServer" ) if not retVal[ 'OK' ]: self.__initialized = S_ERROR( "Master server is not known. Is everything initialized?" ) return self.__initialized self.__rpcClient = RPCClient( gConfig.getValue( "/DIRAC/Configuration/MasterServer", "" ) ) self.__csMod = Modificator( self.__rpcClient, "%s - %s" % ( self.__userGroup, self.__userDN ) ) retVal = self.downloadCSData() if not retVal[ 'OK' ]: self.__initialized = S_ERROR( "Can not download the remote cfg. Is everything initialized?" ) return self.__initialized self.__initialized = S_OK() return self.__initialized
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()
class CSAPI: def __init__( self ): """ Initialization function """ self.__csModified = False self.__baseSecurity = "/Registry" self.__userDN = '' self.__userGroup = '' self.__rpcClient = None self.__csMod = None self.__initialized = S_ERROR( "Not initialized" ) self.initialize() if not self.__initialized[ 'OK' ]: gLogger.error( self.__initialized ) def __getProxyID( self ): proxyLocation = Locations.getProxyLocation() if not proxyLocation: gLogger.error( "No proxy found!" ) return False chain = X509Chain() if not chain.loadProxyFromFile( proxyLocation ): gLogger.error( "Can't read proxy!", proxyLocation ) return False retVal = chain.getIssuerCert() if not retVal[ 'OK' ]: gLogger.error( "Can't parse proxy!", retVal[ 'Message' ] ) return False idCert = retVal[ 'Value' ] self.__userDN = idCert.getSubjectDN()[ 'Value' ] self.__userGroup = idCert.getDIRACGroup()[ 'Value' ] return True def __getCertificateID( self ): certLocation = Locations.getHostCertificateAndKeyLocation() if not certLocation: gLogger.error( "No certificate found!" ) return False chain = X509Chain() retVal = chain.loadChainFromFile( certLocation[ 0 ] ) if not retVal[ 'OK' ]: gLogger.error( "Can't parse certificate!", retVal[ 'Message' ] ) return False idCert = chain.getIssuerCert()[ 'Value' ] self.__userDN = idCert.getSubjectDN()[ 'Value' ] self.__userGroup = 'host' return True def initialize( self ): if self.__initialized[ 'OK' ]: return self.__initialized if not gConfig.useServerCertificate(): res = self.__getProxyID() else: res = self.__getCertificateID() if not res: self.__initialized = S_ERROR( "Cannot locate client credentials" ) return self.__initialized retVal = gConfig.getOption( "/DIRAC/Configuration/MasterServer" ) if not retVal[ 'OK' ]: self.__initialized = S_ERROR( "Master server is not known. Is everything initialized?" ) return self.__initialized self.__rpcClient = RPCClient( gConfig.getValue( "/DIRAC/Configuration/MasterServer", "" ) ) self.__csMod = Modificator( self.__rpcClient, "%s - %s" % ( self.__userGroup, self.__userDN ) ) retVal = self.downloadCSData() if not retVal[ 'OK' ]: self.__initialized = S_ERROR( "Can not download the remote cfg. Is everything initialized?" ) return self.__initialized self.__initialized = S_OK() return self.__initialized def downloadCSData( self ): if not self.__csMod: return S_ERROR( "CSAPI not yet initialized" ) result = self.__csMod.loadFromRemote() if not result[ 'OK' ]: return result self.__csModified = False self.__csMod.updateGConfigurationData() return S_OK() def listUsers( self , group = False ): if not self.__initialized[ 'OK' ]: return self.__initialized if not group: return S_OK( self.__csMod.getSections( "%s/Users" % self.__baseSecurity ) ) else: users = self.__csMod.getValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) ) if not users: return S_OK( [] ) else: return S_OK( List.fromChar( users ) ) def listHosts( self ): if not self.__initialized[ 'OK' ]: return self.__initialized return S_OK( self.__csMod.getSections( "%s/Hosts" % self.__baseSecurity ) ) def describeUsers( self, users = False ): if not self.__initialized[ 'OK' ]: return self.__initialized return S_OK( self.__describeEntity( users ) ) def describeHosts( self, hosts = False ): if not self.__initialized[ 'OK' ]: return self.__initialized return S_OK( self.__describeEntity( hosts, True ) ) def __describeEntity( self, mask, hosts = False ): if hosts: csSection = "%s/Hosts" % self.__baseSecurity else: csSection = "%s/Users" % self.__baseSecurity if mask: entities = [ entity for entity in self.__csMod.getSections( csSection ) if entity in mask ] else: entities = self.__csMod.getSections( csSection ) entitiesDict = {} for entity in entities: entitiesDict[ entity ] = {} for option in self.__csMod.getOptions( "%s/%s" % ( csSection, entity ) ): entitiesDict[ entity ][ option ] = self.__csMod.getValue( "%s/%s/%s" % ( csSection, entity, option ) ) if not hosts: groupsDict = self.describeGroups()[ 'Value' ] entitiesDict[ entity ][ 'Groups' ] = [] for group in groupsDict: if 'Users' in groupsDict[ group ] and entity in groupsDict[ group ][ 'Users' ]: entitiesDict[ entity ][ 'Groups' ].append( group ) entitiesDict[ entity ][ 'Groups' ].sort() return entitiesDict def listGroups( self ): """ List all groups """ if not self.__initialized[ 'OK' ]: return self.__initialized return S_OK( self.__csMod.getSections( "%s/Groups" % self.__baseSecurity ) ) def describeGroups( self, mask = False ): """ List all groups that are in the mask (or all if no mask) with their properties """ if not self.__initialized[ 'OK' ]: return self.__initialized groups = [ group for group in self.__csMod.getSections( "%s/Groups" % self.__baseSecurity ) if not mask or ( mask and group in mask ) ] groupsDict = {} for group in groups: groupsDict[ group ] = {} for option in self.__csMod.getOptions( "%s/Groups/%s" % ( self.__baseSecurity, group ) ): groupsDict[ group ][ option ] = self.__csMod.getValue( "%s/Groups/%s/%s" % ( self.__baseSecurity, group, option ) ) if option in ( "Users", "Properties" ): groupsDict[ group ][ option ] = List.fromChar( groupsDict[ group ][ option ] ) return S_OK( groupsDict ) def deleteUsers( self, users ): """ Delete a user/s can receive as a param either a string or a list """ if not self.__initialized[ 'OK' ]: return self.__initialized if type( users ) == types.StringType: users = [ users ] usersData = self.describeUsers( users )['Value'] for username in users: if not username in usersData: gLogger.warn( "User %s does not exist" ) continue userGroups = usersData[ username ][ 'Groups' ] for group in userGroups: self.__removeUserFromGroup( group, username ) gLogger.info( "Deleted user %s from group %s" % ( username, group ) ) self.__csMod.removeSection( "%s/Users/%s" % ( self.__baseSecurity, username ) ) gLogger.info( "Deleted user %s" % username ) self.__csModified = True return S_OK( True ) def __removeUserFromGroup( self, group, username ): """ Remove user from a group """ usersInGroup = self.__csMod.getValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) ) if usersInGroup != None: userList = List.fromChar( usersInGroup, "," ) userPos = userList.index( username ) userList.pop( userPos ) self.__csMod.setOptionValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ), ",".join( userList ) ) def __addUserToGroup( self, group, username ): """ Add user to a group """ usersInGroup = self.__csMod.getValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) ) if usersInGroup != None: userList = List.fromChar( usersInGroup ) if username not in userList: userList.append( username ) self.__csMod.setOptionValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ), ",".join( userList ) ) else: gLogger.warn( "User %s is already in group %s" % ( username, group ) ) def addUser( self, username, properties ): """ Add a user to the cs - username - properties is a dict with keys: - DN - groups - <extra params> Returns True/False """ if not self.__initialized[ 'OK' ]: return self.__initialized for prop in ( "DN", "Groups" ): if prop not in properties: gLogger.error( "Missing %s property for user %s" % ( prop, username ) ) return S_OK( False ) if username in self.listUsers()['Value']: gLogger.error( "User %s is already registered" % username ) return S_OK( False ) groups = self.listGroups()['Value'] for userGroup in properties[ 'Groups' ]: if not userGroup in groups: gLogger.error( "User %s group %s is not a valid group" % ( username, userGroup ) ) return S_OK( False ) self.__csMod.createSection( "%s/Users/%s" % ( self.__baseSecurity, username ) ) for prop in properties: if prop == "Groups": continue self.__csMod.setOptionValue( "%s/Users/%s/%s" % ( self.__baseSecurity, username, prop ), properties[ prop ] ) for userGroup in properties[ 'Groups' ]: gLogger.info( "Added user %s to group %s" % ( username, userGroup ) ) self.__addUserToGroup( userGroup, username ) gLogger.info( "Registered user %s" % username ) self.__csModified = True return S_OK( True ) def modifyUser( self, username, properties, createIfNonExistant = False ): """ Modify a user - username - properties is a dict with keys: - DN - Groups - <extra params> Returns True/False """ if not self.__initialized[ 'OK' ]: return self.__initialized modifiedUser = False userData = self.describeUsers( [ username ] )['Value'] if username not in userData: if createIfNonExistant: gLogger.info( "Registering user %s" % username ) return self.addUser( username, properties ) gLogger.error( "User %s is not registered" % username ) return S_OK( False ) for prop in properties: if prop == "Groups": continue prevVal = self.__csMod.getValue( "%s/Users/%s/%s" % ( self.__baseSecurity, username, prop ) ) if not prevVal or prevVal != properties[ prop ]: gLogger.info( "Setting %s property for user %s to %s" % ( prop, username, properties[ prop ] ) ) self.__csMod.setOptionValue( "%s/Users/%s/%s" % ( self.__baseSecurity, username, prop ), properties[ prop ] ) modifiedUser = True if 'Groups' in properties: groups = self.listGroups()['Value'] for userGroup in properties[ 'Groups' ]: if not userGroup in groups: gLogger.error( "User %s group %s is not a valid group" % ( username, userGroup ) ) return S_OK( False ) groupsToBeDeletedFrom = [] groupsToBeAddedTo = [] for prevGroup in userData[ username ][ 'Groups' ]: if prevGroup not in properties[ 'Groups' ]: groupsToBeDeletedFrom.append( prevGroup ) modifiedUser = True for newGroup in properties[ 'Groups' ]: if newGroup not in userData[ username ][ 'Groups' ]: groupsToBeAddedTo.append( newGroup ) modifiedUser = True for group in groupsToBeDeletedFrom: self.__removeUserFromGroup( group, username ) gLogger.info( "Removed user %s from group %s" % ( username, group ) ) for group in groupsToBeAddedTo: self.__addUserToGroup( group, username ) gLogger.info( "Added user %s to group %s" % ( username, group ) ) if modifiedUser: gLogger.info( "Modified user %s" % username ) self.__csModified = True else: gLogger.info( "Nothing to modify for user %s" % username ) return S_OK( True ) def addGroup( self, groupname, properties ): """ Add a group to the cs - groupname - properties is a dict with keys: - Users - Properties - <extra params> Returns True/False """ if not self.__initialized[ 'OK' ]: return self.__initialized if groupname in self.listGroups()['Value']: gLogger.error( "Group %s is already registered" % groupname ) return S_OK( False ) self.__csMod.createSection( "%s/Groups/%s" % ( self.__baseSecurity, groupname ) ) for prop in properties: self.__csMod.setOptionValue( "%s/Groups/%s/%s" % ( self.__baseSecurity, groupname, prop ), properties[ prop ] ) gLogger.info( "Registered group %s" % groupname ) self.__csModified = True return S_OK( True ) def modifyGroup( self, groupname, properties, createIfNonExistant = False ): """ Modify a user - groupname - properties is a dict with keys: - Users - Properties - <extra params> Returns True/False """ if not self.__initialized[ 'OK' ]: return self.__initialized modifiedGroup = False groupData = self.describeGroups( [ groupname ] )['Value'] if groupname not in groupData: if createIfNonExistant: gLogger.info( "Registering group %s" % groupname ) return self.addGroup( groupname, properties ) gLogger.error( "Group %s is not registered" % groupname ) return S_OK( False ) for prop in properties: prevVal = self.__csMod.getValue( "%s/Groups/%s/%s" % ( self.__baseSecurity, groupname, prop ) ) if not prevVal or prevVal != properties[ prop ]: gLogger.info( "Setting %s property for group %s to %s" % ( prop, groupname, properties[ prop ] ) ) self.__csMod.setOptionValue( "%s/Groups/%s/%s" % ( self.__baseSecurity, groupname, prop ), properties[ prop ] ) modifiedGroup = True if modifiedGroup: gLogger.info( "Modified group %s" % groupname ) self.__csModified = True else: gLogger.info( "Nothing to modify for group %s" % groupname ) return S_OK( True ) def addHost( self, hostname, properties ): """ Add a host to the cs - hostname - properties is a dict with keys: - DN - Properties - <extra params> Returns True/False """ if not self.__initialized[ 'OK' ]: return self.__initialized for prop in ( "DN", ): if prop not in properties: gLogger.error( "Missing %s property for host %s" % ( prop, hostname ) ) return S_OK( False ) if hostname in self.listHosts()['Value']: gLogger.error( "Host %s is already registered" % hostname ) return S_OK( False ) self.__csMod.createSection( "%s/Hosts/%s" % ( self.__baseSecurity, hostname ) ) for prop in properties: self.__csMod.setOptionValue( "%s/Hosts/%s/%s" % ( self.__baseSecurity, hostname, prop ), properties[ prop ] ) gLogger.info( "Registered host %s" % hostname ) self.__csModified = True return S_OK( True ) def modifyHost( self, hostname, properties, createIfNonExistant = False ): """ Modify a user - hostname - properties is a dict with keys: - DN - Properties - <extra params> Returns True/False """ if not self.__initialized[ 'OK' ]: return self.__initialized modifiedHost = False hostData = self.describeHosts( [ hostname ] )['Value'] if hostname not in hostData: if createIfNonExistant: gLogger.info( "Registering host %s" % hostname ) return self.addHost( hostname, properties ) gLogger.error( "Host %s is not registered" % hostname ) return S_OK( False ) for prop in properties: prevVal = self.__csMod.getValue( "%s/Hosts/%s/%s" % ( self.__baseSecurity, hostname, prop ) ) if not prevVal or prevVal != properties[ prop ]: gLogger.info( "Setting %s property for host %s to %s" % ( prop, hostname, properties[ prop ] ) ) self.__csMod.setOptionValue( "%s/Hosts/%s/%s" % ( self.__baseSecurity, hostname, prop ), properties[ prop ] ) modifiedHost = True if modifiedHost: gLogger.info( "Modified host %s" % hostname ) self.__csModified = True else: gLogger.info( "Nothing to modify for host %s" % hostname ) return S_OK( True ) def syncUsersWithCFG( self, usersCFG ): """ Sync users with the cfg contents. Usernames have to be sections containing DN, Groups, and extra properties as parameters """ if not self.__initialized[ 'OK' ]: return self.__initialized done = True for user in usersCFG.listSections(): properties = {} propList = usersCFG[ user ].listOptions() for prop in propList: if prop == "Groups": properties[ prop ] = List.fromChar( usersCFG[ user ][ prop ] ) else: properties[ prop ] = usersCFG[ user ][ prop ] if not self.modifyUser( user, properties, createIfNonExistant = True ): done = False return S_OK( done ) def sortUsersAndGroups( self ): self.__csMod.sortAlphabetically( "%s/Users" % self.__baseSecurity ) self.__csMod.sortAlphabetically( "%s/Hosts" % self.__baseSecurity ) for group in self.__csMod.getSections( "%s/Groups" % self.__baseSecurity ): usersOptionPath = "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) users = self.__csMod.getValue( usersOptionPath ) usersList = List.fromChar( users ) usersList.sort() sortedUsers = ", ".join( usersList ) if users != sortedUsers: self.__csMod.setOptionValue( usersOptionPath, sortedUsers ) def checkForUnexistantUsersInGroups( self ): allUsers = self.__csMod.getSections( "%s/Users" % self.__baseSecurity ) allGroups = self.__csMod.getSections( "%s/Groups" % self.__baseSecurity ) for group in allGroups: usersInGroup = self.__csMod.getValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) ) if usersInGroup: filteredUsers = [] usersInGroup = List.fromChar( usersInGroup ) for user in usersInGroup: if user in allUsers: filteredUsers.append( user ) self.__csMod.setOptionValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ), ",".join( filteredUsers ) ) def commitChanges( self, sortUsers = True ): if not self.__initialized[ 'OK' ]: return self.__initialized if self.__csModified: self.checkForUnexistantUsersInGroups() if sortUsers: self.sortUsersAndGroups() retVal = self.__csMod.commit() if not retVal[ 'OK' ]: gLogger.error( "Can't commit new data: %s" % retVal[ 'Message' ] ) return retVal return self.downloadCSData() return S_OK() def commit( self ): """ Commit the accumulated changes to the CS server """ if not self.__initialized[ 'OK' ]: return self.__initialized if self.__csModified: retVal = self.__csMod.commit() if not retVal[ 'OK' ]: gLogger.error( "Can't commit new data: %s" % retVal[ 'Message' ] ) return retVal return self.downloadCSData() return S_OK() def mergeFromCFG( self, cfg ): """ Merge the internal CFG data with the input """ if not self.__initialized[ 'OK' ]: return self.__initialized self.__csMod.mergeFromCFG( cfg ) self.__csModified = True return S_OK() def modifyValue( self, optionPath, newValue ): """Modify an existing value at the specified options path. """ if not self.__initialized[ 'OK' ]: return self.__initialized prevVal = self.__csMod.getValue( optionPath ) if not prevVal: return S_ERROR( 'Trying to set %s to %s but option does not exist' % ( optionPath, newValue ) ) gLogger.verbose( "Changing %s from \n%s \nto \n%s" % ( optionPath, prevVal, newValue ) ) self.__csMod.setOptionValue( optionPath, newValue ) self.__csModified = True return S_OK( 'Modified %s' % optionPath ) def setOption( self, optionPath, optionValue ): """Create an option at the specified path. """ if not self.__initialized[ 'OK' ]: return self.__initialized self.__csMod.setOptionValue( optionPath, optionValue ) self.__csModified = True return S_OK( 'Created new option %s = %s' % ( optionPath, optionValue ) ) def setOptionComment( self, optionPath, comment ): """Create an option at the specified path. """ if not self.__initialized[ 'OK' ]: return self.__initialized self.__csMod.setComment( optionPath, comment ) self.__csModified = True return S_OK( 'Set option comment %s : %s' % ( optionPath, comment ) ) def delOption( self, optionPath ): """ Delete an option """ if not self.__initialized[ 'OK' ]: return self.__initialized if not self.__csMod.removeOption( optionPath ): return S_ERROR( "Couldn't delete option %s" % optionPath ) self.__csModified = True return S_OK( 'Deleted option %s' % ( optionPath ) ) def createSection( self, sectionPath, comment = "" ): """ Create a new section """ if not self.__initialized[ 'OK' ]: return self.__initialized self.__csMod.createSection( sectionPath ) self.__csModified = True if comment: self.__csMod.setComment( sectionPath, comment ) return S_OK() def delSection( self, sectionPath ): """ Delete a section """ if not self.__initialized[ 'OK' ]: return self.__initialized if not self.__csMod.removeSection( sectionPath ): return S_ERROR( "Could not delete section %s " % sectionPath ) self.__csModified = True return S_OK() def mergeCFGUnderSection( self, sectionPath, cfg ): """ Merge the given cfg under a certain section """ if not self.__initialized[ 'OK' ]: return self.__initialized result = self.createSection( sectionPath ) if not result[ 'OK' ]: return result if not self.__csMod.mergeSectionFromCFG( sectionPath, cfg ): return S_ERROR( "Could not merge cfg into section %s" % sectionPath ) self.__csModified = True return S_OK() def mergeWithCFG( self, cfg ): """ Merge the given cfg with the current config """ if not self.__initialized[ 'OK' ]: return self.__initialized self.__csMod.mergeFromCFG( cfg ) self.__csModified = True return S_OK() def getCurrentCFG( self ): """ Get the current CFG as it is """ if not self.__initialized[ 'OK' ]: return self.__initialized return S_OK( self.__csMod.getCFG() )
class CSAPI(object): """ CSAPI objects need an initialization phase """ def __init__(self): """ Initialization function """ self.csModified = False self.__baseSecurity = "/Registry" self.__baseResources = "/Resources" self.__userDN = '' self.__userGroup = '' self.__rpcClient = None self.__csMod = None self.__initialized = S_ERROR("Not initialized") self.initialize() if not self.__initialized['OK']: gLogger.error(self.__initialized) def __getProxyID(self): proxyLocation = Locations.getProxyLocation() if not proxyLocation: gLogger.error("No proxy found!") return False chain = X509Chain() if not chain.loadProxyFromFile(proxyLocation): gLogger.error("Can't read proxy!", proxyLocation) return False retVal = chain.getIssuerCert() if not retVal['OK']: gLogger.error("Can't parse proxy!", retVal['Message']) return False idCert = retVal['Value'] self.__userDN = idCert.getSubjectDN()['Value'] self.__userGroup = chain.getDIRACGroup()['Value'] return True def __getCertificateID(self): certLocation = Locations.getHostCertificateAndKeyLocation() if not certLocation: gLogger.error("No certificate found!") return False chain = X509Chain() retVal = chain.loadChainFromFile(certLocation[0]) if not retVal['OK']: gLogger.error("Can't parse certificate!", retVal['Message']) return False idCert = chain.getIssuerCert()['Value'] self.__userDN = idCert.getSubjectDN()['Value'] self.__userGroup = 'host' return True def initialize(self): if self.__initialized['OK']: return self.__initialized if not gConfig.useServerCertificate(): res = self.__getProxyID() else: res = self.__getCertificateID() if not res: self.__initialized = S_ERROR("Cannot locate client credentials") return self.__initialized retVal = gConfig.getOption("/DIRAC/Configuration/MasterServer") if not retVal['OK']: self.__initialized = S_ERROR( "Master server is not known. Is everything initialized?") return self.__initialized self.__rpcClient = RPCClient( gConfig.getValue("/DIRAC/Configuration/MasterServer", "")) self.__csMod = Modificator( self.__rpcClient, "%s - %s - %s" % (self.__userGroup, self.__userDN, Time.dateTime().strftime("%Y-%m-%d %H:%M:%S"))) retVal = self.downloadCSData() if not retVal['OK']: self.__initialized = S_ERROR( "Can not download the remote cfg. Is everything initialized?") return self.__initialized self.__initialized = S_OK() return self.__initialized def downloadCSData(self): if not self.__csMod: return S_ERROR("CSAPI not yet initialized") result = self.__csMod.loadFromRemote() if not result['OK']: return result self.csModified = False self.__csMod.updateGConfigurationData() return S_OK() # Resources-related methods ######################################### def addSite(self, siteName, optionsDict=None): """ Adds a new site to the CS. A site is a container for services, so after calling this function, at least addCEtoSite() should be called. :param str siteName: FQN of the site (e.g. LCG.CERN.ch) :param dict optionsDict: optional dictionary of options :returns: S_OK/S_ERROR structure """ if not self.__initialized['OK']: return self.__initialized self.__csMod.createSection(cfgPath(self.__baseResources, 'Sites')) self.__csMod.createSection( cfgPath(self.__baseResources, 'Sites', siteName.split('.')[0])) self.__csMod.createSection( cfgPath(self.__baseResources, 'Sites', siteName.split('.')[0], siteName)) # add options if requested if optionsDict is not None: for option, optionValue in optionsDict.items( ): # can be an iterator self.__csMod.setOptionValue( cfgPath(self.__baseResources, 'Sites', siteName.split('.')[0], option), optionValue) self.csModified = True return S_OK(True) def addCEtoSite(self, siteName, ceName, optionsDict=None): """ Adds a new CE to a site definition in the CS. A CE normally has queues, so addQueueToCE should be called after this one. :param str siteName: FQN of the site (e.g. LCG.CERN.ch) :param str ceName: FQN of the CE (e.g. ce503.cern.ch) :param dict optionsDict: optional dictionary of options :returns: S_OK/S_ERROR structure """ res = getSites() if not res['OK']: return res if siteName not in res['Value']: res = self.addSite(siteName) if not res['OK']: return res # CSAPI.createSection() always returns S_OK even if the section already exists self.__csMod.createSection( cfgPath(self.__baseResources, 'Sites', siteName.split('.')[0], siteName, 'CEs')) self.__csMod.createSection( cfgPath(self.__baseResources, 'Sites', siteName.split('.')[0], siteName, 'CEs', ceName)) # add options if requested if optionsDict is not None: for option, optionValue in optionsDict.items( ): # can be an iterator self.__csMod.setOptionValue( cfgPath(self.__baseResources, 'Sites', siteName.split('.')[0], siteName, 'CEs', ceName, option), optionValue) self.csModified = True return S_OK(True) def addQueueToCE(self, ceName, queueName, optionsDict=None): """ Adds a new queue to a CE definition in the CS. :param str ceName: FQN of the CE (e.g. ce503.cern.ch) :param str queueName: name of the queue (e.g. ce503.cern.ch-condor) :param dict optionsDict: optional dictionary of options :returns: S_OK/S_ERROR structure """ res = getCESiteMapping(ceName) if not res['OK']: return res if ceName not in res['Value']: return S_ERROR("CE does not exist") siteName = res['Value'][ceName] # CSAPI.createSection() always returns S_OK even if the section already exists self.__csMod.createSection( cfgPath(self.__baseResources, 'Sites', siteName.split('.')[0], siteName, 'CEs', ceName, 'Queues', queueName)) # add options if requested if optionsDict is not None: for option, optionValue in optionsDict.items( ): # can be an iterator self.__csMod.setOptionValue( cfgPath(self.__baseResources, 'Sites', siteName.split('.')[0], siteName, 'CEs', ceName, 'Queues', queueName, option), optionValue) self.csModified = True return S_OK(True) # Registry-related methods ######################################### def listUsers(self, group=False): if not self.__initialized['OK']: return self.__initialized if not group: return S_OK( self.__csMod.getSections("%s/Users" % self.__baseSecurity)) users = self.__csMod.getValue("%s/Groups/%s/Users" % (self.__baseSecurity, group)) if not users: return S_OK([]) return S_OK(List.fromChar(users)) def listHosts(self): if not self.__initialized['OK']: return self.__initialized return S_OK(self.__csMod.getSections("%s/Hosts" % self.__baseSecurity)) def describeUsers(self, users=None): """ describe users by nickname :param list users: list of users' nicknames :return: a S_OK(description) of the users in input """ if users is None: users = [] if not self.__initialized['OK']: return self.__initialized return S_OK(self.__describeEntity(users)) def describeHosts(self, hosts=None): if hosts is None: hosts = [] if not self.__initialized['OK']: return self.__initialized return S_OK(self.__describeEntity(hosts, True)) def __describeEntity(self, mask, hosts=False): if hosts: csSection = "%s/Hosts" % self.__baseSecurity else: csSection = "%s/Users" % self.__baseSecurity if mask: entities = [ entity for entity in self.__csMod.getSections(csSection) if entity in mask ] else: entities = self.__csMod.getSections(csSection) entitiesDict = {} for entity in entities: entitiesDict[entity] = {} for option in self.__csMod.getOptions("%s/%s" % (csSection, entity)): entitiesDict[entity][option] = self.__csMod.getValue( "%s/%s/%s" % (csSection, entity, option)) if not hosts: groupsDict = self.describeGroups()['Value'] entitiesDict[entity]['Groups'] = [] for group in groupsDict: if 'Users' in groupsDict[group] and entity in groupsDict[ group]['Users']: entitiesDict[entity]['Groups'].append(group) entitiesDict[entity]['Groups'].sort() return entitiesDict def listGroups(self): """ List all groups """ if not self.__initialized['OK']: return self.__initialized return S_OK(self.__csMod.getSections("%s/Groups" % self.__baseSecurity)) def describeGroups(self, mask=None): """ List all groups that are in the mask (or all if no mask) with their properties """ if mask is None: mask = [] if not self.__initialized['OK']: return self.__initialized groups = [ group for group in self.__csMod.getSections("%s/Groups" % self.__baseSecurity) if not mask or (mask and group in mask) ] groupsDict = {} for group in groups: groupsDict[group] = {} for option in self.__csMod.getOptions( "%s/Groups/%s" % (self.__baseSecurity, group)): groupsDict[group][option] = self.__csMod.getValue( "%s/Groups/%s/%s" % (self.__baseSecurity, group, option)) if option in ("Users", "Properties"): groupsDict[group][option] = List.fromChar( groupsDict[group][option]) return S_OK(groupsDict) def deleteUsers(self, users): """ Delete a user/s can receive as a param either a string or a list """ if not self.__initialized['OK']: return self.__initialized if isinstance(users, six.string_types): users = [users] usersData = self.describeUsers(users)['Value'] for username in users: if username not in usersData: gLogger.warn("User %s does not exist") continue userGroups = usersData[username]['Groups'] for group in userGroups: self.__removeUserFromGroup(group, username) gLogger.info("Deleted user %s from group %s" % (username, group)) self.__csMod.removeSection("%s/Users/%s" % (self.__baseSecurity, username)) gLogger.info("Deleted user %s" % username) self.csModified = True return S_OK(True) def __removeUserFromGroup(self, group, username): """ Remove user from a group """ usersInGroup = self.__csMod.getValue("%s/Groups/%s/Users" % (self.__baseSecurity, group)) if usersInGroup is not None: userList = List.fromChar(usersInGroup, ",") userPos = userList.index(username) userList.pop(userPos) self.__csMod.setOptionValue( "%s/Groups/%s/Users" % (self.__baseSecurity, group), ",".join(userList)) def __addUserToGroup(self, group, username): """ Add user to a group """ usersInGroup = self.__csMod.getValue("%s/Groups/%s/Users" % (self.__baseSecurity, group)) if usersInGroup is not None: userList = List.fromChar(usersInGroup) if username not in userList: userList.append(username) self.__csMod.setOptionValue( "%s/Groups/%s/Users" % (self.__baseSecurity, group), ",".join(userList)) else: gLogger.warn("User %s is already in group %s" % (username, group)) def addUser(self, username, properties): """ Add a user to the cs :param str username: username :param dict properties: dictionary describing user properties: - DN - groups - <extra params> :return: True/False """ if not self.__initialized['OK']: return self.__initialized for prop in ("DN", "Groups"): if prop not in properties: gLogger.error("Missing property for user", "%s: %s" % (prop, username)) return S_OK(False) if username in self.listUsers()['Value']: gLogger.error("User is already registered", username) return S_OK(False) groups = self.listGroups()['Value'] for userGroup in properties['Groups']: if userGroup not in groups: gLogger.error("User group is not a valid group", "%s %s" % (username, userGroup)) return S_OK(False) self.__csMod.createSection("%s/Users/%s" % (self.__baseSecurity, username)) for prop in properties: if prop == "Groups": continue self.__csMod.setOptionValue( "%s/Users/%s/%s" % (self.__baseSecurity, username, prop), properties[prop]) for userGroup in properties['Groups']: gLogger.info("Added user %s to group %s" % (username, userGroup)) self.__addUserToGroup(userGroup, username) gLogger.info("Registered user %s" % username) self.csModified = True return S_OK(True) def modifyUser(self, username, properties, createIfNonExistant=False): """ Modify a user :param str username: group name :param dict properties: dictionary describing user properties: - DN - Groups - <extra params> :param bool createIfNonExistant: if true, registers the users if it did not exist :return: S_OK, Value = True/False """ if not self.__initialized['OK']: return self.__initialized modifiedUser = False userData = self.describeUsers([username])['Value'] if username not in userData: if createIfNonExistant: gLogger.info("Registering user %s" % username) return self.addUser(username, properties) gLogger.error("User is not registered", username) return S_OK(False) for prop in properties: if prop == "Groups": continue prevVal = self.__csMod.getValue( "%s/Users/%s/%s" % (self.__baseSecurity, username, prop)) if not prevVal or prevVal != properties[prop]: gLogger.info("Setting %s property for user %s to %s" % (prop, username, properties[prop])) self.__csMod.setOptionValue( "%s/Users/%s/%s" % (self.__baseSecurity, username, prop), properties[prop]) modifiedUser = True if 'Groups' in properties: groups = self.listGroups()['Value'] for userGroup in properties['Groups']: if userGroup not in groups: gLogger.error("User group is not a valid group", "%s %s" % (username, userGroup)) return S_OK(False) groupsToBeDeletedFrom = [] groupsToBeAddedTo = [] for prevGroup in userData[username]['Groups']: if prevGroup not in properties['Groups']: groupsToBeDeletedFrom.append(prevGroup) modifiedUser = True for newGroup in properties['Groups']: if newGroup not in userData[username]['Groups']: groupsToBeAddedTo.append(newGroup) modifiedUser = True for group in groupsToBeDeletedFrom: self.__removeUserFromGroup(group, username) gLogger.info("Removed user %s from group %s" % (username, group)) for group in groupsToBeAddedTo: self.__addUserToGroup(group, username) gLogger.info("Added user %s to group %s" % (username, group)) modified = False if modifiedUser: modified = True gLogger.info("Modified user %s" % username) self.csModified = True else: gLogger.info("Nothing to modify for user %s" % username) return S_OK(modified) def addGroup(self, groupname, properties): """ Add a group to the cs :param str groupname: group name :param dict properties: dictionary describing group properties: - Users - Properties - <extra params> :return: S_OK, Value = True/False """ if not self.__initialized['OK']: return self.__initialized if groupname in self.listGroups()['Value']: gLogger.error("Group is already registered", groupname) return S_OK(False) self.__csMod.createSection("%s/Groups/%s" % (self.__baseSecurity, groupname)) for prop in properties: self.__csMod.setOptionValue( "%s/Groups/%s/%s" % (self.__baseSecurity, groupname, prop), properties[prop]) gLogger.info("Registered group %s" % groupname) self.csModified = True return S_OK(True) def modifyGroup(self, groupname, properties, createIfNonExistant=False): """ Modify a group :param str groupname: group name :param dict properties: dictionary describing group properties: - Users - Properties - <extra params> :param bool createIfNonExistant: if true, creates the group if it did not exist :return: S_OK, Value = True/False """ if not self.__initialized['OK']: return self.__initialized modifiedGroup = False groupData = self.describeGroups([groupname])['Value'] if groupname not in groupData: if createIfNonExistant: gLogger.info("Registering group %s" % groupname) return self.addGroup(groupname, properties) gLogger.error("Group is not registered", groupname) return S_OK(False) for prop in properties: prevVal = self.__csMod.getValue( "%s/Groups/%s/%s" % (self.__baseSecurity, groupname, prop)) if not prevVal or prevVal != properties[prop]: gLogger.info("Setting %s property for group %s to %s" % (prop, groupname, properties[prop])) self.__csMod.setOptionValue( "%s/Groups/%s/%s" % (self.__baseSecurity, groupname, prop), properties[prop]) modifiedGroup = True if modifiedGroup: gLogger.info("Modified group %s" % groupname) self.csModified = True else: gLogger.info("Nothing to modify for group %s" % groupname) return S_OK(True) def addHost(self, hostname, properties): """ Add a host to the cs :param str hostname: host name :param dict properties: dictionary describing host properties: - DN - Properties - <extra params> :return: S_OK, Value = True/False """ if not self.__initialized['OK']: return self.__initialized for prop in ("DN", ): if prop not in properties: gLogger.error("Missing property for host", "%s %s" % (prop, hostname)) return S_OK(False) if hostname in self.listHosts()['Value']: gLogger.error("Host is already registered", hostname) return S_OK(False) self.__csMod.createSection("%s/Hosts/%s" % (self.__baseSecurity, hostname)) for prop in properties: self.__csMod.setOptionValue( "%s/Hosts/%s/%s" % (self.__baseSecurity, hostname, prop), properties[prop]) gLogger.info("Registered host %s" % hostname) self.csModified = True return S_OK(True) def addShifter(self, shifters=None): """ Adds or modify one or more shifters. Also, adds the shifter section in case this is not present. Shifter identities are used in several places, mostly for running agents :param dict shifters: has to be in the form {'ShifterRole':{'User':'******', 'Group':'aDIRACGroup'}} :return: S_OK/S_ERROR """ def getOpsSection(): """ Where is the shifters section? """ vo = CSGlobals.getVO() setup = CSGlobals.getSetup() if vo: res = gConfig.getSections('/Operations/%s/%s/Shifter' % (vo, setup)) if res['OK']: return S_OK('/Operations/%s/%s/Shifter' % (vo, setup)) res = gConfig.getSections('/Operations/%s/Defaults/Shifter' % vo) if res['OK']: return S_OK('/Operations/%s/Defaults/Shifter' % vo) else: res = gConfig.getSections('/Operations/%s/Shifter' % setup) if res['OK']: return S_OK('/Operations/%s/Shifter' % setup) res = gConfig.getSections('/Operations/Defaults/Shifter') if res['OK']: return S_OK('/Operations/Defaults/Shifter') return S_ERROR("No shifter section") if shifters is None: shifters = {} if not self.__initialized['OK']: return self.__initialized # get current shifters opsH = Operations() currentShifterRoles = opsH.getSections('Shifter') if not currentShifterRoles['OK']: # we assume the shifter section is not present currentShifterRoles = [] else: currentShifterRoles = currentShifterRoles['Value'] currentShiftersDict = {} for currentShifterRole in currentShifterRoles: currentShifter = opsH.getOptionsDict('Shifter/%s' % currentShifterRole) if not currentShifter['OK']: return currentShifter currentShifter = currentShifter['Value'] currentShiftersDict[currentShifterRole] = currentShifter # Removing from shifters what does not need to be changed for sRole in shifters.keys(): # note the pop below if sRole in currentShiftersDict: if currentShiftersDict[sRole] == shifters[sRole]: shifters.pop(sRole) # get shifters section to modify section = getOpsSection() # Is this section present? if not section['OK']: if section['Message'] == "No shifter section": gLogger.warn(section['Message']) gLogger.info("Adding shifter section") vo = CSGlobals.getVO() if vo: section = '/Operations/%s/Defaults/Shifter' % vo else: section = '/Operations/Defaults/Shifter' res = self.__csMod.createSection(section) if not res: gLogger.error("Section %s not created" % section) return S_ERROR("Section %s not created" % section) else: gLogger.error(section['Message']) return section else: section = section['Value'] # add or modify shifters for shifter in shifters: self.__csMod.removeSection(section + '/' + shifter) self.__csMod.createSection(section + '/' + shifter) self.__csMod.createSection(section + '/' + shifter + '/' + 'User') self.__csMod.createSection(section + '/' + shifter + '/' + 'Group') self.__csMod.setOptionValue(section + '/' + shifter + '/' + 'User', shifters[shifter]['User']) self.__csMod.setOptionValue( section + '/' + shifter + '/' + 'Group', shifters[shifter]['Group']) self.csModified = True return S_OK(True) def modifyHost(self, hostname, properties, createIfNonExistant=False): """ Modify a host :param str hostname: hostname name :param dict properties: dictionary describing host properties: - DN - Properties - <extra params> :param bool createIfNonExistant: if true, creates the host if it did not exist :return: S_OK, Value = True/False """ if not self.__initialized['OK']: return self.__initialized modifiedHost = False hostData = self.describeHosts([hostname])['Value'] if hostname not in hostData: if createIfNonExistant: gLogger.info("Registering host %s" % hostname) return self.addHost(hostname, properties) gLogger.error("Host is not registered", hostname) return S_OK(False) for prop in properties: prevVal = self.__csMod.getValue( "%s/Hosts/%s/%s" % (self.__baseSecurity, hostname, prop)) if not prevVal or prevVal != properties[prop]: gLogger.info("Setting %s property for host %s to %s" % (prop, hostname, properties[prop])) self.__csMod.setOptionValue( "%s/Hosts/%s/%s" % (self.__baseSecurity, hostname, prop), properties[prop]) modifiedHost = True if modifiedHost: gLogger.info("Modified host %s" % hostname) self.csModified = True else: gLogger.info("Nothing to modify for host %s" % hostname) return S_OK(True) def syncUsersWithCFG(self, usersCFG): """ Sync users with the cfg contents. Usernames have to be sections containing DN, Groups, and extra properties as parameters """ if not self.__initialized['OK']: return self.__initialized done = True for user in usersCFG.listSections(): properties = {} propList = usersCFG[user].listOptions() for prop in propList: if prop == "Groups": properties[prop] = List.fromChar(usersCFG[user][prop]) else: properties[prop] = usersCFG[user][prop] if not self.modifyUser(user, properties, createIfNonExistant=True): done = False return S_OK(done) def sortUsersAndGroups(self): self.__csMod.sortAlphabetically("%s/Users" % self.__baseSecurity) self.__csMod.sortAlphabetically("%s/Hosts" % self.__baseSecurity) for group in self.__csMod.getSections("%s/Groups" % self.__baseSecurity): usersOptionPath = "%s/Groups/%s/Users" % (self.__baseSecurity, group) users = self.__csMod.getValue(usersOptionPath) if users: usersList = sorted(List.fromChar(users)) sortedUsers = ", ".join(usersList) if users != sortedUsers: self.__csMod.setOptionValue(usersOptionPath, sortedUsers) def checkForUnexistantUsersInGroups(self): allUsers = self.__csMod.getSections("%s/Users" % self.__baseSecurity) allGroups = self.__csMod.getSections("%s/Groups" % self.__baseSecurity) for group in allGroups: usersInGroup = self.__csMod.getValue("%s/Groups/%s/Users" % (self.__baseSecurity, group)) if usersInGroup: filteredUsers = [] usersInGroup = List.fromChar(usersInGroup) for user in usersInGroup: if user in allUsers: filteredUsers.append(user) self.__csMod.setOptionValue( "%s/Groups/%s/Users" % (self.__baseSecurity, group), ",".join(filteredUsers)) def commitChanges(self, sortUsers=True): if not self.__initialized['OK']: return self.__initialized if self.csModified: self.checkForUnexistantUsersInGroups() if sortUsers: self.sortUsersAndGroups() retVal = self.__csMod.commit() if not retVal['OK']: gLogger.error("Can't commit new configuration data", "%s" % retVal['Message']) return retVal return self.downloadCSData() return S_OK() def commit(self): """ Commit the accumulated changes to the CS server """ if not self.__initialized['OK']: return self.__initialized if self.csModified: retVal = self.__csMod.commit() if not retVal['OK']: gLogger.error("Can't commit new configuration data", "%s" % retVal['Message']) return retVal return self.downloadCSData() return S_OK() def mergeFromCFG(self, cfg): """ Merge the internal CFG data with the input """ if not self.__initialized['OK']: return self.__initialized self.__csMod.mergeFromCFG(cfg) self.csModified = True return S_OK() def modifyValue(self, optionPath, newValue): """Modify an existing value at the specified options path. """ if not self.__initialized['OK']: return self.__initialized prevVal = self.__csMod.getValue(optionPath) if prevVal is None: return S_ERROR('Trying to set %s to %s but option does not exist' % (optionPath, newValue)) gLogger.verbose("Changing %s from \n%s \nto \n%s" % (optionPath, prevVal, newValue)) self.__csMod.setOptionValue(optionPath, newValue) self.csModified = True return S_OK('Modified %s' % optionPath) def setOption(self, optionPath, optionValue): """Create an option at the specified path. """ if not self.__initialized['OK']: return self.__initialized self.__csMod.setOptionValue(optionPath, optionValue) self.csModified = True return S_OK('Created new option %s = %s' % (optionPath, optionValue)) def setOptionComment(self, optionPath, comment): """Create an option at the specified path. """ if not self.__initialized['OK']: return self.__initialized self.__csMod.setComment(optionPath, comment) self.csModified = True return S_OK('Set option comment %s : %s' % (optionPath, comment)) def delOption(self, optionPath): """ Delete an option """ if not self.__initialized['OK']: return self.__initialized if not self.__csMod.removeOption(optionPath): return S_ERROR("Couldn't delete option %s" % optionPath) self.csModified = True return S_OK('Deleted option %s' % optionPath) def createSection(self, sectionPath, comment=""): """ Create a new section """ if not self.__initialized['OK']: return self.__initialized self.__csMod.createSection(sectionPath) self.csModified = True if comment: self.__csMod.setComment(sectionPath, comment) return S_OK() def delSection(self, sectionPath): """ Delete a section """ if not self.__initialized['OK']: return self.__initialized if not self.__csMod.removeSection(sectionPath): return S_ERROR("Could not delete section %s " % sectionPath) self.csModified = True return S_OK() def copySection(self, originalPath, targetPath): """ Copy a whole section to a new location """ if not self.__initialized['OK']: return self.__initialized cfg = self.__csMod.getCFG() sectionCfg = cfg[originalPath] result = self.createSection(targetPath) if not result['OK']: return result if not self.__csMod.mergeSectionFromCFG(targetPath, sectionCfg): return S_ERROR("Could not merge cfg into section %s" % targetPath) self.csModified = True return S_OK() def moveSection(self, originalPath, targetPath): """ Move a whole section to a new location """ result = self.copySection(originalPath, targetPath) if not result['OK']: return result result = self.delSection(originalPath) if not result['OK']: return result self.csModified = True return S_OK() def mergeCFGUnderSection(self, sectionPath, cfg): """ Merge the given cfg under a certain section """ if not self.__initialized['OK']: return self.__initialized result = self.createSection(sectionPath) if not result['OK']: return result if not self.__csMod.mergeSectionFromCFG(sectionPath, cfg): return S_ERROR("Could not merge cfg into section %s" % sectionPath) self.csModified = True return S_OK() def mergeWithCFG(self, cfg): """ Merge the given cfg with the current config """ if not self.__initialized['OK']: return self.__initialized self.__csMod.mergeFromCFG(cfg) self.csModified = True return S_OK() def getCurrentCFG(self): """ Get the current CFG as it is """ if not self.__initialized['OK']: return self.__initialized return S_OK(self.__csMod.getCFG()) def showDiff(self): """ Just shows the differences accumulated within the Modificator object """ diffData = self.__csMod.showCurrentDiff() gLogger.notice("Accumulated diff with master CS") for line in diffData: if line[0] in ('+', '-'): gLogger.notice(line) def forceGlobalConfigurationUpdate(self): """ Force global update of configuration on all the registered services :return: S_OK/S_ERROR """ return self.__rpcClient.forceGlobalConfigurationUpdate()
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 )
diffList=processedData["diff"]) })) def __showDiff(self, params): if not self.__authorizeAction(): raise WErr(500, "You are not authorized to get diff's!! Bad boy!") try: fromDate = str(params['fromVersion']) toDate = str(params['toVersion']) except Exception, e: raise WErr(500, "Can't decode params: %s" % e) rpcClient = RPCClient( gConfig.getValue("/DIRAC/Configuration/MasterServer", "Configuration/Server")) modCfg = Modificator(rpcClient) diffGen = modCfg.getVersionDiff(fromDate, toDate) processedData = self.__generateHTMLDiff(diffGen) return self.write_message( json.dumps({ "success": 1, "op": "showDiff", "lines": processedData["lines"], "totalLines": processedData["totalLines"], "html": self.render_string("ConfigurationManager/diffConfig.tpl", titles=("From version %s" % fromDate,
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 CSAPI: def __init__(self): """ Initialization function """ self.__csModified = False self.__baseSecurity = "/Registry" self.__userDN = '' self.__userGroup = '' self.__rpcClient = None self.__csMod = None self.__initialized = self.initialize() def __getProxyID(self): proxyLocation = Locations.getProxyLocation() if not proxyLocation: gLogger.error("No proxy found!") return False chain = X509Chain() if not chain.loadProxyFromFile(proxyLocation): gLogger.error("Can't read proxy!", proxyLocation) return False retVal = chain.getIssuerCert() if not retVal['OK']: gLogger.error("Can't parse proxy!", retVal['Message']) return False idCert = retVal['Value'] self.__userDN = idCert.getSubjectDN()['Value'] self.__userGroup = idCert.getDIRACGroup()['Value'] return True def __getCertificateID(self): certLocation = Locations.getHostCertificateAndKeyLocation() if not certLocation: gLogger.error("No certificate found!") return False chain = X509Chain() retVal = chain.loadChainFromFile(certLocation[0]) if not retVal['OK']: gLogger.error("Can't parse certificate!", retVal['Message']) return False idCert = chain.getIssuerCert()['Value'] self.__userDN = idCert.getSubjectDN()['Value'] self.__userGroup = 'host' return True def initialize(self): if not gConfig.useServerCertificate(): res = self.__getProxyID() else: res = self.__getCertificateID() if not res: return False retVal = gConfig.getOption("/DIRAC/Configuration/MasterServer") if not retVal['OK']: gLogger.warn( "Master server is not known. Is everything initialized?") return False self.__rpcClient = RPCClient( gConfig.getValue("/DIRAC/Configuration/MasterServer", "")) self.__csMod = Modificator( self.__rpcClient, "%s - %s" % (self.__userGroup, self.__userDN)) retVal = self.downloadCSData() if not retVal['OK']: gLogger.error( "Can not download the remote cfg. Is everything initialized?") return False return True def downloadCSData(self): result = self.__csMod.loadFromRemote() if not result['OK']: return result self.__csMod.updateGConfigurationData() return S_OK() def listUsers(self, group=False): if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") if not group: return S_OK( self.__csMod.getSections("%s/Users" % self.__baseSecurity)) else: users = self.__csMod.getValue("%s/Groups/%s/Users" % (self.__baseSecurity, group)) if not users: return S_OK([]) else: return S_OK(List.fromChar(users)) def listHosts(self): if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") return S_OK(self.__csMod.getSections("%s/Hosts" % self.__baseSecurity)) def describeUsers(self, users=False): if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") return S_OK(self.__describeEntity(users)) def describeHosts(self, hosts=False): if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") return S_OK(self.__describeEntity(hosts, True)) def __describeEntity(self, mask, hosts=False): if hosts: csSection = "%s/Hosts" % self.__baseSecurity else: csSection = "%s/Users" % self.__baseSecurity if mask: entities = [ entity for entity in self.__csMod.getSections(csSection) if entity in mask ] else: entities = self.__csMod.getSections(csSection) entitiesDict = {} for entity in entities: entitiesDict[entity] = {} for option in self.__csMod.getOptions("%s/%s" % (csSection, entity)): entitiesDict[entity][option] = self.__csMod.getValue( "%s/%s/%s" % (csSection, entity, option)) if not hosts: groupsDict = self.describeGroups()['Value'] entitiesDict[entity]['Groups'] = [] for group in groupsDict: if 'Users' in groupsDict[group] and entity in groupsDict[ group]['Users']: entitiesDict[entity]['Groups'].append(group) entitiesDict[entity]['Groups'].sort() return entitiesDict def listGroups(self): """ List all groups """ if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") return S_OK(self.__csMod.getSections("%s/Groups" % self.__baseSecurity)) def describeGroups(self, mask=False): """ List all groups that are in the mask (or all if no mask) with their properties """ if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") groups = [ group for group in self.__csMod.getSections("%s/Groups" % self.__baseSecurity) if not mask or (mask and group in mask) ] groupsDict = {} for group in groups: groupsDict[group] = {} for option in self.__csMod.getOptions( "%s/Groups/%s" % (self.__baseSecurity, group)): groupsDict[group][option] = self.__csMod.getValue( "%s/Groups/%s/%s" % (self.__baseSecurity, group, option)) if option in ("Users", "Properties"): groupsDict[group][option] = List.fromChar( groupsDict[group][option]) return S_OK(groupsDict) def deleteUsers(self, users): """ Delete a user/s can receive as a param either a string or a list """ if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") if type(users) == types.StringType: users = [users] usersData = self.describeUsers(users)['Value'] for username in users: if not username in usersData: gLogger.warn("User %s does not exist") continue userGroups = usersData[username]['Groups'] for group in userGroups: self.__removeUserFromGroup(group, username) gLogger.info("Deleted user %s from group %s" % (username, group)) self.__csMod.removeSection("%s/Users/%s" % (self.__baseSecurity, username)) gLogger.info("Deleted user %s" % username) self.__csModified = True return S_OK(True) def __removeUserFromGroup(self, group, username): """ Remove user from a group """ usersInGroup = self.__csMod.getValue("%s/Groups/%s/Users" % (self.__baseSecurity, group)) if usersInGroup != None: userList = List.fromChar(usersInGroup, ",") userPos = userList.index(username) userList.pop(userPos) self.__csMod.setOptionValue( "%s/Groups/%s/Users" % (self.__baseSecurity, group), ",".join(userList)) def __addUserToGroup(self, group, username): """ Add user to a group """ usersInGroup = self.__csMod.getValue("%s/Groups/%s/Users" % (self.__baseSecurity, group)) if usersInGroup != None: userList = List.fromChar(usersInGroup) if username not in userList: userList.append(username) self.__csMod.setOptionValue( "%s/Groups/%s/Users" % (self.__baseSecurity, group), ",".join(userList)) else: gLogger.warn("User %s is already in group %s" % (username, group)) def addUser(self, username, properties): """ Add a user to the cs - username - properties is a dict with keys: - DN - groups - <extra params> Returns True/False """ if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") for prop in ("DN", "Groups"): if prop not in properties: gLogger.error("Missing %s property for user %s" % (prop, username)) return S_OK(False) if username in self.listUsers()['Value']: gLogger.error("User %s is already registered" % username) return S_OK(False) groups = self.listGroups()['Value'] for userGroup in properties['Groups']: if not userGroup in groups: gLogger.error("User %s group %s is not a valid group" % (username, userGroup)) return S_OK(False) self.__csMod.createSection("%s/Users/%s" % (self.__baseSecurity, username)) for prop in properties: if prop == "Groups": continue self.__csMod.setOptionValue( "%s/Users/%s/%s" % (self.__baseSecurity, username, prop), properties[prop]) for userGroup in properties['Groups']: gLogger.info("Added user %s to group %s" % (username, userGroup)) self.__addUserToGroup(userGroup, username) gLogger.info("Registered user %s" % username) self.__csModified = True return S_OK(True) def modifyUser(self, username, properties, createIfNonExistant=False): """ Modify a user - username - properties is a dict with keys: - DN - Groups - <extra params> Returns True/False """ if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") modifiedUser = False userData = self.describeUsers([username])['Value'] if username not in userData: if createIfNonExistant: gLogger.info("Registering user %s" % username) return self.addUser(username, properties) gLogger.error("User %s is not registered" % username) return S_OK(False) for prop in properties: if prop == "Groups": continue prevVal = self.__csMod.getValue( "%s/Users/%s/%s" % (self.__baseSecurity, username, prop)) if not prevVal or prevVal != properties[prop]: gLogger.info("Setting %s property for user %s to %s" % (prop, username, properties[prop])) self.__csMod.setOptionValue( "%s/Users/%s/%s" % (self.__baseSecurity, username, prop), properties[prop]) modifiedUser = True if 'Groups' in properties: groups = self.listGroups()['Value'] for userGroup in properties['Groups']: if not userGroup in groups: gLogger.error("User %s group %s is not a valid group" % (username, userGroup)) return S_OK(False) groupsToBeDeletedFrom = [] groupsToBeAddedTo = [] for prevGroup in userData[username]['Groups']: if prevGroup not in properties['Groups']: groupsToBeDeletedFrom.append(prevGroup) modifiedUser = True for newGroup in properties['Groups']: if newGroup not in userData[username]['Groups']: groupsToBeAddedTo.append(newGroup) modifiedUser = True for group in groupsToBeDeletedFrom: self.__removeUserFromGroup(group, username) gLogger.info("Removed user %s from group %s" % (username, group)) for group in groupsToBeAddedTo: self.__addUserToGroup(group, username) gLogger.info("Added user %s to group %s" % (username, group)) if modifiedUser: gLogger.info("Modified user %s" % username) self.__csModified = True else: gLogger.info("Nothing to modify for user %s" % username) return S_OK(True) def addGroup(self, groupname, properties): """ Add a group to the cs - groupname - properties is a dict with keys: - Users - Properties - <extra params> Returns True/False """ if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") if groupname in self.listGroups()['Value']: gLogger.error("Group %s is already registered" % groupname) return S_OK(False) self.__csMod.createSection("%s/Groups/%s" % (self.__baseSecurity, groupname)) for prop in properties: self.__csMod.setOptionValue( "%s/Groups/%s/%s" % (self.__baseSecurity, groupname, prop), properties[prop]) gLogger.info("Registered group %s" % groupname) self.__csModified = True return S_OK(True) def modifyGroup(self, groupname, properties, createIfNonExistant=False): """ Modify a user - groupname - properties is a dict with keys: - Users - Properties - <extra params> Returns True/False """ if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") modifiedGroup = False groupData = self.describeGroups([groupname])['Value'] if groupname not in groupData: if createIfNonExistant: gLogger.info("Registering group %s" % groupname) return self.addGroup(groupname, properties) gLogger.error("Group %s is not registered" % groupname) return S_OK(False) for prop in properties: prevVal = self.__csMod.getValue( "%s/Groups/%s/%s" % (self.__baseSecurity, groupname, prop)) if not prevVal or prevVal != properties[prop]: gLogger.info("Setting %s property for group %s to %s" % (prop, groupname, properties[prop])) self.__csMod.setOptionValue( "%s/Groups/%s/%s" % (self.__baseSecurity, groupname, prop), properties[prop]) modifiedGroup = True if modifiedGroup: gLogger.info("Modified group %s" % groupname) self.__csModified = True else: gLogger.info("Nothing to modify for group %s" % groupname) return S_OK(True) def addHost(self, hostname, properties): """ Add a host to the cs - hostname - properties is a dict with keys: - DN - Properties - <extra params> Returns True/False """ if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") for prop in ("DN", ): if prop not in properties: gLogger.error("Missing %s property for host %s" % (prop, hostname)) return S_OK(False) if hostname in self.listHosts()['Value']: gLogger.error("Host %s is already registered" % hostname) return S_OK(False) self.__csMod.createSection("%s/Hosts/%s" % (self.__baseSecurity, hostname)) for prop in properties: self.__csMod.setOptionValue( "%s/Hosts/%s/%s" % (self.__baseSecurity, hostname, prop), properties[prop]) gLogger.info("Registered host %s" % hostname) self.__csModified = True return S_OK(True) def modifyHost(self, hostname, properties, createIfNonExistant=False): """ Modify a user - hostname - properties is a dict with keys: - DN - Properties - <extra params> Returns True/False """ if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") modifiedHost = False hostData = self.describeHosts([hostname])['Value'] if hostname not in hostData: if createIfNonExistant: gLogger.info("Registering host %s" % hostname) return self.addHost(hostname, properties) gLogger.error("Host %s is not registered" % hostname) return S_OK(False) for prop in properties: prevVal = self.__csMod.getValue( "%s/Hosts/%s/%s" % (self.__baseSecurity, hostname, prop)) if not prevVal or prevVal != properties[prop]: gLogger.info("Setting %s property for host %s to %s" % (prop, hostname, properties[prop])) self.__csMod.setOptionValue( "%s/Hosts/%s/%s" % (self.__baseSecurity, hostname, prop), properties[prop]) modifiedHost = True if modifiedHost: gLogger.info("Modified host %s" % hostname) self.__csModified = True else: gLogger.info("Nothing to modify for host %s" % hostname) return S_OK(True) def syncUsersWithCFG(self, usersCFG): """ Sync users with the cfg contents. Usernames have to be sections containing DN, Groups, and extra properties as parameters """ if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") done = True for user in usersCFG.listSections(): properties = {} propList = usersCFG[user].listOptions() for prop in propList: if prop == "Groups": properties[prop] = List.fromChar(usersCFG[user][prop]) else: properties[prop] = usersCFG[user][prop] if not self.modifyUser(user, properties, createIfNonExistant=True): done = False return S_OK(done) def sortUsersAndGroups(self): self.__csMod.sortAlphabetically("%s/Users" % self.__baseSecurity) self.__csMod.sortAlphabetically("%s/Hosts" % self.__baseSecurity) for group in self.__csMod.getSections("%s/Groups" % self.__baseSecurity): usersOptionPath = "%s/Groups/%s/Users" % (self.__baseSecurity, group) users = self.__csMod.getValue(usersOptionPath) usersList = List.fromChar(users) usersList.sort() sortedUsers = ", ".join(usersList) if users != sortedUsers: self.__csMod.setOptionValue(usersOptionPath, sortedUsers) def checkForUnexistantUsersInGroups(self): allUsers = self.__csMod.getSections("%s/Users" % self.__baseSecurity) allGroups = self.__csMod.getSections("%s/Groups" % self.__baseSecurity) for group in allGroups: usersInGroup = self.__csMod.getValue("%s/Groups/%s/Users" % (self.__baseSecurity, group)) if usersInGroup: filteredUsers = [] usersInGroup = List.fromChar(usersInGroup) for user in usersInGroup: if user in allUsers: filteredUsers.append(user) self.__csMod.setOptionValue( "%s/Groups/%s/Users" % (self.__baseSecurity, group), ",".join(filteredUsers)) def commitChanges(self, sortUsers=True): if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") if self.__csModified: self.checkForUnexistantUsersInGroups() if sortUsers: self.sortUsersAndGroups() retVal = self.__csMod.commit() if not retVal['OK']: gLogger.error("Can't commit new data: %s" % retVal['Message']) return retVal self.__csModified = False return self.downloadCSData() return S_OK() def commit(self): """ Commit the accumulated changes to the CS server """ if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") if self.__csModified: retVal = self.__csMod.commit() if not retVal['OK']: gLogger.error("Can't commit new data: %s" % retVal['Message']) return retVal self.__csModified = False return self.downloadCSData() return S_OK() def mergeFromCFG(self, cfg): """ Merge the internal CFG data with the input """ if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") self.__csMod.mergeFromCFG(cfg) self.__csModified = True return S_OK() def modifyValue(self, optionPath, newValue): """Modify an existing value at the specified options path. """ if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") prevVal = self.__csMod.getValue(optionPath) if not prevVal: return S_ERROR('Trying to set %s to %s but option does not exist' % (optionPath, newValue)) gLogger.verbose("Changing %s from \n%s \nto \n%s" % (optionPath, prevVal, newValue)) self.__csMod.setOptionValue(optionPath, newValue) self.__csModified = True return S_OK('Modified %s' % optionPath) def setOption(self, optionPath, optionValue): """Create an option at the specified path. """ if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") self.__csMod.setOptionValue(optionPath, optionValue) self.__csModified = True return S_OK('Created new option %s = %s' % (optionPath, optionValue)) def setOptionComment(self, optionPath, comment): """Create an option at the specified path. """ if not self.__initialized: return S_ERROR("CSAPI didn't initialize properly") self.__csMod.setComment(optionPath, comment) self.__csModified = True return S_OK('Set option comment %s : %s' % (optionPath, comment))
class CSAPI( object ): """ CSAPI objects need an initialization phase """ def __init__( self ): """ Initialization function """ self.csModified = False self.__baseSecurity = "/Registry" self.__userDN = '' self.__userGroup = '' self.__rpcClient = None self.__csMod = None self.__initialized = S_ERROR( "Not initialized" ) self.initialize() if not self.__initialized[ 'OK' ]: gLogger.error( self.__initialized ) def __getProxyID( self ): proxyLocation = Locations.getProxyLocation() if not proxyLocation: gLogger.error( "No proxy found!" ) return False chain = X509Chain() if not chain.loadProxyFromFile( proxyLocation ): gLogger.error( "Can't read proxy!", proxyLocation ) return False retVal = chain.getIssuerCert() if not retVal[ 'OK' ]: gLogger.error( "Can't parse proxy!", retVal[ 'Message' ] ) return False idCert = retVal[ 'Value' ] self.__userDN = idCert.getSubjectDN()[ 'Value' ] self.__userGroup = chain.getDIRACGroup()[ 'Value' ] return True def __getCertificateID( self ): certLocation = Locations.getHostCertificateAndKeyLocation() if not certLocation: gLogger.error( "No certificate found!" ) return False chain = X509Chain() retVal = chain.loadChainFromFile( certLocation[ 0 ] ) if not retVal[ 'OK' ]: gLogger.error( "Can't parse certificate!", retVal[ 'Message' ] ) return False idCert = chain.getIssuerCert()[ 'Value' ] self.__userDN = idCert.getSubjectDN()[ 'Value' ] self.__userGroup = 'host' return True def initialize( self ): if self.__initialized[ 'OK' ]: return self.__initialized if not gConfig.useServerCertificate(): res = self.__getProxyID() else: res = self.__getCertificateID() if not res: self.__initialized = S_ERROR( "Cannot locate client credentials" ) return self.__initialized retVal = gConfig.getOption( "/DIRAC/Configuration/MasterServer" ) if not retVal[ 'OK' ]: self.__initialized = S_ERROR( "Master server is not known. Is everything initialized?" ) return self.__initialized self.__rpcClient = RPCClient( gConfig.getValue( "/DIRAC/Configuration/MasterServer", "" ) ) self.__csMod = Modificator( self.__rpcClient, "%s - %s" % ( self.__userGroup, self.__userDN ) ) retVal = self.downloadCSData() if not retVal[ 'OK' ]: self.__initialized = S_ERROR( "Can not download the remote cfg. Is everything initialized?" ) return self.__initialized self.__initialized = S_OK() return self.__initialized def downloadCSData( self ): if not self.__csMod: return S_ERROR( "CSAPI not yet initialized" ) result = self.__csMod.loadFromRemote() if not result[ 'OK' ]: return result self.csModified = False self.__csMod.updateGConfigurationData() return S_OK() def listUsers( self , group = False ): if not self.__initialized[ 'OK' ]: return self.__initialized if not group: return S_OK( self.__csMod.getSections( "%s/Users" % self.__baseSecurity ) ) else: users = self.__csMod.getValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) ) if not users: return S_OK( [] ) else: return S_OK( List.fromChar( users ) ) def listHosts( self ): if not self.__initialized[ 'OK' ]: return self.__initialized return S_OK( self.__csMod.getSections( "%s/Hosts" % self.__baseSecurity ) ) def describeUsers( self, users = None ): if users is None: users = [] if not self.__initialized[ 'OK' ]: return self.__initialized return S_OK( self.__describeEntity( users ) ) def describeHosts( self, hosts = None ): if hosts is None: hosts = [] if not self.__initialized[ 'OK' ]: return self.__initialized return S_OK( self.__describeEntity( hosts, True ) ) def __describeEntity( self, mask, hosts = False ): if hosts: csSection = "%s/Hosts" % self.__baseSecurity else: csSection = "%s/Users" % self.__baseSecurity if mask: entities = [ entity for entity in self.__csMod.getSections( csSection ) if entity in mask ] else: entities = self.__csMod.getSections( csSection ) entitiesDict = {} for entity in entities: entitiesDict[ entity ] = {} for option in self.__csMod.getOptions( "%s/%s" % ( csSection, entity ) ): entitiesDict[ entity ][ option ] = self.__csMod.getValue( "%s/%s/%s" % ( csSection, entity, option ) ) if not hosts: groupsDict = self.describeGroups()[ 'Value' ] entitiesDict[ entity ][ 'Groups' ] = [] for group in groupsDict: if 'Users' in groupsDict[ group ] and entity in groupsDict[ group ][ 'Users' ]: entitiesDict[ entity ][ 'Groups' ].append( group ) entitiesDict[ entity ][ 'Groups' ].sort() return entitiesDict def listGroups( self ): """ List all groups """ if not self.__initialized[ 'OK' ]: return self.__initialized return S_OK( self.__csMod.getSections( "%s/Groups" % self.__baseSecurity ) ) def describeGroups( self, mask = None ): """ List all groups that are in the mask (or all if no mask) with their properties """ if mask is None: mask = [] if not self.__initialized[ 'OK' ]: return self.__initialized groups = [ group for group in self.__csMod.getSections( "%s/Groups" % self.__baseSecurity ) if not mask or ( mask and group in mask ) ] groupsDict = {} for group in groups: groupsDict[ group ] = {} for option in self.__csMod.getOptions( "%s/Groups/%s" % ( self.__baseSecurity, group ) ): groupsDict[ group ][ option ] = self.__csMod.getValue( "%s/Groups/%s/%s" % ( self.__baseSecurity, group, option ) ) if option in ( "Users", "Properties" ): groupsDict[ group ][ option ] = List.fromChar( groupsDict[ group ][ option ] ) return S_OK( groupsDict ) def deleteUsers( self, users ): """ Delete a user/s can receive as a param either a string or a list """ if not self.__initialized[ 'OK' ]: return self.__initialized if isinstance( users, basestring ): users = [ users ] usersData = self.describeUsers( users )['Value'] for username in users: if not username in usersData: gLogger.warn( "User %s does not exist" ) continue userGroups = usersData[ username ][ 'Groups' ] for group in userGroups: self.__removeUserFromGroup( group, username ) gLogger.info( "Deleted user %s from group %s" % ( username, group ) ) self.__csMod.removeSection( "%s/Users/%s" % ( self.__baseSecurity, username ) ) gLogger.info( "Deleted user %s" % username ) self.csModified = True return S_OK( True ) def __removeUserFromGroup( self, group, username ): """ Remove user from a group """ usersInGroup = self.__csMod.getValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) ) if usersInGroup is not None: userList = List.fromChar( usersInGroup, "," ) userPos = userList.index( username ) userList.pop( userPos ) self.__csMod.setOptionValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ), ",".join( userList ) ) def __addUserToGroup( self, group, username ): """ Add user to a group """ usersInGroup = self.__csMod.getValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) ) if usersInGroup is not None: userList = List.fromChar( usersInGroup ) if username not in userList: userList.append( username ) self.__csMod.setOptionValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ), ",".join( userList ) ) else: gLogger.warn( "User %s is already in group %s" % ( username, group ) ) def addUser( self, username, properties ): """ Add a user to the cs :param str username: group name :param dict properties: dictionary describing user properties: - DN - groups - <extra params> :return: True/False """ if not self.__initialized[ 'OK' ]: return self.__initialized for prop in ( "DN", "Groups" ): if prop not in properties: gLogger.error( "Missing property for user", "%s: %s" % ( prop, username ) ) return S_OK( False ) if username in self.listUsers()['Value']: gLogger.error( "User is already registered", username ) return S_OK( False ) groups = self.listGroups()['Value'] for userGroup in properties[ 'Groups' ]: if not userGroup in groups: gLogger.error( "User group is not a valid group", "%s %s" % ( username, userGroup ) ) return S_OK( False ) self.__csMod.createSection( "%s/Users/%s" % ( self.__baseSecurity, username ) ) for prop in properties: if prop == "Groups": continue self.__csMod.setOptionValue( "%s/Users/%s/%s" % ( self.__baseSecurity, username, prop ), properties[ prop ] ) for userGroup in properties[ 'Groups' ]: gLogger.info( "Added user %s to group %s" % ( username, userGroup ) ) self.__addUserToGroup( userGroup, username ) gLogger.info( "Registered user %s" % username ) self.csModified = True return S_OK( True ) def modifyUser( self, username, properties, createIfNonExistant = False ): """ Modify a user :param str username: group name :param dict properties: dictionary describing user properties: - DN - Groups - <extra params> :return: S_OK, Value = True/False """ if not self.__initialized[ 'OK' ]: return self.__initialized modifiedUser = False userData = self.describeUsers( [ username ] )['Value'] if username not in userData: if createIfNonExistant: gLogger.info( "Registering user %s" % username ) return self.addUser( username, properties ) gLogger.error( "User is not registered", username ) return S_OK( False ) for prop in properties: if prop == "Groups": continue prevVal = self.__csMod.getValue( "%s/Users/%s/%s" % ( self.__baseSecurity, username, prop ) ) if not prevVal or prevVal != properties[ prop ]: gLogger.info( "Setting %s property for user %s to %s" % ( prop, username, properties[ prop ] ) ) self.__csMod.setOptionValue( "%s/Users/%s/%s" % ( self.__baseSecurity, username, prop ), properties[ prop ] ) modifiedUser = True if 'Groups' in properties: groups = self.listGroups()['Value'] for userGroup in properties[ 'Groups' ]: if not userGroup in groups: gLogger.error( "User group is not a valid group", "%s %s" % ( username, userGroup ) ) return S_OK( False ) groupsToBeDeletedFrom = [] groupsToBeAddedTo = [] for prevGroup in userData[ username ][ 'Groups' ]: if prevGroup not in properties[ 'Groups' ]: groupsToBeDeletedFrom.append( prevGroup ) modifiedUser = True for newGroup in properties[ 'Groups' ]: if newGroup not in userData[ username ][ 'Groups' ]: groupsToBeAddedTo.append( newGroup ) modifiedUser = True for group in groupsToBeDeletedFrom: self.__removeUserFromGroup( group, username ) gLogger.info( "Removed user %s from group %s" % ( username, group ) ) for group in groupsToBeAddedTo: self.__addUserToGroup( group, username ) gLogger.info( "Added user %s to group %s" % ( username, group ) ) modified = False if modifiedUser: modified = True gLogger.info( "Modified user %s" % username ) self.csModified = True else: gLogger.info( "Nothing to modify for user %s" % username ) return S_OK( modified ) def addGroup( self, groupname, properties ): """ Add a group to the cs :param str groupname: group name :param dict properties: dictionary describing group properties: - Users - Properties - <extra params> :return: True/False """ if not self.__initialized[ 'OK' ]: return self.__initialized if groupname in self.listGroups()['Value']: gLogger.error( "Group is already registered", groupname ) return S_OK( False ) self.__csMod.createSection( "%s/Groups/%s" % ( self.__baseSecurity, groupname ) ) for prop in properties: self.__csMod.setOptionValue( "%s/Groups/%s/%s" % ( self.__baseSecurity, groupname, prop ), properties[ prop ] ) gLogger.info( "Registered group %s" % groupname ) self.csModified = True return S_OK( True ) def modifyGroup( self, groupname, properties, createIfNonExistant = False ): """ Modify a group :param str groupname: group name :param dict properties: dictionary describing group properties: - Users - Properties - <extra params> :return: True/False """ if not self.__initialized[ 'OK' ]: return self.__initialized modifiedGroup = False groupData = self.describeGroups( [ groupname ] )['Value'] if groupname not in groupData: if createIfNonExistant: gLogger.info( "Registering group %s" % groupname ) return self.addGroup( groupname, properties ) gLogger.error( "Group is not registered", groupname ) return S_OK( False ) for prop in properties: prevVal = self.__csMod.getValue( "%s/Groups/%s/%s" % ( self.__baseSecurity, groupname, prop ) ) if not prevVal or prevVal != properties[ prop ]: gLogger.info( "Setting %s property for group %s to %s" % ( prop, groupname, properties[ prop ] ) ) self.__csMod.setOptionValue( "%s/Groups/%s/%s" % ( self.__baseSecurity, groupname, prop ), properties[ prop ] ) modifiedGroup = True if modifiedGroup: gLogger.info( "Modified group %s" % groupname ) self.csModified = True else: gLogger.info( "Nothing to modify for group %s" % groupname ) return S_OK( True ) def addHost( self, hostname, properties ): """ Add a host to the cs :param str hostname: hostname name :param dict properties: dictionary describing host properties: - DN - Properties - <extra params> :return: True/False """ if not self.__initialized[ 'OK' ]: return self.__initialized for prop in ( "DN", ): if prop not in properties: gLogger.error( "Missing property for host", "%s %s" % ( prop, hostname ) ) return S_OK( False ) if hostname in self.listHosts()['Value']: gLogger.error( "Host is already registered", hostname ) return S_OK( False ) self.__csMod.createSection( "%s/Hosts/%s" % ( self.__baseSecurity, hostname ) ) for prop in properties: self.__csMod.setOptionValue( "%s/Hosts/%s/%s" % ( self.__baseSecurity, hostname, prop ), properties[ prop ] ) gLogger.info( "Registered host %s" % hostname ) self.csModified = True return S_OK( True ) def addShifter( self, shifters = None ): """ Adds or modify one or more shifters. Also, adds the shifter section in case this is not present. Shifter identities are used in several places, mostly for running agents shifters should be in the form {'ShifterRole':{'User':'******', 'Group':'aDIRACGroup'}} :return: S_OK/S_ERROR """ def getOpsSection(): """ Where is the shifters section? """ vo = CSGlobals.getVO() setup = CSGlobals.getSetup() if vo: res = gConfig.getSections( '/Operations/%s/%s/Shifter' % (vo, setup) ) if res['OK']: return S_OK( '/Operations/%s/%s/Shifter' % ( vo, setup ) ) res = gConfig.getSections( '/Operations/%s/Defaults/Shifter' % vo ) if res['OK']: return S_OK( '/Operations/%s/Defaults/Shifter' % vo ) else: res = gConfig.getSections( '/Operations/%s/Shifter' % setup ) if res['OK']: return S_OK( '/Operations/%s/Shifter' % setup ) res = gConfig.getSections( '/Operations/Defaults/Shifter' ) if res['OK']: return S_OK( '/Operations/Defaults/Shifter' ) return S_ERROR( "No shifter section" ) if shifters is None: shifters = {} if not self.__initialized['OK']: return self.__initialized # get current shifters opsH = Operations( ) currentShifterRoles = opsH.getSections( 'Shifter' ) if not currentShifterRoles['OK']: # we assume the shifter section is not present currentShifterRoles = [] else: currentShifterRoles = currentShifterRoles['Value'] currentShiftersDict = {} for currentShifterRole in currentShifterRoles: currentShifter = opsH.getOptionsDict( 'Shifter/%s' % currentShifterRole ) if not currentShifter['OK']: return currentShifter currentShifter = currentShifter['Value'] currentShiftersDict[currentShifterRole] = currentShifter # Removing from shifters what does not need to be changed for sRole in shifters: if sRole in currentShiftersDict: if currentShiftersDict[sRole] == shifters[sRole]: shifters.pop( sRole ) # get shifters section to modify section = getOpsSection() # Is this section present? if not section['OK']: if section['Message'] == "No shifter section": gLogger.warn( section['Message'] ) gLogger.info( "Adding shifter section" ) vo = CSGlobals.getVO() if vo: section = '/Operations/%s/Defaults/Shifter' % vo else: section = '/Operations/Defaults/Shifter' res = self.__csMod.createSection( section ) if not res: gLogger.error( "Section %s not created" % section ) return S_ERROR( "Section %s not created" % section ) else: gLogger.error( section['Message'] ) return section else: section = section['Value'] #add or modify shifters for shifter in shifters: self.__csMod.removeSection( section + '/' + shifter ) self.__csMod.createSection( section + '/' + shifter ) self.__csMod.createSection( section + '/' + shifter + '/' + 'User' ) self.__csMod.createSection( section + '/' + shifter + '/' + 'Group' ) self.__csMod.setOptionValue( section + '/' + shifter + '/' + 'User', shifters[shifter]['User'] ) self.__csMod.setOptionValue( section + '/' + shifter + '/' + 'Group', shifters[shifter]['Group'] ) self.csModified = True return S_OK( True ) def modifyHost( self, hostname, properties, createIfNonExistant = False ): """ Modify a host :param str hostname: hostname name :param dict properties: dictionary describing host properties: - DN - Properties - <extra params> :return: True/False """ if not self.__initialized[ 'OK' ]: return self.__initialized modifiedHost = False hostData = self.describeHosts( [ hostname ] )['Value'] if hostname not in hostData: if createIfNonExistant: gLogger.info( "Registering host %s" % hostname ) return self.addHost( hostname, properties ) gLogger.error( "Host is not registered", hostname ) return S_OK( False ) for prop in properties: prevVal = self.__csMod.getValue( "%s/Hosts/%s/%s" % ( self.__baseSecurity, hostname, prop ) ) if not prevVal or prevVal != properties[ prop ]: gLogger.info( "Setting %s property for host %s to %s" % ( prop, hostname, properties[ prop ] ) ) self.__csMod.setOptionValue( "%s/Hosts/%s/%s" % ( self.__baseSecurity, hostname, prop ), properties[ prop ] ) modifiedHost = True if modifiedHost: gLogger.info( "Modified host %s" % hostname ) self.csModified = True else: gLogger.info( "Nothing to modify for host %s" % hostname ) return S_OK( True ) def syncUsersWithCFG( self, usersCFG ): """ Sync users with the cfg contents. Usernames have to be sections containing DN, Groups, and extra properties as parameters """ if not self.__initialized[ 'OK' ]: return self.__initialized done = True for user in usersCFG.listSections(): properties = {} propList = usersCFG[ user ].listOptions() for prop in propList: if prop == "Groups": properties[ prop ] = List.fromChar( usersCFG[ user ][ prop ] ) else: properties[ prop ] = usersCFG[ user ][ prop ] if not self.modifyUser( user, properties, createIfNonExistant = True ): done = False return S_OK( done ) def sortUsersAndGroups( self ): self.__csMod.sortAlphabetically( "%s/Users" % self.__baseSecurity ) self.__csMod.sortAlphabetically( "%s/Hosts" % self.__baseSecurity ) for group in self.__csMod.getSections( "%s/Groups" % self.__baseSecurity ): usersOptionPath = "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) users = self.__csMod.getValue( usersOptionPath ) if users: usersList = List.fromChar( users ) usersList.sort() sortedUsers = ", ".join( usersList ) if users != sortedUsers: self.__csMod.setOptionValue( usersOptionPath, sortedUsers ) def checkForUnexistantUsersInGroups( self ): allUsers = self.__csMod.getSections( "%s/Users" % self.__baseSecurity ) allGroups = self.__csMod.getSections( "%s/Groups" % self.__baseSecurity ) for group in allGroups: usersInGroup = self.__csMod.getValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) ) if usersInGroup: filteredUsers = [] usersInGroup = List.fromChar( usersInGroup ) for user in usersInGroup: if user in allUsers: filteredUsers.append( user ) self.__csMod.setOptionValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ), ",".join( filteredUsers ) ) def commitChanges( self, sortUsers = True ): if not self.__initialized[ 'OK' ]: return self.__initialized if self.csModified: self.checkForUnexistantUsersInGroups() if sortUsers: self.sortUsersAndGroups() retVal = self.__csMod.commit() if not retVal[ 'OK' ]: gLogger.error( "Can't commit new configuration data", "%s" % retVal[ 'Message' ] ) return retVal return self.downloadCSData() return S_OK() def commit( self ): """ Commit the accumulated changes to the CS server """ if not self.__initialized[ 'OK' ]: return self.__initialized if self.csModified: retVal = self.__csMod.commit() if not retVal[ 'OK' ]: gLogger.error( "Can't commit new configuration data", "%s" % retVal[ 'Message' ] ) return retVal return self.downloadCSData() return S_OK() def mergeFromCFG( self, cfg ): """ Merge the internal CFG data with the input """ if not self.__initialized[ 'OK' ]: return self.__initialized self.__csMod.mergeFromCFG( cfg ) self.csModified = True return S_OK() def modifyValue( self, optionPath, newValue ): """Modify an existing value at the specified options path. """ if not self.__initialized[ 'OK' ]: return self.__initialized prevVal = self.__csMod.getValue( optionPath ) if prevVal is None: return S_ERROR( 'Trying to set %s to %s but option does not exist' % ( optionPath, newValue ) ) gLogger.verbose( "Changing %s from \n%s \nto \n%s" % ( optionPath, prevVal, newValue ) ) self.__csMod.setOptionValue( optionPath, newValue ) self.csModified = True return S_OK( 'Modified %s' % optionPath ) def setOption( self, optionPath, optionValue ): """Create an option at the specified path. """ if not self.__initialized[ 'OK' ]: return self.__initialized self.__csMod.setOptionValue( optionPath, optionValue ) self.csModified = True return S_OK( 'Created new option %s = %s' % ( optionPath, optionValue ) ) def setOptionComment( self, optionPath, comment ): """Create an option at the specified path. """ if not self.__initialized[ 'OK' ]: return self.__initialized self.__csMod.setComment( optionPath, comment ) self.csModified = True return S_OK( 'Set option comment %s : %s' % ( optionPath, comment ) ) def delOption( self, optionPath ): """ Delete an option """ if not self.__initialized[ 'OK' ]: return self.__initialized if not self.__csMod.removeOption( optionPath ): return S_ERROR( "Couldn't delete option %s" % optionPath ) self.csModified = True return S_OK( 'Deleted option %s' % optionPath ) def createSection( self, sectionPath, comment = "" ): """ Create a new section """ if not self.__initialized[ 'OK' ]: return self.__initialized self.__csMod.createSection( sectionPath ) self.csModified = True if comment: self.__csMod.setComment( sectionPath, comment ) return S_OK() def delSection( self, sectionPath ): """ Delete a section """ if not self.__initialized[ 'OK' ]: return self.__initialized if not self.__csMod.removeSection( sectionPath ): return S_ERROR( "Could not delete section %s " % sectionPath ) self.csModified = True return S_OK( ) def copySection( self, originalPath, targetPath ): """ Copy a whole section to a new location """ if not self.__initialized['OK']: return self.__initialized cfg = self.__csMod.getCFG( ) sectionCfg = cfg[originalPath] result = self.createSection( targetPath ) if not result['OK']: return result if not self.__csMod.mergeSectionFromCFG( targetPath, sectionCfg ): return S_ERROR( "Could not merge cfg into section %s" % targetPath ) self.csModified = True return S_OK( ) def moveSection( self, originalPath, targetPath ): """ Move a whole section to a new location """ result = self.copySection( originalPath, targetPath ) if not result['OK']: return result result = self.delSection( originalPath ) if not result['OK']: return result self.csModified = True return S_OK() def mergeCFGUnderSection( self, sectionPath, cfg ): """ Merge the given cfg under a certain section """ if not self.__initialized[ 'OK' ]: return self.__initialized result = self.createSection( sectionPath ) if not result[ 'OK' ]: return result if not self.__csMod.mergeSectionFromCFG( sectionPath, cfg ): return S_ERROR( "Could not merge cfg into section %s" % sectionPath ) self.csModified = True return S_OK() def mergeWithCFG( self, cfg ): """ Merge the given cfg with the current config """ if not self.__initialized[ 'OK' ]: return self.__initialized self.__csMod.mergeFromCFG( cfg ) self.csModified = True return S_OK() def getCurrentCFG( self ): """ Get the current CFG as it is """ if not self.__initialized[ 'OK' ]: return self.__initialized return S_OK( self.__csMod.getCFG() ) def showDiff( self ): """ Just shows the differences accumulated within the Modificator object """ diffData = self.__csMod.showCurrentDiff() gLogger.notice( "Accumulated diff with master CS" ) for line in diffData: if line[0] in ( '+', '-' ): gLogger.notice( 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]) 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 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 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)
processedData = self.__generateHTMLDiff( diffGen ) return self.write_message( json.dumps( {"success":1, "op":"showCurrentDiff", "lines":processedData["lines"], "totalLines": processedData["totalLines"], "html":self.render_string( "ConfigurationManager/diffConfig.tpl", titles = ( "Server's version", "User's current version" ), diffList = processedData["diff"] )} ) ) def __showDiff( self, params ): if not self.__authorizeAction(): raise WErr( 500, "You are not authorized to get diff's!! Bad boy!" ) try: fromDate = str( params[ 'fromVersion' ] ) toDate = str( params[ 'toVersion' ] ) except Exception, e: raise WErr( 500, "Can't decode params: %s" % e ) rpcClient = RPCClient( gConfig.getValue( "/DIRAC/Configuration/MasterServer", "Configuration/Server" ) ) modCfg = Modificator( rpcClient ) diffGen = modCfg.getVersionDiff( fromDate, toDate ) processedData = self.__generateHTMLDiff( diffGen ) return self.write_message( json.dumps( {"success":1, "op":"showDiff", "lines":processedData["lines"], "totalLines": processedData["totalLines"], "html":self.render_string( "ConfigurationManager/diffConfig.tpl", titles = ( "From version %s" % fromDate, "To version %s" % toDate ), diffList = processedData["diff"] )} ) ) def __rollback( self, params ): rollbackVersion = "" if not self.__authorizeAction(): raise WErr( 500, "You are not authorized to get diff's!! Bad boy!" ) try: rollbackVersion = str( params[ 'rollbackToVersion' ] ) except Exception, e: raise WErr( 500, "Can't decode params: %s" % e ) rpcClient = RPCClient( gConfig.getValue( "/DIRAC/Configuration/MasterServer", "Configuration/Server" ) )
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 )