class CSCLI(CLI): def __init__(self): CLI.__init__(self) self.connected = False self.masterURL = "unset" self.writeEnabled = False self.modifiedData = False self.rpcClient = None self.do_connect() if self.connected: self.modificator = Modificator(self.rpcClient) else: self.modificator = Modificator() self.indentSpace = 20 self.backupFilename = "dataChanges" # store history histfilename = os.path.basename(sys.argv[0]) historyFile = os.path.expanduser("~/.dirac/%s.history" % histfilename[0:-3]) mkDir(os.path.dirname(historyFile)) if os.path.isfile(historyFile): readline.read_history_file(historyFile) readline.set_history_length(1000) atexit.register(readline.write_history_file, historyFile) def start(self): if self.connected: self.modificator.loadFromRemote() retVal = self.modificator.loadCredentials() if not retVal['OK']: print("There was an error gathering your credentials") print(retVal['Message']) self._setStatus(False) try: self.cmdloop() except KeyboardInterrupt: gLogger.warn("Received a keyboard interrupt.") self.do_quit("") def _setConnected(self, connected, writeEnabled): self.connected = connected self.modifiedData = False self.writeEnabled = writeEnabled if connected: if writeEnabled: self.prompt = "(%s)-%s> " % (self.masterURL, colorize("Connected", "green")) else: self.prompt = "(%s)-%s> " % ( self.masterURL, colorize("Connected (RO)", "yellow")) else: self.prompt = "(%s)-%s> " % (self.masterURL, colorize("Disconnected", "red")) def do_quit(self, dummy): """ Exits the application without sending changes to server Usage: quit """ print() if self.modifiedData: print("Changes are about to be written to file for later use.") self.do_writeToFile(self.backupFilename) print("Changes written to %s.cfg" % self.backupFilename) sys.exit(0) def _setStatus(self, connected=True): if not connected: self.masterURL = "unset" self._setConnected(False, False) else: retVal = self.rpcClient.writeEnabled() if retVal['OK']: if retVal['Value']: self._setConnected(True, True) else: self._setConnected(True, False) else: print("Server returned an error: %s" % retVal['Message']) self._setConnected(True, False) def _tryConnection(self): print("Trying connection to %s" % self.masterURL) try: self.rpcClient = ConfigurationClient(url=self.masterURL) self._setStatus() except Exception as x: gLogger.error("Couldn't connect to master CS server", "%s (%s)" % (self.masterURL, str(x))) self._setStatus(False) def do_connect(self, args=''): """ Connects to configuration master server (in specified url if provided). Usage: connect <url> """ if not args or not isinstance(args, six.string_types): self.masterURL = gConfigurationData.getMasterServer() if self.masterURL != "unknown" and self.masterURL: self._tryConnection() else: self._setStatus(False) else: splitted = args.split() if len(splitted) == 0: print("Must specify witch url to connect") self._setStatus(False) else: self.masterURL = splitted[0].strip() self._tryConnection() def do_sections(self, args): """ Shows all sections with their comments. If no section is specified, root is taken. Usage: sections <section> """ try: argList = args.split() if argList: baseSection = argList[0].strip() else: baseSection = "/" if not self.modificator.existsSection(baseSection): print("Section %s does not exist" % baseSection) return sectionList = self.modificator.getSections(baseSection) if not sectionList: print("Section %s is empty" % baseSection) return for section in sectionList: section = "%s/%s" % (baseSection, section) self.printPair(section, self.modificator.getComment(section), " #") except Exception: _showTraceback() def do_options(self, args): """ Shows all options and values of a specified section Usage: options <section> """ try: argList = args.split() if argList: section = argList[0].strip() else: print("Which section?") return if not self.modificator.existsSection(section): print("Section %s does not exist" % section) return optionsList = self.modificator.getOptions(section) if not optionsList: print("Section %s has no options" % section) return for option in optionsList: _printComment( self.modificator.getComment("%s/%s" % (section, option))) self.printPair( option, self.modificator.getValue("%s/%s" % (section, option)), "=") except Exception: _showTraceback() def do_get(self, args): """ Shows value and comment for specified option in section Usage: get <path to option> """ try: argList = args.split() if argList: optionPath = argList[0].strip() else: print("Which option?") return if self.modificator.existsOption(optionPath): option = optionPath.split("/")[-1] _printComment(self.modificator.getComment(optionPath)) self.printPair(option, self.modificator.getValue(optionPath), "=") else: print("Option %s does not exist" % optionPath) except Exception: _showTraceback() def do_writeToServer(self, dummy): """ Sends changes to server. Usage: writeToServer """ if not self.connected: print("You are not connected!") return try: if not self.writeEnabled: print("This server can't receive data modifications") return if not self.modifiedData: while True: choice = six.moves.input( "Data has not been modified, do you still want to upload changes? yes/no [no]: " ) choice = choice.lower() if choice in ("yes", "y"): break else: print("Commit aborted") return choice = six.moves.input( "Do you really want to send changes to server? yes/no [no]: ") choice = choice.lower() if choice in ("yes", "y"): print("Uploading changes to %s (It may take some seconds)..." % self.masterURL) response = self.modificator.commit() if response['OK']: self.modifiedData = False print("Data sent to server.") self.modificator.loadFromRemote() else: print("Error sending data, server said: %s" % response['Message']) return else: print("Commit aborted") except Exception as x: _showTraceback() print("Could not upload changes. ", str(x)) def do_set(self, args): """ Sets option's value Usage: set <optionPath> <value>... From second argument until the last one is considered option's value NOTE: If specified section does not exist it is created. """ try: argsList = args.split() if len(argsList) < 2: print("Must specify option and value to use") return optionPath = argsList[0].strip() value = " ".join(argsList[1:]).strip() self.modificator.setOptionValue(optionPath, value) self.modifiedData = True except Exception as x: print("Cannot insert value: ", str(x)) def do_removeOption(self, args): """ Removes an option. Usage: removeOption <option> There can be empty sections. """ try: argsList = args.split() if len(argsList) < 1: print("Must specify option to delete") return optionPath = argsList[0].strip() choice = six.moves.input( "Are you sure you want to delete %s? yes/no [no]: " % optionPath) choice = choice.lower() if choice in ("yes", "y", "true"): if self.modificator.removeOption(optionPath): self.modifiedData = True else: print("Can't be deleted") else: print("Aborting removal.") except Exception as x: print("Error removing option, %s" % str(x)) def do_removeSection(self, args): """ Removes a section. Usage: removeSection <section> """ try: argsList = args.split() if len(argsList) < 1: print("Must specify section to delete") return section = argsList[0].strip() choice = six.moves.input( "Are you sure you want to delete %s? yes/no [no]: " % section) choice = choice.lower() if choice in ("yes", "y", "true"): if self.modificator.removeSection(section): self.modifiedData = True else: print("Can't be deleted") else: print("Aborting removal.") except Exception as x: print("Error removing section, %s" % str(x)) def do_setComment(self, args): """ Sets option or section's comment. Requested entry MUST exist. Usage: set <option/section> <comment>... From third argument until the last one is considered option's comment. """ try: argsList = args.split() if len(argsList) < 2: print("Must specify option and value to use") return entryPath = argsList[0].strip() value = " ".join(argsList[1:]).strip() self.modificator.setComment(entryPath, value) self.modifiedData = True except Exception as x: print("Cannot insert comment: ", str(x)) def do_writeToFile(self, args): """ Writes modification to file for later use. Usage: writeToFile <filename>.cfg Note that if a file extension is specified, it is replaced by .cfg suffix. If not it is added automatically """ try: if len(args) == 0: print("Filename to write must be specified!") return filename = args.split()[0].strip() filename = _appendExtensionIfMissing(filename) self.modificator.dumpToFile(filename) except Exception as x: print("Couldn't write to file %s: %s" % (filename, str(x))) def do_readFromFile(self, args): """ Reads data from filename to be used. Actual data will be replaced! Usage: readFromFile <filename>.cfg Note that if a file extension is specified, it is replaced by .cfg suffix. If not it is added automatically """ try: if len(args) == 0: print("Filename to read must be specified!") return filename = args.split()[0].strip() filename = _appendExtensionIfMissing(filename) self.modificator.loadFromFile(filename) self.modifiedData = True except Exception as x: print("Couldn't read from file %s: %s" % (filename, str(x))) def do_mergeFromFile(self, args): """ Reads data from filename and merges it with current data. Data read from file has more precedence that current one. Usage: mergeFromFile <filename>.cfg Note that if a file extension is specified, it is replaced by .cfg suffix. If not it is added automatically """ try: if len(args) == 0: print("Filename to read must be specified!") return filename = args.split()[0].strip() filename = _appendExtensionIfMissing(filename) self.modificator.mergeFromFile(filename) self.modifiedData = True except Exception as x: _showTraceback() print("Couldn't read from file %s: %s" % (filename, str(x))) def do_showData(self, dummy): """ Shows the current modified configuration Usage: showData """ print(self.modificator) def do_showHistory(self, args): """ Shows the last commit history Usage: showHistory <update limit> """ try: argsList = args.split() limit = 100 if len(argsList) > 0: limit = int(argsList[0]) history = self.modificator.getHistory(limit) print("%s recent commits:" % limit) for entry in history: self.printPair(entry[0], entry[1], "@") except Exception: _showTraceback() def do_showDiffWithServer(self, dummy): """ Shows diff with lastest version in server Usage: showDiffWithServer """ try: diffData = self.modificator.showCurrentDiff() print("Diff with latest from server ( + local - remote )") for line in diffData: if line[0] in ('-'): print(colorize(line, "red")) elif line[0] in ('+'): print(colorize(line, "green")) elif line[0] in ('?'): print(colorize(line, "yellow"), end=' ') except Exception: _showTraceback() def do_showDiffBetweenVersions(self, args): """ Shows diff between two versions Usage: showDiffBetweenVersions <version 1 with spaces> <version 2 with spaces> """ try: argsList = args.split() if len(argsList) < 4: print("What are the two versions to compare?") return v1 = " ".join(argsList[0:2]) v2 = " ".join(argsList[2:4]) print("Comparing '%s' with '%s' " % (v1, v2)) diffData = self.modificator.getVersionDiff(v1, v2) print("Diff with latest from server ( + %s - %s )" % (v2, v1)) for line in diffData: if line[0] in ('-'): print(colorize(line, "red")) elif line[0] in ('+'): print(colorize(line, "green")) elif line[0] in ('?'): print(colorize(line, "yellow"), end=' ') else: print(line) except Exception: _showTraceback() def do_rollbackToVersion(self, args): """ rolls back to user selected version of the configuration Usage: rollbackToVersion <version with spaces>> """ try: argsList = args.split() if len(argsList) < 2: print("What version to rollback?") return version = " ".join(argsList[0:2]) choice = six.moves.input( "Do you really want to rollback to version %s? yes/no [no]: " % version) choice = choice.lower() if choice in ("yes", "y"): response = self.modificator.rollbackToVersion(version) if response['OK']: self.modifiedData = False print("Rolled back.") self.modificator.loadFromRemote() else: print("Error sending data, server said: %s" % response['Message']) except Exception: _showTraceback() def do_mergeWithServer(self, dummy): """ Shows diff with lastest version in server Usage: diffWithServer """ try: choice = six.moves.input( "Do you want to merge with server configuration? yes/no [no]: " ) choice = choice.lower() if choice in ("yes", "y"): retVal = self.modificator.mergeWithServer() if retVal['OK']: print("Merged") else: print("There was an error: ", retVal['Message']) else: print("Merge aborted") except Exception: _showTraceback()
class CSCLI( 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.__baseResources = '/Resources_new' 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 __addResourceLikeSection(self, resourcePath, resourceDict): """ Add one of Resource level entries ( site, resource, access point ) """ self.__csMod.createSection(resourcePath) for property in resourceDict: value = resourceDict[property] if type(value) in types.StringTypes: self.__csMod.setOptionValue("%s/%s" % (resourcePath, property), value) elif type(value) == types.ListType: optValue = ','.join(value) self.__csMod.setOptionValue("%s/%s" % (resourcePath, property), optValue) elif type(value) == types.DictType: self.__csMod.createSection("%s/%s" % (resourcePath, property)) for option in value: newValue = value[option] if type(newValue) in types.StringTypes: self.__csMod.setOptionValue( "%s/%s/%s" % (resourcePath, property, option), newValue) elif type(value) == types.ListType: optValue = ','.join(newValue) self.__csMod.setOptionValue( "%s/%s/%s" % (resourcePath, property, option), optValue) self.__csModified = True return S_OK(True) def addSite(self, siteName, siteDict): """ Add a new Site to the CS """ if not self.__initialized['OK']: return self.__initialized sitePath = "%s/Sites/%s" % (self.__baseResources, siteName) if self.__csMod.existsSection(sitePath): return S_ERROR('Site %s already exists ' % siteName) return self.__addResourceLikeSection(sitePath, siteDict) def addResource(self, siteName, resourceType, resourceName, resourceDict): """ Add a new Resource to the CS """ if not self.__initialized['OK']: return self.__initialized sitePath = "%s/Sites/%s" % (self.__baseResources, siteName) if not self.__csMod.existsSection(sitePath): return S_ERROR('Site %s does not exist' % siteName) resourcePath = "%s/Sites/%s/%s/%s" % (self.__baseResources, siteName, resourceType, resourceName) if self.__csMod.existsSection(resourcePath): return S_ERROR('%s resource %s at site %s already exists' % (resourceType, resourceName, siteName)) return self.__addResourceLikeSection(resourcePath, resourceDict) def addNode(self, siteName, resourceType, resourceName, apType, apName, apDict): """ Add a new site to the CS """ if not self.__initialized['OK']: return self.__initialized sitePath = "%s/Sites/%s" % (self.__baseResources, siteName) if not self.__csMod.existsSection(sitePath): return S_ERROR('Site %s does not exist' % siteName) resourcePath = "%s/Sites/%s/%s/%s" % (self.__baseResources, siteName, resourceType, resourceName) if not self.__csMod.existsSection(resourcePath): return S_ERROR('%s resource %s at site %s does not exist' % (resourceType, resourceName, siteName)) apPath = "%s/Sites/%s/%s/%s/%s/%s" % (self.__baseResources, siteName, resourceType, resourceName, apType, apName) if self.__csMod.existsSection(apPath): return S_ERROR( '%s access point %s at %s resource %s at site %s already exists ' % \ ( apType, apName, resourceType, resourceName, siteName ) ) return self.__addResourceLikeSection(apPath, apDict) def sortSection(self, section): self.__csMod.sortAlphabetically(section) 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 deleteOption(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 deleteSection(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.deleteSection(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())
class CSAPI: def __init__( self ): """ Initialization function """ self.__csModified = False self.__baseSecurity = "/Registry" self.__baseResources = '/Resources_new' 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 __addResourceLikeSection( self, resourcePath, resourceDict ): """ Add one of Resource level entries ( site, resource, access point ) """ self.__csMod.createSection( resourcePath ) for property in resourceDict: value = resourceDict[property] if type( value ) in types.StringTypes: self.__csMod.setOptionValue( "%s/%s" % ( resourcePath, property ), value ) elif type( value ) == types.ListType: optValue = ','.join(value) self.__csMod.setOptionValue( "%s/%s" % ( resourcePath, property ), optValue ) elif type( value ) == types.DictType: self.__csMod.createSection( "%s/%s" % ( resourcePath, property ) ) for option in value: newValue = value[option] if type( newValue ) in types.StringTypes: self.__csMod.setOptionValue( "%s/%s/%s" % ( resourcePath, property, option ), newValue ) elif type( value ) == types.ListType: optValue = ','.join( newValue) self.__csMod.setOptionValue( "%s/%s/%s" % ( resourcePath, property, option ), optValue ) self.__csModified = True return S_OK( True ) def addSite( self, siteName, siteDict ): """ Add a new Site to the CS """ if not self.__initialized[ 'OK' ]: return self.__initialized sitePath = "%s/Sites/%s" % ( self.__baseResources, siteName ) if self.__csMod.existsSection( sitePath ): return S_ERROR( 'Site %s already exists ' % siteName ) return self.__addResourceLikeSection( sitePath, siteDict ) def addResource( self, siteName, resourceType, resourceName, resourceDict ): """ Add a new Resource to the CS """ if not self.__initialized[ 'OK' ]: return self.__initialized sitePath = "%s/Sites/%s" % ( self.__baseResources, siteName ) if not self.__csMod.existsSection( sitePath ): return S_ERROR( 'Site %s does not exist' % siteName ) resourcePath = "%s/Sites/%s/%s/%s" % ( self.__baseResources, siteName, resourceType, resourceName ) if self.__csMod.existsSection( resourcePath ): return S_ERROR( '%s resource %s at site %s already exists' % ( resourceType, resourceName, siteName ) ) return self.__addResourceLikeSection( resourcePath, resourceDict ) def addNode( self, siteName, resourceType, resourceName, apType, apName, apDict ): """ Add a new site to the CS """ if not self.__initialized[ 'OK' ]: return self.__initialized sitePath = "%s/Sites/%s" % ( self.__baseResources, siteName ) if not self.__csMod.existsSection( sitePath ): return S_ERROR( 'Site %s does not exist' % siteName ) resourcePath = "%s/Sites/%s/%s/%s" % ( self.__baseResources, siteName, resourceType, resourceName ) if not self.__csMod.existsSection( resourcePath ): return S_ERROR( '%s resource %s at site %s does not exist' % ( resourceType, resourceName, siteName ) ) apPath = "%s/Sites/%s/%s/%s/%s/%s" % ( self.__baseResources, siteName, resourceType, resourceName, apType, apName ) if self.__csMod.existsSection( apPath ): return S_ERROR( '%s access point %s at %s resource %s at site %s already exists ' % \ ( apType, apName, resourceType, resourceName, siteName ) ) return self.__addResourceLikeSection( apPath, apDict ) def sortSection( self, section ): self.__csMod.sortAlphabetically( section ) 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 deleteOption( 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 deleteSection( 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.deleteSection( 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() )