Example #1
0
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()
Example #2
0
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()
Example #3
0
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())
Example #4
0
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() )