def do_connect(self, line):
        """connect
    Connect to the CS
    Usage: connect <URL> (Connect to the CS at the specified URL)
           connect       (Connect to the default CS URL of your config)
    """
        if line == "":
            self.serverURL = gConfigurationData.getMasterServer()
            self.serverName = gConfigurationData.getName()
        else:
            self.serverURL = self.serverName = line

        if self.serverURL == None:
            print "Unable to connect to the default server. Maybe you don't have a proxy ?"
            return self.do_disconnect("")

        print "Trying to connect to " + self.serverURL + "...",

        self.modificator = Modificator(RPCClient(self.serverURL))
        rv = self.modificator.loadFromRemote()
        rv2 = self.modificator.loadCredentials()

        if rv['OK'] == False or rv2['OK'] == False:
            print "failed: ",
            if rv['OK'] == False: print rv['Message']
            else: print rv2['Message']
            self.connected = False
            self.update_prompt()
Exemple #2
0
    def __getRemoteConfiguration(self, funcName):
        rpcClient = RPCClient(
            gConfig.getValue("/DIRAC/Configuration/MasterServer",
                             "Configuration/Server"))
        modCfg = Modificator(rpcClient)
        retVal = modCfg.loadFromRemote()

        if not retVal['OK']:
            return {
                "success": 0,
                "op": "getSubnodes",
                "message": "The configuration cannot be read from the remote !"
            }

        self.__configData['cfgData'] = modCfg
        self.__configData['strCfgData'] = str(modCfg)

        version = str(modCfg.getCFG()["DIRAC"]["Configuration"]["Version"])
        configName = str(modCfg.getCFG()["DIRAC"]["Configuration"]["Name"])
        return {
            "success": 1,
            "op": funcName,
            "version": version,
            "name": configName
        }
Exemple #3
0
    def initialize(self):
        """API initialization

        :return: S_OK()/S_ERROR()
        """
        if self.__initialized["OK"]:
            return self.__initialized
        res = self.__getCertificateID() if gConfig.useServerCertificate(
        ) else self.__getProxyID()
        if not res["OK"]:
            gLogger.error(res["Message"])
            self.__initialized = S_ERROR("Cannot locate client credentials")
            return self.__initialized
        retVal = gConfig.getOption("/DIRAC/Configuration/MasterServer")
        if not retVal["OK"]:
            self.__initialized = S_ERROR(
                "Master server is not known. Is everything initialized?")
            return self.__initialized
        self.__rpcClient = ConfigurationClient(
            url=gConfig.getValue("/DIRAC/Configuration/MasterServer", ""))
        self.__csMod = Modificator(
            self.__rpcClient,
            "%s - %s - %s" %
            (self.__userGroup, self.__userDN,
             datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")),
        )
        retVal = self.downloadCSData()
        if not retVal["OK"]:
            self.__initialized = S_ERROR(
                "Can not download the remote cfg. Is everything initialized?")
            return self.__initialized
        self.__initialized = S_OK()
        return self.__initialized
Exemple #4
0
 def initialize(self):
     if self.__initialized['OK']:
         return self.__initialized
     if not gConfig.useServerCertificate():
         res = self.__getProxyID()
     else:
         res = self.__getCertificateID()
     if not res:
         self.__initialized = S_ERROR("Cannot locate client credentials")
         return self.__initialized
     retVal = gConfig.getOption("/DIRAC/Configuration/MasterServer")
     if not retVal['OK']:
         self.__initialized = S_ERROR(
             "Master server is not known. Is everything initialized?")
         return self.__initialized
     self.__rpcClient = RPCClient(
         gConfig.getValue("/DIRAC/Configuration/MasterServer", ""))
     self.__csMod = Modificator(
         self.__rpcClient,
         "%s - %s - %s" % (self.__userGroup, self.__userDN,
                           Time.dateTime().strftime("%Y-%m-%d %H:%M:%S")))
     retVal = self.downloadCSData()
     if not retVal['OK']:
         self.__initialized = S_ERROR(
             "Can not download the remote cfg. Is everything initialized?")
         return self.__initialized
     self.__initialized = S_OK()
     return self.__initialized
Exemple #5
0
 def __init__( self ):
   cmd.Cmd.__init__( self )
   self.connected = False
   self.masterURL = "unset"
   self.writeEnabled = False
   self.modifiedData = False
   self.rpcClient = None
   self.do_connect()
   if self.connected:
     self.modificator = Modificator ( self.rpcClient )
   else:
     self.modificator = Modificator()
   self.identSpace = 20
   self.backupFilename = "dataChanges"
   self._initSignals()
   #User friendly hack
   self.do_exit = self.do_quit
   # store history
   histfilename = os.path.basename(sys.argv[0])
   historyFile = os.path.expanduser( "~/.dirac/%s.history" % histfilename[0:-3])
   if not os.path.exists( os.path.dirname(historyFile) ):
     os.makedirs( os.path.dirname(historyFile) )
   if os.path.isfile( historyFile ):
     readline.read_history_file( historyFile )
   readline.set_history_length(1000)
   atexit.register( readline.write_history_file, historyFile )
Exemple #6
0
    def do_connect(self, line):
        """connect
        Connect to the CS
        Usage: connect <URL> (Connect to the CS at the specified URL)
               connect       (Connect to the default CS URL of your config)
        """
        if line == "":
            self.serverURL = gConfigurationData.getMasterServer()
            self.serverName = gConfigurationData.getName()
        else:
            self.serverURL = self.serverName = line

        if self.serverURL is None:
            print("Unable to connect to the default server. Maybe you don't have a proxy ?")
            return self.do_disconnect("")

        print("Trying to connect to " + self.serverURL + "...", end=" ")

        self.modificator = Modificator(ConfigurationClient(url=self.serverURL))
        rv = self.modificator.loadFromRemote()
        rv2 = self.modificator.loadCredentials()

        if rv["OK"] == False or rv2["OK"] == False:
            print("failed: ", end=" ")
            if rv["OK"] is False:
                print(rv["Message"])
            else:
                print(rv2["Message"])
            self.connected = False
            self.update_prompt()
        else:
            self.connected = True
            self.update_prompt()
            print("done.")
Exemple #7
0
 def __getRemoteConfiguration(self):
     rpcClient = getRPCClient(gConfig.getValue("/DIRAC/Configuration/MasterServer", "Configuration/Server"))
     modCfg = Modificator(rpcClient)
     retVal = modCfg.loadFromRemote()
     if retVal["OK"]:
         session["cfgData"] = str(modCfg)
         session["csName"] = "%s Configuration" % (modCfg.getValue("/DIRAC/VirtualOrganization"))
         session.save()
         c.cfgData = modCfg.cfgData
         c.csName = session["csName"]
     return retVal
  def __getRemoteConfiguration(self, funcName):
    rpcClient = RPCClient(gConfig.getValue("/DIRAC/Configuration/MasterServer", "Configuration/Server"))
    modCfg = Modificator(rpcClient)
    retVal = modCfg.loadFromRemote()

    if not retVal[ 'OK' ]:
      return {"success":0, "op":"getSubnodes", "message":"The configuration cannot be read from the remote !"}

    self.__configData[ 'cfgData' ] = modCfg
    self.__configData[ 'strCfgData' ] = str(modCfg)

    version = str(modCfg.getCFG()["DIRAC"]["Configuration"]["Version"])
    configName = str(modCfg.getCFG()["DIRAC"]["Configuration"]["Name"])
    return {"success":1, "op":funcName, "version":version, "name":configName}
 def __getRemoteConfiguration(self):
     rpcClient = getRPCClient(
         gConfig.getValue("/DIRAC/Configuration/MasterServer",
                          "Configuration/Server"))
     modCfg = Modificator(rpcClient)
     retVal = modCfg.loadFromRemote()
     if retVal['OK']:
         session['cfgData'] = str(modCfg)
         session['csName'] = "%s Configuration" % (
             modCfg.getValue("/DIRAC/VirtualOrganization"))
         session.save()
         c.cfgData = modCfg.cfgData
         c.csName = session['csName']
     return retVal
class CSShellCmd(cmd.Cmd):
    def __init__(self):
        cmd.Cmd.__init__(self)
        self.serverURL = ""
        self.serverName = ""
        self.modificator = None
        self.connected = False
        self.dirty = False
        self.root = "/"

        self.do_connect("")

    def update_prompt(self):
        if self.connected:
            self.prompt = "[" + colorize(self.serverName,
                                         "green") + ":" + self.root + " ]% "
        else:
            self.prompt = "[" + colorize("disconnected",
                                         "red") + ":" + self.root + " ]% "

    def do_connect(self, line):
        """connect
    Connect to the CS
    Usage: connect <URL> (Connect to the CS at the specified URL)
           connect       (Connect to the default CS URL of your config)
    """
        if line == "":
            self.serverURL = gConfigurationData.getMasterServer()
            self.serverName = gConfigurationData.getName()
        else:
            self.serverURL = self.serverName = line

        if self.serverURL == None:
            print "Unable to connect to the default server. Maybe you don't have a proxy ?"
            return self.do_disconnect("")

        print "Trying to connect to " + self.serverURL + "...",

        self.modificator = Modificator(RPCClient(self.serverURL))
        rv = self.modificator.loadFromRemote()
        rv2 = self.modificator.loadCredentials()

        if rv['OK'] == False or rv2['OK'] == False:
            print "failed: ",
            if rv['OK'] == False: print rv['Message']
            else: print rv2['Message']
            self.connected = False
            self.update_prompt()
        else:
class CSShellCmd(cmd.Cmd):

  def __init__(self):
    cmd.Cmd.__init__(self)
    self.serverURL   = ""
    self.serverName  = ""
    self.modificator = None
    self.connected   = False
    self.dirty       = False
    self.root        = "/"

    self.do_connect("")

  def update_prompt(self):
    if self.connected:
      self.prompt = "[" + colorize(self.serverName, "green") + ":" + self.root + " ]% "
    else:
      self.prompt = "[" + colorize("disconnected", "red") + ":" + self.root + " ]% "

  def do_connect(self, line):
    """connect
    Connect to the CS
    Usage: connect <URL> (Connect to the CS at the specified URL)
           connect       (Connect to the default CS URL of your config)
    """
    if line == "":
      self.serverURL  = gConfigurationData.getMasterServer()
      self.serverName = gConfigurationData.getName()
    else:
      self.serverURL  = self.serverName = line

    if self.serverURL == None:
      print "Unable to connect to the default server. Maybe you don't have a proxy ?"
      return self.do_disconnect("")

    print "Trying to connect to " + self.serverURL + "...",

    self.modificator = Modificator(RPCClient(self.serverURL))
    rv               = self.modificator.loadFromRemote()
    rv2              = self.modificator.loadCredentials()

    if rv['OK'] == False or rv2['OK'] == False:
      print "failed: ",
      if rv['OK'] == False: print rv['Message']
      else:                 print rv2['Message']
      self.connected = False
      self.update_prompt()
    else:
  def do_connect(self, line):
    """connect
    Connect to the CS
    Usage: connect <URL> (Connect to the CS at the specified URL)
           connect       (Connect to the default CS URL of your config)
    """
    if line == "":
      self.serverURL  = gConfigurationData.getMasterServer()
      self.serverName = gConfigurationData.getName()
    else:
      self.serverURL  = self.serverName = line

    if self.serverURL == None:
      print "Unable to connect to the default server. Maybe you don't have a proxy ?"
      return self.do_disconnect("")

    print "Trying to connect to " + self.serverURL + "...",

    self.modificator = Modificator(RPCClient(self.serverURL))
    rv               = self.modificator.loadFromRemote()
    rv2              = self.modificator.loadCredentials()

    if rv['OK'] == False or rv2['OK'] == False:
      print "failed: ",
      if rv['OK'] == False: print rv['Message']
      else:                 print rv2['Message']
      self.connected = False
      self.update_prompt()
Exemple #13
0
 def __init__(self):
     cmd.Cmd.__init__(self)
     self.connected = False
     self.masterURL = "unset"
     self.writeEnabled = False
     self.modifiedData = False
     self.rpcClient = None
     self.do_connect()
     if self.connected:
         self.modificator = Modificator(self.rpcClient)
     else:
         self.modificator = Modificator()
     self.identSpace = 20
     self.backupFilename = "dataChanges"
     self._initSignals()
     #User friendly hack
     self.do_exit = self.do_quit
Exemple #14
0
 def __init__(self):
     cmd.Cmd.__init__(self)
     self.connected = False
     self.masterURL = "unset"
     self.writeEnabled = False
     self.modifiedData = False
     self.rpcClient = None
     self.do_connect()
     if self.connected:
         self.modificator = Modificator(self.rpcClient)
     else:
         self.modificator = Modificator()
     self.identSpace = 20
     self.backupFilename = "dataChanges"
     self._initSignals()
     # User friendly hack
     self.do_exit = self.do_quit
 def __getModificator(self):
     rpcClient = getRPCClient(
         gConfig.getValue("/DIRAC/Configuration/MasterServer",
                          "Configuration/Server"))
     commiter = "%s@%s - %s" % (
         credentials.getUsername(), credentials.getSelectedGroup(),
         Time.dateTime().strftime("%Y-%m-%d %H:%M:%S"))
     return Modificator(rpcClient, commiter)
Exemple #16
0
 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 )
Exemple #17
0
 def initialize(self):
     if not gConfig.useServerCertificate():
         res = self.__getProxyID()
     else:
         res = self.__getCertificateID()
     if not res:
         return False
     retVal = gConfig.getOption("/DIRAC/Configuration/MasterServer")
     if not retVal['OK']:
         gLogger.warn(
             "Master server is not known. Is everything initialized?")
         return False
     self.__rpcClient = RPCClient(
         gConfig.getValue("/DIRAC/Configuration/MasterServer", ""))
     self.__csMod = Modificator(
         self.__rpcClient, "%s - %s" % (self.__userGroup, self.__userDN))
     retVal = self.downloadCSData()
     if not retVal['OK']:
         gLogger.error(
             "Can not download the remote cfg. Is everything initialized?")
         return False
     return True
Exemple #18
0
 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)
Exemple #19
0
 def initialize( self ):
   if not gConfig.useServerCertificate():
     res = self.__getProxyID()
   else:
     res = self.__getCertificateID()
   if not res:
     return False
   retVal = gConfig.getOption( "/DIRAC/Configuration/MasterServer" )
   if not retVal[ 'OK' ]:
     gLogger.warn( "Master server is not known. Is everything initialized?" )
     return False
   self.__rpcClient = RPCClient( gConfig.getValue( "/DIRAC/Configuration/MasterServer", "" ) )
   self.__csMod = Modificator( self.__rpcClient, "%s - %s" % ( self.__userGroup, self.__userDN ) )
   retVal = self.downloadCSData()
   if not retVal[ 'OK' ]:
     gLogger.error( "Can not download the remote cfg. Is everything initialized?" )
     return False
   return True
Exemple #20
0
 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
Exemple #21
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()
Exemple #22
0
class CSAPI:

  def __init__( self ):
    """
    Initialization function
    """
    self.__csModified = False
    self.__baseSecurity = "/Registry"

    self.__userDN = ''
    self.__userGroup = ''
    self.__rpcClient = None
    self.__csMod = None

    self.__initialized = S_ERROR( "Not initialized" )
    self.initialize()
    if not self.__initialized[ 'OK' ]:
      gLogger.error( self.__initialized )

  def __getProxyID( self ):
    proxyLocation = Locations.getProxyLocation()
    if not proxyLocation:
      gLogger.error( "No proxy found!" )
      return False
    chain = X509Chain()
    if not chain.loadProxyFromFile( proxyLocation ):
      gLogger.error( "Can't read proxy!", proxyLocation )
      return False
    retVal = chain.getIssuerCert()
    if not retVal[ 'OK' ]:
      gLogger.error( "Can't parse proxy!", retVal[ 'Message' ] )
      return False
    idCert = retVal[ 'Value' ]
    self.__userDN = idCert.getSubjectDN()[ 'Value' ]
    self.__userGroup = idCert.getDIRACGroup()[ 'Value' ]
    return True

  def __getCertificateID( self ):
    certLocation = Locations.getHostCertificateAndKeyLocation()
    if not certLocation:
      gLogger.error( "No certificate found!" )
      return False
    chain = X509Chain()
    retVal = chain.loadChainFromFile( certLocation[ 0 ] )
    if not retVal[ 'OK' ]:
      gLogger.error( "Can't parse certificate!", retVal[ 'Message' ] )
      return False
    idCert = chain.getIssuerCert()[ 'Value' ]
    self.__userDN = idCert.getSubjectDN()[ 'Value' ]
    self.__userGroup = 'host'
    return True

  def initialize( self ):
    if self.__initialized[ 'OK' ]:
      return self.__initialized
    if not gConfig.useServerCertificate():
      res = self.__getProxyID()
    else:
      res = self.__getCertificateID()
    if not res:
      self.__initialized = S_ERROR( "Cannot locate client credentials" )
      return self.__initialized
    retVal = gConfig.getOption( "/DIRAC/Configuration/MasterServer" )
    if not retVal[ 'OK' ]:
      self.__initialized = S_ERROR( "Master server is not known. Is everything initialized?" )
      return self.__initialized
    self.__rpcClient = RPCClient( gConfig.getValue( "/DIRAC/Configuration/MasterServer", "" ) )
    self.__csMod = Modificator( self.__rpcClient, "%s - %s" % ( self.__userGroup, self.__userDN ) )
    retVal = self.downloadCSData()
    if not retVal[ 'OK' ]:
      self.__initialized = S_ERROR( "Can not download the remote cfg. Is everything initialized?" )
      return self.__initialized
    self.__initialized = S_OK()
    return self.__initialized

  def downloadCSData( self ):
    if not self.__csMod:
      return S_ERROR( "CSAPI not yet initialized" )
    result = self.__csMod.loadFromRemote()
    if not result[ 'OK' ]:
      return result
    self.__csModified = False
    self.__csMod.updateGConfigurationData()
    return S_OK()

  def listUsers( self , group = False ):
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    if not group:
      return S_OK( self.__csMod.getSections( "%s/Users" % self.__baseSecurity ) )
    else:
      users = self.__csMod.getValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) )
      if not users:
        return S_OK( [] )
      else:
        return S_OK( List.fromChar( users ) )

  def listHosts( self ):
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    return S_OK( self.__csMod.getSections( "%s/Hosts" % self.__baseSecurity ) )

  def describeUsers( self, users = False ):
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    return S_OK( self.__describeEntity( users ) )

  def describeHosts( self, hosts = False ):
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    return S_OK( self.__describeEntity( hosts, True ) )

  def __describeEntity( self, mask, hosts = False ):
    if hosts:
      csSection = "%s/Hosts" % self.__baseSecurity
    else:
      csSection = "%s/Users" % self.__baseSecurity
    if mask:
      entities = [ entity for entity in self.__csMod.getSections( csSection ) if entity in mask ]
    else:
      entities = self.__csMod.getSections( csSection )
    entitiesDict = {}
    for entity in entities:
      entitiesDict[ entity ] = {}
      for option in self.__csMod.getOptions( "%s/%s" % ( csSection, entity ) ):
        entitiesDict[ entity ][ option ] = self.__csMod.getValue( "%s/%s/%s" % ( csSection, entity, option ) )
      if not hosts:
        groupsDict = self.describeGroups()[ 'Value' ]
        entitiesDict[ entity ][ 'Groups' ] = []
        for group in groupsDict:
          if 'Users' in groupsDict[ group ] and entity in groupsDict[ group ][ 'Users' ]:
            entitiesDict[ entity ][ 'Groups' ].append( group )
        entitiesDict[ entity ][ 'Groups' ].sort()
    return entitiesDict

  def listGroups( self ):
    """
    List all groups
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    return S_OK( self.__csMod.getSections( "%s/Groups" % self.__baseSecurity ) )

  def describeGroups( self, mask = False ):
    """
    List all groups that are in the mask (or all if no mask) with their properties
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    groups = [ group for group in self.__csMod.getSections( "%s/Groups" % self.__baseSecurity ) if not mask or ( mask and group in mask ) ]
    groupsDict = {}
    for group in groups:
      groupsDict[ group ] = {}
      for option in self.__csMod.getOptions( "%s/Groups/%s" % ( self.__baseSecurity, group ) ):
        groupsDict[ group ][ option ] = self.__csMod.getValue( "%s/Groups/%s/%s" % ( self.__baseSecurity, group, option ) )
        if option in ( "Users", "Properties" ):
          groupsDict[ group ][ option ] = List.fromChar( groupsDict[ group ][ option ] )
    return S_OK( groupsDict )

  def deleteUsers( self, users ):
    """
    Delete a user/s can receive as a param either a string or a list
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    if type( users ) == types.StringType:
      users = [ users ]
    usersData = self.describeUsers( users )['Value']
    for username in users:
      if not username in usersData:
        gLogger.warn( "User %s does not exist" )
        continue
      userGroups = usersData[ username ][ 'Groups' ]
      for group in userGroups:
        self.__removeUserFromGroup( group, username )
        gLogger.info( "Deleted user %s from group %s" % ( username, group ) )
      self.__csMod.removeSection( "%s/Users/%s" % ( self.__baseSecurity, username ) )
      gLogger.info( "Deleted user %s" % username )
      self.__csModified = True
    return S_OK( True )

  def __removeUserFromGroup( self, group, username ):
    """
    Remove user from a group
    """
    usersInGroup = self.__csMod.getValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) )
    if usersInGroup != None:
      userList = List.fromChar( usersInGroup, "," )
      userPos = userList.index( username )
      userList.pop( userPos )
      self.__csMod.setOptionValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ), ",".join( userList ) )

  def __addUserToGroup( self, group, username ):
    """
    Add user to a group
    """
    usersInGroup = self.__csMod.getValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) )
    if usersInGroup != None:
      userList = List.fromChar( usersInGroup )
      if username not in userList:
        userList.append( username )
        self.__csMod.setOptionValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ), ",".join( userList ) )
      else:
        gLogger.warn( "User %s is already in group %s" % ( username, group ) )

  def addUser( self, username, properties ):
    """
    Add a user to the cs
      - username
      - properties is a dict with keys:
        - DN
        - groups
        - <extra params>
    Returns True/False
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    for prop in ( "DN", "Groups" ):
      if prop not in properties:
        gLogger.error( "Missing %s property for user %s" % ( prop, username ) )
        return S_OK( False )
    if username in self.listUsers()['Value']:
      gLogger.error( "User %s is already registered" % username )
      return S_OK( False )
    groups = self.listGroups()['Value']
    for userGroup in properties[ 'Groups' ]:
      if not userGroup in groups:
        gLogger.error( "User %s group %s is not a valid group" % ( username, userGroup ) )
        return S_OK( False )
    self.__csMod.createSection( "%s/Users/%s" % ( self.__baseSecurity, username ) )
    for prop in properties:
      if prop == "Groups":
        continue
      self.__csMod.setOptionValue( "%s/Users/%s/%s" % ( self.__baseSecurity, username, prop ), properties[ prop ] )
    for userGroup in properties[ 'Groups' ]:
      gLogger.info( "Added user %s to group %s" % ( username, userGroup ) )
      self.__addUserToGroup( userGroup, username )
    gLogger.info( "Registered user %s" % username )
    self.__csModified = True
    return S_OK( True )

  def modifyUser( self, username, properties, createIfNonExistant = False ):
    """
    Modify a user
      - username
      - properties is a dict with keys:
        - DN
        - Groups
        - <extra params>
    Returns True/False
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    modifiedUser = False
    userData = self.describeUsers( [ username ] )['Value']
    if username not in userData:
      if createIfNonExistant:
        gLogger.info( "Registering user %s" % username )
        return self.addUser( username, properties )
      gLogger.error( "User %s is not registered" % username )
      return S_OK( False )
    for prop in properties:
      if prop == "Groups":
        continue
      prevVal = self.__csMod.getValue( "%s/Users/%s/%s" % ( self.__baseSecurity, username, prop ) )
      if not prevVal or prevVal != properties[ prop ]:
        gLogger.info( "Setting %s property for user %s to %s" % ( prop, username, properties[ prop ] ) )
        self.__csMod.setOptionValue( "%s/Users/%s/%s" % ( self.__baseSecurity, username, prop ), properties[ prop ] )
        modifiedUser = True
    if 'Groups' in properties:
      groups = self.listGroups()['Value']
      for userGroup in properties[ 'Groups' ]:
        if not userGroup in groups:
          gLogger.error( "User %s group %s is not a valid group" % ( username, userGroup ) )
          return S_OK( False )
      groupsToBeDeletedFrom = []
      groupsToBeAddedTo = []
      for prevGroup in userData[ username ][ 'Groups' ]:
        if prevGroup not in properties[ 'Groups' ]:
          groupsToBeDeletedFrom.append( prevGroup )
          modifiedUser = True
      for newGroup in properties[ 'Groups' ]:
        if newGroup not in userData[ username ][ 'Groups' ]:
          groupsToBeAddedTo.append( newGroup )
          modifiedUser = True
      for group in groupsToBeDeletedFrom:
        self.__removeUserFromGroup( group, username )
        gLogger.info( "Removed user %s from group %s" % ( username, group ) )
      for group in groupsToBeAddedTo:
        self.__addUserToGroup( group, username )
        gLogger.info( "Added user %s to group %s" % ( username, group ) )
    if modifiedUser:
      gLogger.info( "Modified user %s" % username )
      self.__csModified = True
    else:
      gLogger.info( "Nothing to modify for user %s" % username )
    return S_OK( True )

  def addGroup( self, groupname, properties ):
    """
    Add a group to the cs
      - groupname
      - properties is a dict with keys:
        - Users
        - Properties
        - <extra params>
    Returns True/False
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    if groupname in self.listGroups()['Value']:
      gLogger.error( "Group %s is already registered" % groupname )
      return S_OK( False )
    self.__csMod.createSection( "%s/Groups/%s" % ( self.__baseSecurity, groupname ) )
    for prop in properties:
      self.__csMod.setOptionValue( "%s/Groups/%s/%s" % ( self.__baseSecurity, groupname, prop ), properties[ prop ] )
    gLogger.info( "Registered group %s" % groupname )
    self.__csModified = True
    return S_OK( True )

  def modifyGroup( self, groupname, properties, createIfNonExistant = False ):
    """
    Modify a user
      - groupname
      - properties is a dict with keys:
        - Users
        - Properties
        - <extra params>
    Returns True/False
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    modifiedGroup = False
    groupData = self.describeGroups( [ groupname ] )['Value']
    if groupname not in groupData:
      if createIfNonExistant:
        gLogger.info( "Registering group %s" % groupname )
        return self.addGroup( groupname, properties )
      gLogger.error( "Group %s is not registered" % groupname )
      return S_OK( False )
    for prop in properties:
      prevVal = self.__csMod.getValue( "%s/Groups/%s/%s" % ( self.__baseSecurity, groupname, prop ) )
      if not prevVal or prevVal != properties[ prop ]:
        gLogger.info( "Setting %s property for group %s to %s" % ( prop, groupname, properties[ prop ] ) )
        self.__csMod.setOptionValue( "%s/Groups/%s/%s" % ( self.__baseSecurity, groupname, prop ), properties[ prop ] )
        modifiedGroup = True
    if modifiedGroup:
      gLogger.info( "Modified group %s" % groupname )
      self.__csModified = True
    else:
      gLogger.info( "Nothing to modify for group %s" % groupname )
    return S_OK( True )

  def addHost( self, hostname, properties ):
    """
    Add a host to the cs
      - hostname
      - properties is a dict with keys:
        - DN
        - Properties
        - <extra params>
    Returns True/False
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    for prop in ( "DN", ):
      if prop not in properties:
        gLogger.error( "Missing %s property for host %s" % ( prop, hostname ) )
        return S_OK( False )
    if hostname in self.listHosts()['Value']:
      gLogger.error( "Host %s is already registered" % hostname )
      return S_OK( False )
    self.__csMod.createSection( "%s/Hosts/%s" % ( self.__baseSecurity, hostname ) )
    for prop in properties:
      self.__csMod.setOptionValue( "%s/Hosts/%s/%s" % ( self.__baseSecurity, hostname, prop ), properties[ prop ] )
    gLogger.info( "Registered host %s" % hostname )
    self.__csModified = True
    return S_OK( True )

  def modifyHost( self, hostname, properties, createIfNonExistant = False ):
    """
    Modify a user
      - hostname
      - properties is a dict with keys:
        - DN
        - Properties
        - <extra params>
    Returns True/False
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    modifiedHost = False
    hostData = self.describeHosts( [ hostname ] )['Value']
    if hostname not in hostData:
      if createIfNonExistant:
        gLogger.info( "Registering host %s" % hostname )
        return self.addHost( hostname, properties )
      gLogger.error( "Host %s is not registered" % hostname )
      return S_OK( False )
    for prop in properties:
      prevVal = self.__csMod.getValue( "%s/Hosts/%s/%s" % ( self.__baseSecurity, hostname, prop ) )
      if not prevVal or prevVal != properties[ prop ]:
        gLogger.info( "Setting %s property for host %s to %s" % ( prop, hostname, properties[ prop ] ) )
        self.__csMod.setOptionValue( "%s/Hosts/%s/%s" % ( self.__baseSecurity, hostname, prop ), properties[ prop ] )
        modifiedHost = True
    if modifiedHost:
      gLogger.info( "Modified host %s" % hostname )
      self.__csModified = True
    else:
      gLogger.info( "Nothing to modify for host %s" % hostname )
    return S_OK( True )

  def syncUsersWithCFG( self, usersCFG ):
    """
    Sync users with the cfg contents. Usernames have to be sections containing
    DN, Groups, and extra properties as parameters
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    done = True
    for user in usersCFG.listSections():
      properties = {}
      propList = usersCFG[ user ].listOptions()
      for prop in propList:
        if prop == "Groups":
          properties[ prop ] = List.fromChar( usersCFG[ user ][ prop ] )
        else:
          properties[ prop ] = usersCFG[ user ][ prop ]
      if not self.modifyUser( user, properties, createIfNonExistant = True ):
        done = False
    return S_OK( done )

  def sortUsersAndGroups( self ):
    self.__csMod.sortAlphabetically( "%s/Users" % self.__baseSecurity )
    self.__csMod.sortAlphabetically( "%s/Hosts" % self.__baseSecurity )
    for group in self.__csMod.getSections( "%s/Groups" % self.__baseSecurity ):
      usersOptionPath = "%s/Groups/%s/Users" % ( self.__baseSecurity, group )
      users = self.__csMod.getValue( usersOptionPath )
      usersList = List.fromChar( users )
      usersList.sort()
      sortedUsers = ", ".join( usersList )
      if users != sortedUsers:
        self.__csMod.setOptionValue( usersOptionPath, sortedUsers )

  def checkForUnexistantUsersInGroups( self ):
    allUsers = self.__csMod.getSections( "%s/Users" % self.__baseSecurity )
    allGroups = self.__csMod.getSections( "%s/Groups" % self.__baseSecurity )
    for group in allGroups:
      usersInGroup = self.__csMod.getValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) )
      if usersInGroup:
        filteredUsers = []
        usersInGroup = List.fromChar( usersInGroup )
        for user in usersInGroup:
          if user in allUsers:
            filteredUsers.append( user )
        self.__csMod.setOptionValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ),
                                     ",".join( filteredUsers ) )

  def commitChanges( self, sortUsers = True ):
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    if self.__csModified:
      self.checkForUnexistantUsersInGroups()
      if sortUsers:
        self.sortUsersAndGroups()
      retVal = self.__csMod.commit()
      if not retVal[ 'OK' ]:
        gLogger.error( "Can't commit new data: %s" % retVal[ 'Message' ] )
        return retVal
      return self.downloadCSData()
    return S_OK()

  def commit( self ):
    """ Commit the accumulated changes to the CS server
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    if self.__csModified:
      retVal = self.__csMod.commit()
      if not retVal[ 'OK' ]:
        gLogger.error( "Can't commit new data: %s" % retVal[ 'Message' ] )
        return retVal
      return self.downloadCSData()
    return S_OK()

  def mergeFromCFG( self, cfg ):
    """ Merge the internal CFG data with the input
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    self.__csMod.mergeFromCFG( cfg )
    self.__csModified = True
    return S_OK()

  def modifyValue( self, optionPath, newValue ):
    """Modify an existing value at the specified options path.
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    prevVal = self.__csMod.getValue( optionPath )
    if not prevVal:
      return S_ERROR( 'Trying to set %s to %s but option does not exist' % ( optionPath, newValue ) )
    gLogger.verbose( "Changing %s from \n%s \nto \n%s" % ( optionPath, prevVal, newValue ) )
    self.__csMod.setOptionValue( optionPath, newValue )
    self.__csModified = True
    return S_OK( 'Modified %s' % optionPath )

  def setOption( self, optionPath, optionValue ):
    """Create an option at the specified path.
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    self.__csMod.setOptionValue( optionPath, optionValue )
    self.__csModified = True
    return S_OK( 'Created new option %s = %s' % ( optionPath, optionValue ) )


  def setOptionComment( self, optionPath, comment ):
    """Create an option at the specified path.
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    self.__csMod.setComment( optionPath, comment )
    self.__csModified = True
    return S_OK( 'Set option comment %s : %s' % ( optionPath, comment ) )

  def delOption( self, optionPath ):
    """ Delete an option
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    if not self.__csMod.removeOption( optionPath ):
      return S_ERROR( "Couldn't delete option %s" % optionPath )
    self.__csModified = True
    return S_OK( 'Deleted option %s' % ( optionPath ) )

  def createSection( self, sectionPath, comment = "" ):
    """ Create a new section
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    self.__csMod.createSection( sectionPath )
    self.__csModified = True
    if comment:
      self.__csMod.setComment( sectionPath, comment )
    return S_OK()

  def delSection( self, sectionPath ):
    """ Delete a section
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    if not self.__csMod.removeSection( sectionPath ):
      return S_ERROR( "Could not delete section %s " % sectionPath )
    self.__csModified = True
    return S_OK()

  def mergeCFGUnderSection( self, sectionPath, cfg ):
    """ Merge the given cfg under a certain section
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    result = self.createSection( sectionPath )
    if not result[ 'OK' ]:
      return result
    if not self.__csMod.mergeSectionFromCFG( sectionPath, cfg ):
      return S_ERROR( "Could not merge cfg into section %s" % sectionPath )
    self.__csModified = True
    return S_OK()

  def mergeWithCFG( self, cfg ):
    """ Merge the given cfg with the current config
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    self.__csMod.mergeFromCFG( cfg )
    self.__csModified = True
    return S_OK()

  def getCurrentCFG( self ):
    """ Get the current CFG as it is
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    return S_OK( self.__csMod.getCFG() )
Exemple #23
0
class CSAPI(object):
    """ CSAPI objects need an initialization phase
  """
    def __init__(self):
        """
    Initialization function
    """
        self.csModified = False
        self.__baseSecurity = "/Registry"
        self.__baseResources = "/Resources"

        self.__userDN = ''
        self.__userGroup = ''
        self.__rpcClient = None
        self.__csMod = None

        self.__initialized = S_ERROR("Not initialized")
        self.initialize()
        if not self.__initialized['OK']:
            gLogger.error(self.__initialized)

    def __getProxyID(self):
        proxyLocation = Locations.getProxyLocation()
        if not proxyLocation:
            gLogger.error("No proxy found!")
            return False
        chain = X509Chain()
        if not chain.loadProxyFromFile(proxyLocation):
            gLogger.error("Can't read proxy!", proxyLocation)
            return False
        retVal = chain.getIssuerCert()
        if not retVal['OK']:
            gLogger.error("Can't parse proxy!", retVal['Message'])
            return False
        idCert = retVal['Value']
        self.__userDN = idCert.getSubjectDN()['Value']
        self.__userGroup = chain.getDIRACGroup()['Value']
        return True

    def __getCertificateID(self):
        certLocation = Locations.getHostCertificateAndKeyLocation()
        if not certLocation:
            gLogger.error("No certificate found!")
            return False
        chain = X509Chain()
        retVal = chain.loadChainFromFile(certLocation[0])
        if not retVal['OK']:
            gLogger.error("Can't parse certificate!", retVal['Message'])
            return False
        idCert = chain.getIssuerCert()['Value']
        self.__userDN = idCert.getSubjectDN()['Value']
        self.__userGroup = 'host'
        return True

    def initialize(self):
        if self.__initialized['OK']:
            return self.__initialized
        if not gConfig.useServerCertificate():
            res = self.__getProxyID()
        else:
            res = self.__getCertificateID()
        if not res:
            self.__initialized = S_ERROR("Cannot locate client credentials")
            return self.__initialized
        retVal = gConfig.getOption("/DIRAC/Configuration/MasterServer")
        if not retVal['OK']:
            self.__initialized = S_ERROR(
                "Master server is not known. Is everything initialized?")
            return self.__initialized
        self.__rpcClient = RPCClient(
            gConfig.getValue("/DIRAC/Configuration/MasterServer", ""))
        self.__csMod = Modificator(
            self.__rpcClient,
            "%s - %s - %s" % (self.__userGroup, self.__userDN,
                              Time.dateTime().strftime("%Y-%m-%d %H:%M:%S")))
        retVal = self.downloadCSData()
        if not retVal['OK']:
            self.__initialized = S_ERROR(
                "Can not download the remote cfg. Is everything initialized?")
            return self.__initialized
        self.__initialized = S_OK()
        return self.__initialized

    def downloadCSData(self):
        if not self.__csMod:
            return S_ERROR("CSAPI not yet initialized")
        result = self.__csMod.loadFromRemote()
        if not result['OK']:
            return result
        self.csModified = False
        self.__csMod.updateGConfigurationData()
        return S_OK()

    # Resources-related methods
    #########################################

    def addSite(self, siteName, optionsDict=None):
        """ Adds a new site to the CS.
      A site is a container for services, so after calling this function,
      at least addCEtoSite() should be called.

      :param str siteName: FQN of the site (e.g. LCG.CERN.ch)
      :param dict optionsDict: optional dictionary of options
      :returns: S_OK/S_ERROR structure
    """

        if not self.__initialized['OK']:
            return self.__initialized

        self.__csMod.createSection(cfgPath(self.__baseResources, 'Sites'))
        self.__csMod.createSection(
            cfgPath(self.__baseResources, 'Sites',
                    siteName.split('.')[0]))
        self.__csMod.createSection(
            cfgPath(self.__baseResources, 'Sites',
                    siteName.split('.')[0], siteName))
        # add options if requested
        if optionsDict is not None:
            for option, optionValue in optionsDict.items(
            ):  # can be an iterator
                self.__csMod.setOptionValue(
                    cfgPath(self.__baseResources, 'Sites',
                            siteName.split('.')[0], option), optionValue)
        self.csModified = True
        return S_OK(True)

    def addCEtoSite(self, siteName, ceName, optionsDict=None):
        """ Adds a new CE to a site definition in the CS.
        A CE normally has queues, so addQueueToCE should be called after this one.

        :param str siteName: FQN of the site (e.g. LCG.CERN.ch)
        :param str ceName: FQN of the CE (e.g. ce503.cern.ch)
        :param dict optionsDict: optional dictionary of options
        :returns: S_OK/S_ERROR structure
    """
        res = getSites()
        if not res['OK']:
            return res
        if siteName not in res['Value']:
            res = self.addSite(siteName)
            if not res['OK']:
                return res

        # CSAPI.createSection() always returns S_OK even if the section already exists
        self.__csMod.createSection(
            cfgPath(self.__baseResources, 'Sites',
                    siteName.split('.')[0], siteName, 'CEs'))
        self.__csMod.createSection(
            cfgPath(self.__baseResources, 'Sites',
                    siteName.split('.')[0], siteName, 'CEs', ceName))
        # add options if requested
        if optionsDict is not None:
            for option, optionValue in optionsDict.items(
            ):  # can be an iterator
                self.__csMod.setOptionValue(
                    cfgPath(self.__baseResources, 'Sites',
                            siteName.split('.')[0], siteName, 'CEs', ceName,
                            option), optionValue)
        self.csModified = True
        return S_OK(True)

    def addQueueToCE(self, ceName, queueName, optionsDict=None):
        """ Adds a new queue to a CE definition in the CS.

        :param str ceName: FQN of the CE (e.g. ce503.cern.ch)
        :param str queueName: name of the queue (e.g. ce503.cern.ch-condor)
        :param dict optionsDict: optional dictionary of options
        :returns: S_OK/S_ERROR structure
    """
        res = getCESiteMapping(ceName)
        if not res['OK']:
            return res
        if ceName not in res['Value']:
            return S_ERROR("CE does not exist")
        siteName = res['Value'][ceName]

        # CSAPI.createSection() always returns S_OK even if the section already exists
        self.__csMod.createSection(
            cfgPath(self.__baseResources, 'Sites',
                    siteName.split('.')[0], siteName, 'CEs', ceName, 'Queues',
                    queueName))
        # add options if requested
        if optionsDict is not None:
            for option, optionValue in optionsDict.items(
            ):  # can be an iterator
                self.__csMod.setOptionValue(
                    cfgPath(self.__baseResources, 'Sites',
                            siteName.split('.')[0], siteName, 'CEs', ceName,
                            'Queues', queueName, option), optionValue)
        self.csModified = True
        return S_OK(True)

    # Registry-related methods
    #########################################

    def listUsers(self, group=False):
        if not self.__initialized['OK']:
            return self.__initialized
        if not group:
            return S_OK(
                self.__csMod.getSections("%s/Users" % self.__baseSecurity))
        users = self.__csMod.getValue("%s/Groups/%s/Users" %
                                      (self.__baseSecurity, group))
        if not users:
            return S_OK([])
        return S_OK(List.fromChar(users))

    def listHosts(self):
        if not self.__initialized['OK']:
            return self.__initialized
        return S_OK(self.__csMod.getSections("%s/Hosts" % self.__baseSecurity))

    def describeUsers(self, users=None):
        """ describe users by nickname

        :param list users: list of users' nicknames
        :return: a S_OK(description) of the users in input
    """
        if users is None:
            users = []
        if not self.__initialized['OK']:
            return self.__initialized
        return S_OK(self.__describeEntity(users))

    def describeHosts(self, hosts=None):
        if hosts is None:
            hosts = []
        if not self.__initialized['OK']:
            return self.__initialized
        return S_OK(self.__describeEntity(hosts, True))

    def __describeEntity(self, mask, hosts=False):
        if hosts:
            csSection = "%s/Hosts" % self.__baseSecurity
        else:
            csSection = "%s/Users" % self.__baseSecurity
        if mask:
            entities = [
                entity for entity in self.__csMod.getSections(csSection)
                if entity in mask
            ]
        else:
            entities = self.__csMod.getSections(csSection)
        entitiesDict = {}
        for entity in entities:
            entitiesDict[entity] = {}
            for option in self.__csMod.getOptions("%s/%s" %
                                                  (csSection, entity)):
                entitiesDict[entity][option] = self.__csMod.getValue(
                    "%s/%s/%s" % (csSection, entity, option))
            if not hosts:
                groupsDict = self.describeGroups()['Value']
                entitiesDict[entity]['Groups'] = []
                for group in groupsDict:
                    if 'Users' in groupsDict[group] and entity in groupsDict[
                            group]['Users']:
                        entitiesDict[entity]['Groups'].append(group)
                entitiesDict[entity]['Groups'].sort()
        return entitiesDict

    def listGroups(self):
        """
    List all groups
    """
        if not self.__initialized['OK']:
            return self.__initialized
        return S_OK(self.__csMod.getSections("%s/Groups" %
                                             self.__baseSecurity))

    def describeGroups(self, mask=None):
        """
    List all groups that are in the mask (or all if no mask) with their properties
    """
        if mask is None:
            mask = []
        if not self.__initialized['OK']:
            return self.__initialized
        groups = [
            group for group in self.__csMod.getSections("%s/Groups" %
                                                        self.__baseSecurity)
            if not mask or (mask and group in mask)
        ]
        groupsDict = {}
        for group in groups:
            groupsDict[group] = {}
            for option in self.__csMod.getOptions(
                    "%s/Groups/%s" % (self.__baseSecurity, group)):
                groupsDict[group][option] = self.__csMod.getValue(
                    "%s/Groups/%s/%s" % (self.__baseSecurity, group, option))
                if option in ("Users", "Properties"):
                    groupsDict[group][option] = List.fromChar(
                        groupsDict[group][option])
        return S_OK(groupsDict)

    def deleteUsers(self, users):
        """
    Delete a user/s can receive as a param either a string or a list
    """
        if not self.__initialized['OK']:
            return self.__initialized
        if isinstance(users, six.string_types):
            users = [users]
        usersData = self.describeUsers(users)['Value']
        for username in users:
            if username not in usersData:
                gLogger.warn("User %s does not exist")
                continue
            userGroups = usersData[username]['Groups']
            for group in userGroups:
                self.__removeUserFromGroup(group, username)
                gLogger.info("Deleted user %s from group %s" %
                             (username, group))
            self.__csMod.removeSection("%s/Users/%s" %
                                       (self.__baseSecurity, username))
            gLogger.info("Deleted user %s" % username)
            self.csModified = True
        return S_OK(True)

    def __removeUserFromGroup(self, group, username):
        """
    Remove user from a group
    """
        usersInGroup = self.__csMod.getValue("%s/Groups/%s/Users" %
                                             (self.__baseSecurity, group))
        if usersInGroup is not None:
            userList = List.fromChar(usersInGroup, ",")
            userPos = userList.index(username)
            userList.pop(userPos)
            self.__csMod.setOptionValue(
                "%s/Groups/%s/Users" % (self.__baseSecurity, group),
                ",".join(userList))

    def __addUserToGroup(self, group, username):
        """
    Add user to a group
    """
        usersInGroup = self.__csMod.getValue("%s/Groups/%s/Users" %
                                             (self.__baseSecurity, group))
        if usersInGroup is not None:
            userList = List.fromChar(usersInGroup)
            if username not in userList:
                userList.append(username)
                self.__csMod.setOptionValue(
                    "%s/Groups/%s/Users" % (self.__baseSecurity, group),
                    ",".join(userList))
            else:
                gLogger.warn("User %s is already in group %s" %
                             (username, group))

    def addUser(self, username, properties):
        """
    Add a user to the cs

    :param str username: username
    :param dict properties: dictionary describing user properties:

      - DN
      - groups
      - <extra params>

    :return: True/False
    """
        if not self.__initialized['OK']:
            return self.__initialized
        for prop in ("DN", "Groups"):
            if prop not in properties:
                gLogger.error("Missing property for user",
                              "%s: %s" % (prop, username))
                return S_OK(False)
        if username in self.listUsers()['Value']:
            gLogger.error("User is already registered", username)
            return S_OK(False)
        groups = self.listGroups()['Value']
        for userGroup in properties['Groups']:
            if userGroup not in groups:
                gLogger.error("User group is not a valid group",
                              "%s %s" % (username, userGroup))
                return S_OK(False)
        self.__csMod.createSection("%s/Users/%s" %
                                   (self.__baseSecurity, username))
        for prop in properties:
            if prop == "Groups":
                continue
            self.__csMod.setOptionValue(
                "%s/Users/%s/%s" % (self.__baseSecurity, username, prop),
                properties[prop])
        for userGroup in properties['Groups']:
            gLogger.info("Added user %s to group %s" % (username, userGroup))
            self.__addUserToGroup(userGroup, username)
        gLogger.info("Registered user %s" % username)
        self.csModified = True
        return S_OK(True)

    def modifyUser(self, username, properties, createIfNonExistant=False):
        """
    Modify a user

    :param str username: group name
    :param dict properties: dictionary describing user properties:

        - DN
        - Groups
        - <extra params>

    :param bool createIfNonExistant: if true, registers the users if it did not exist
    :return: S_OK, Value = True/False
    """
        if not self.__initialized['OK']:
            return self.__initialized
        modifiedUser = False
        userData = self.describeUsers([username])['Value']
        if username not in userData:
            if createIfNonExistant:
                gLogger.info("Registering user %s" % username)
                return self.addUser(username, properties)
            gLogger.error("User is not registered", username)
            return S_OK(False)
        for prop in properties:
            if prop == "Groups":
                continue
            prevVal = self.__csMod.getValue(
                "%s/Users/%s/%s" % (self.__baseSecurity, username, prop))
            if not prevVal or prevVal != properties[prop]:
                gLogger.info("Setting %s property for user %s to %s" %
                             (prop, username, properties[prop]))
                self.__csMod.setOptionValue(
                    "%s/Users/%s/%s" % (self.__baseSecurity, username, prop),
                    properties[prop])
                modifiedUser = True
        if 'Groups' in properties:
            groups = self.listGroups()['Value']
            for userGroup in properties['Groups']:
                if userGroup not in groups:
                    gLogger.error("User group is not a valid group",
                                  "%s %s" % (username, userGroup))
                    return S_OK(False)
            groupsToBeDeletedFrom = []
            groupsToBeAddedTo = []
            for prevGroup in userData[username]['Groups']:
                if prevGroup not in properties['Groups']:
                    groupsToBeDeletedFrom.append(prevGroup)
                    modifiedUser = True
            for newGroup in properties['Groups']:
                if newGroup not in userData[username]['Groups']:
                    groupsToBeAddedTo.append(newGroup)
                    modifiedUser = True
            for group in groupsToBeDeletedFrom:
                self.__removeUserFromGroup(group, username)
                gLogger.info("Removed user %s from group %s" %
                             (username, group))
            for group in groupsToBeAddedTo:
                self.__addUserToGroup(group, username)
                gLogger.info("Added user %s to group %s" % (username, group))
        modified = False
        if modifiedUser:
            modified = True
            gLogger.info("Modified user %s" % username)
            self.csModified = True
        else:
            gLogger.info("Nothing to modify for user %s" % username)
        return S_OK(modified)

    def addGroup(self, groupname, properties):
        """
    Add a group to the cs

    :param str groupname: group name
    :param dict properties: dictionary describing group properties:

        - Users
        - Properties
        - <extra params>

    :return: S_OK, Value = True/False
    """
        if not self.__initialized['OK']:
            return self.__initialized
        if groupname in self.listGroups()['Value']:
            gLogger.error("Group is already registered", groupname)
            return S_OK(False)
        self.__csMod.createSection("%s/Groups/%s" %
                                   (self.__baseSecurity, groupname))
        for prop in properties:
            self.__csMod.setOptionValue(
                "%s/Groups/%s/%s" % (self.__baseSecurity, groupname, prop),
                properties[prop])
        gLogger.info("Registered group %s" % groupname)
        self.csModified = True
        return S_OK(True)

    def modifyGroup(self, groupname, properties, createIfNonExistant=False):
        """
    Modify a group

    :param str groupname: group name
    :param dict properties: dictionary describing group properties:

        - Users
        - Properties
        - <extra params>

    :param bool createIfNonExistant: if true, creates the group if it did not exist
    :return: S_OK, Value = True/False
    """
        if not self.__initialized['OK']:
            return self.__initialized
        modifiedGroup = False
        groupData = self.describeGroups([groupname])['Value']
        if groupname not in groupData:
            if createIfNonExistant:
                gLogger.info("Registering group %s" % groupname)
                return self.addGroup(groupname, properties)
            gLogger.error("Group is not registered", groupname)
            return S_OK(False)
        for prop in properties:
            prevVal = self.__csMod.getValue(
                "%s/Groups/%s/%s" % (self.__baseSecurity, groupname, prop))
            if not prevVal or prevVal != properties[prop]:
                gLogger.info("Setting %s property for group %s to %s" %
                             (prop, groupname, properties[prop]))
                self.__csMod.setOptionValue(
                    "%s/Groups/%s/%s" % (self.__baseSecurity, groupname, prop),
                    properties[prop])
                modifiedGroup = True
        if modifiedGroup:
            gLogger.info("Modified group %s" % groupname)
            self.csModified = True
        else:
            gLogger.info("Nothing to modify for group %s" % groupname)
        return S_OK(True)

    def addHost(self, hostname, properties):
        """
    Add a host to the cs

    :param str hostname: host name
    :param dict properties: dictionary describing host properties:

        - DN
        - Properties
        - <extra params>

    :return: S_OK, Value = True/False
    """
        if not self.__initialized['OK']:
            return self.__initialized
        for prop in ("DN", ):
            if prop not in properties:
                gLogger.error("Missing property for host",
                              "%s %s" % (prop, hostname))
                return S_OK(False)
        if hostname in self.listHosts()['Value']:
            gLogger.error("Host is already registered", hostname)
            return S_OK(False)
        self.__csMod.createSection("%s/Hosts/%s" %
                                   (self.__baseSecurity, hostname))
        for prop in properties:
            self.__csMod.setOptionValue(
                "%s/Hosts/%s/%s" % (self.__baseSecurity, hostname, prop),
                properties[prop])
        gLogger.info("Registered host %s" % hostname)
        self.csModified = True
        return S_OK(True)

    def addShifter(self, shifters=None):
        """
    Adds or modify one or more shifters. Also, adds the shifter section in case this is not present.
    Shifter identities are used in several places, mostly for running agents

    :param dict shifters: has to be in the form {'ShifterRole':{'User':'******', 'Group':'aDIRACGroup'}}

    :return: S_OK/S_ERROR
    """
        def getOpsSection():
            """
      Where is the shifters section?
      """
            vo = CSGlobals.getVO()
            setup = CSGlobals.getSetup()

            if vo:
                res = gConfig.getSections('/Operations/%s/%s/Shifter' %
                                          (vo, setup))
                if res['OK']:
                    return S_OK('/Operations/%s/%s/Shifter' % (vo, setup))

                res = gConfig.getSections('/Operations/%s/Defaults/Shifter' %
                                          vo)
                if res['OK']:
                    return S_OK('/Operations/%s/Defaults/Shifter' % vo)

            else:
                res = gConfig.getSections('/Operations/%s/Shifter' % setup)
                if res['OK']:
                    return S_OK('/Operations/%s/Shifter' % setup)

                res = gConfig.getSections('/Operations/Defaults/Shifter')
                if res['OK']:
                    return S_OK('/Operations/Defaults/Shifter')

            return S_ERROR("No shifter section")

        if shifters is None:
            shifters = {}
        if not self.__initialized['OK']:
            return self.__initialized

        # get current shifters
        opsH = Operations()
        currentShifterRoles = opsH.getSections('Shifter')
        if not currentShifterRoles['OK']:
            # we assume the shifter section is not present
            currentShifterRoles = []
        else:
            currentShifterRoles = currentShifterRoles['Value']
        currentShiftersDict = {}
        for currentShifterRole in currentShifterRoles:
            currentShifter = opsH.getOptionsDict('Shifter/%s' %
                                                 currentShifterRole)
            if not currentShifter['OK']:
                return currentShifter
            currentShifter = currentShifter['Value']
            currentShiftersDict[currentShifterRole] = currentShifter

        # Removing from shifters what does not need to be changed
        for sRole in shifters.keys():  # note the pop below
            if sRole in currentShiftersDict:
                if currentShiftersDict[sRole] == shifters[sRole]:
                    shifters.pop(sRole)

        # get shifters section to modify
        section = getOpsSection()

        # Is this section present?
        if not section['OK']:
            if section['Message'] == "No shifter section":
                gLogger.warn(section['Message'])
                gLogger.info("Adding shifter section")
                vo = CSGlobals.getVO()
                if vo:
                    section = '/Operations/%s/Defaults/Shifter' % vo
                else:
                    section = '/Operations/Defaults/Shifter'
                res = self.__csMod.createSection(section)
                if not res:
                    gLogger.error("Section %s not created" % section)
                    return S_ERROR("Section %s not created" % section)
            else:
                gLogger.error(section['Message'])
                return section
        else:
            section = section['Value']

        # add or modify shifters
        for shifter in shifters:
            self.__csMod.removeSection(section + '/' + shifter)
            self.__csMod.createSection(section + '/' + shifter)
            self.__csMod.createSection(section + '/' + shifter + '/' + 'User')
            self.__csMod.createSection(section + '/' + shifter + '/' + 'Group')
            self.__csMod.setOptionValue(section + '/' + shifter + '/' + 'User',
                                        shifters[shifter]['User'])
            self.__csMod.setOptionValue(
                section + '/' + shifter + '/' + 'Group',
                shifters[shifter]['Group'])

        self.csModified = True
        return S_OK(True)

    def modifyHost(self, hostname, properties, createIfNonExistant=False):
        """
    Modify a host

    :param str hostname: hostname name
    :param dict properties: dictionary describing host properties:

        - DN
        - Properties
        - <extra params>

    :param bool createIfNonExistant: if true, creates the host if it did not exist
    :return: S_OK, Value = True/False
    """
        if not self.__initialized['OK']:
            return self.__initialized
        modifiedHost = False
        hostData = self.describeHosts([hostname])['Value']
        if hostname not in hostData:
            if createIfNonExistant:
                gLogger.info("Registering host %s" % hostname)
                return self.addHost(hostname, properties)
            gLogger.error("Host is not registered", hostname)
            return S_OK(False)
        for prop in properties:
            prevVal = self.__csMod.getValue(
                "%s/Hosts/%s/%s" % (self.__baseSecurity, hostname, prop))
            if not prevVal or prevVal != properties[prop]:
                gLogger.info("Setting %s property for host %s to %s" %
                             (prop, hostname, properties[prop]))
                self.__csMod.setOptionValue(
                    "%s/Hosts/%s/%s" % (self.__baseSecurity, hostname, prop),
                    properties[prop])
                modifiedHost = True
        if modifiedHost:
            gLogger.info("Modified host %s" % hostname)
            self.csModified = True
        else:
            gLogger.info("Nothing to modify for host %s" % hostname)
        return S_OK(True)

    def syncUsersWithCFG(self, usersCFG):
        """
    Sync users with the cfg contents. Usernames have to be sections containing
    DN, Groups, and extra properties as parameters
    """
        if not self.__initialized['OK']:
            return self.__initialized
        done = True
        for user in usersCFG.listSections():
            properties = {}
            propList = usersCFG[user].listOptions()
            for prop in propList:
                if prop == "Groups":
                    properties[prop] = List.fromChar(usersCFG[user][prop])
                else:
                    properties[prop] = usersCFG[user][prop]
            if not self.modifyUser(user, properties, createIfNonExistant=True):
                done = False
        return S_OK(done)

    def sortUsersAndGroups(self):
        self.__csMod.sortAlphabetically("%s/Users" % self.__baseSecurity)
        self.__csMod.sortAlphabetically("%s/Hosts" % self.__baseSecurity)
        for group in self.__csMod.getSections("%s/Groups" %
                                              self.__baseSecurity):
            usersOptionPath = "%s/Groups/%s/Users" % (self.__baseSecurity,
                                                      group)
            users = self.__csMod.getValue(usersOptionPath)
            if users:
                usersList = sorted(List.fromChar(users))
                sortedUsers = ", ".join(usersList)
                if users != sortedUsers:
                    self.__csMod.setOptionValue(usersOptionPath, sortedUsers)

    def checkForUnexistantUsersInGroups(self):
        allUsers = self.__csMod.getSections("%s/Users" % self.__baseSecurity)
        allGroups = self.__csMod.getSections("%s/Groups" % self.__baseSecurity)
        for group in allGroups:
            usersInGroup = self.__csMod.getValue("%s/Groups/%s/Users" %
                                                 (self.__baseSecurity, group))
            if usersInGroup:
                filteredUsers = []
                usersInGroup = List.fromChar(usersInGroup)
                for user in usersInGroup:
                    if user in allUsers:
                        filteredUsers.append(user)
                self.__csMod.setOptionValue(
                    "%s/Groups/%s/Users" % (self.__baseSecurity, group),
                    ",".join(filteredUsers))

    def commitChanges(self, sortUsers=True):
        if not self.__initialized['OK']:
            return self.__initialized
        if self.csModified:
            self.checkForUnexistantUsersInGroups()
            if sortUsers:
                self.sortUsersAndGroups()
            retVal = self.__csMod.commit()
            if not retVal['OK']:
                gLogger.error("Can't commit new configuration data",
                              "%s" % retVal['Message'])
                return retVal
            return self.downloadCSData()
        return S_OK()

    def commit(self):
        """ Commit the accumulated changes to the CS server
    """
        if not self.__initialized['OK']:
            return self.__initialized
        if self.csModified:
            retVal = self.__csMod.commit()
            if not retVal['OK']:
                gLogger.error("Can't commit new configuration data",
                              "%s" % retVal['Message'])
                return retVal
            return self.downloadCSData()
        return S_OK()

    def mergeFromCFG(self, cfg):
        """ Merge the internal CFG data with the input
    """
        if not self.__initialized['OK']:
            return self.__initialized
        self.__csMod.mergeFromCFG(cfg)
        self.csModified = True
        return S_OK()

    def modifyValue(self, optionPath, newValue):
        """Modify an existing value at the specified options path.
    """
        if not self.__initialized['OK']:
            return self.__initialized
        prevVal = self.__csMod.getValue(optionPath)
        if prevVal is None:
            return S_ERROR('Trying to set %s to %s but option does not exist' %
                           (optionPath, newValue))
        gLogger.verbose("Changing %s from \n%s \nto \n%s" %
                        (optionPath, prevVal, newValue))
        self.__csMod.setOptionValue(optionPath, newValue)
        self.csModified = True
        return S_OK('Modified %s' % optionPath)

    def setOption(self, optionPath, optionValue):
        """Create an option at the specified path.
    """
        if not self.__initialized['OK']:
            return self.__initialized
        self.__csMod.setOptionValue(optionPath, optionValue)
        self.csModified = True
        return S_OK('Created new option %s = %s' % (optionPath, optionValue))

    def setOptionComment(self, optionPath, comment):
        """Create an option at the specified path.
    """
        if not self.__initialized['OK']:
            return self.__initialized
        self.__csMod.setComment(optionPath, comment)
        self.csModified = True
        return S_OK('Set option comment %s : %s' % (optionPath, comment))

    def delOption(self, optionPath):
        """ Delete an option
    """
        if not self.__initialized['OK']:
            return self.__initialized
        if not self.__csMod.removeOption(optionPath):
            return S_ERROR("Couldn't delete option %s" % optionPath)
        self.csModified = True
        return S_OK('Deleted option %s' % optionPath)

    def createSection(self, sectionPath, comment=""):
        """ Create a new section
    """
        if not self.__initialized['OK']:
            return self.__initialized
        self.__csMod.createSection(sectionPath)
        self.csModified = True
        if comment:
            self.__csMod.setComment(sectionPath, comment)
        return S_OK()

    def delSection(self, sectionPath):
        """ Delete a section
    """
        if not self.__initialized['OK']:
            return self.__initialized
        if not self.__csMod.removeSection(sectionPath):
            return S_ERROR("Could not delete section %s " % sectionPath)
        self.csModified = True
        return S_OK()

    def copySection(self, originalPath, targetPath):
        """ Copy a whole section to a new location
    """
        if not self.__initialized['OK']:
            return self.__initialized
        cfg = self.__csMod.getCFG()
        sectionCfg = cfg[originalPath]
        result = self.createSection(targetPath)
        if not result['OK']:
            return result
        if not self.__csMod.mergeSectionFromCFG(targetPath, sectionCfg):
            return S_ERROR("Could not merge cfg into section %s" % targetPath)
        self.csModified = True
        return S_OK()

    def moveSection(self, originalPath, targetPath):
        """  Move a whole section to a new location
    """
        result = self.copySection(originalPath, targetPath)
        if not result['OK']:
            return result
        result = self.delSection(originalPath)
        if not result['OK']:
            return result
        self.csModified = True
        return S_OK()

    def mergeCFGUnderSection(self, sectionPath, cfg):
        """ Merge the given cfg under a certain section
    """
        if not self.__initialized['OK']:
            return self.__initialized
        result = self.createSection(sectionPath)
        if not result['OK']:
            return result
        if not self.__csMod.mergeSectionFromCFG(sectionPath, cfg):
            return S_ERROR("Could not merge cfg into section %s" % sectionPath)
        self.csModified = True
        return S_OK()

    def mergeWithCFG(self, cfg):
        """ Merge the given cfg with the current config
    """
        if not self.__initialized['OK']:
            return self.__initialized
        self.__csMod.mergeFromCFG(cfg)
        self.csModified = True
        return S_OK()

    def getCurrentCFG(self):
        """ Get the current CFG as it is
    """
        if not self.__initialized['OK']:
            return self.__initialized
        return S_OK(self.__csMod.getCFG())

    def showDiff(self):
        """ Just shows the differences accumulated within the Modificator object
    """
        diffData = self.__csMod.showCurrentDiff()
        gLogger.notice("Accumulated diff with master CS")
        for line in diffData:
            if line[0] in ('+', '-'):
                gLogger.notice(line)

    def forceGlobalConfigurationUpdate(self):
        """
    Force global update of configuration on all the registered services

    :return: S_OK/S_ERROR
    """

        return self.__rpcClient.forceGlobalConfigurationUpdate()
Exemple #24
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])
    if not os.path.exists( os.path.dirname(historyFile) ):
      os.makedirs( os.path.dirname(historyFile) )
    if os.path.isfile( historyFile ):
      readline.read_history_file( historyFile )
    readline.set_history_length(1000)
    atexit.register( readline.write_history_file, historyFile )


  def start( self ):
    if self.connected:
      self.modificator.loadFromRemote()
      retVal = self.modificator.loadCredentials()
      if not retVal[ 'OK' ]:
        print "There was an error gathering your credentials"
        print retVal[ 'Message' ]
        self._setStatus( False )
    try:
      self.cmdloop()
    except KeyboardInterrupt:
      gLogger.warn( "Received a keyboard interrupt." )
      self.do_quit( "" )

  def _setConnected( self, connected, writeEnabled ):
    self.connected = connected
    self.modifiedData = False
    self.writeEnabled = writeEnabled
    if connected:
      if writeEnabled:
        self.prompt = "(%s)-%s> " % ( self.masterURL, colorize( "Connected", "green" ) )
      else:
        self.prompt = "(%s)-%s> " % ( self.masterURL, colorize( "Connected (RO)", "yellow" ) )
    else:
      self.prompt = "(%s)-%s> " % ( self.masterURL, colorize( "Disconnected", "red" ) )

  def do_quit( self, dummy ):
    """
    Exits the application without sending changes to server

    Usage: quit
    """
    print
    if self.modifiedData:
      print "Changes are about to be written to file for later use."
      self.do_writeToFile( self.backupFilename )
      print "Changes written to %s.cfg" % self.backupFilename
    sys.exit( 0 )


#  def retrieveData( self ):
#    if not self.connected:
#      return False
#    response = self.rpcClient.dumpCompressed()
#    if response[ 'Status' ] == 'OK':
#      self.cDataHolder.loadFromCompressedSource( response[ 'Value' ] )
#      gLogger.info( "Data retrieved from server." )
#      return True
#    else:
#      gLogger.error( "Can't retrieve updated data from server." )
#      return False

  def _setStatus( self, connected = True ):
    if not connected:
      self.masterURL = "unset"
      self._setConnected( False, False )
    else:
      retVal = self.rpcClient.writeEnabled()
      if retVal[ 'OK' ]:
        if retVal[ 'Value' ] == True:
          self._setConnected( True, True )
        else:
          self._setConnected( True, False )
      else:
        print "Server returned an error: %s" % retVal[ 'Message' ]
        self._setConnected( True, False )

  def _tryConnection( self ):
    print "Trying connection to %s" % self.masterURL
    try:
      self.rpcClient = RPCClient( self.masterURL )
      self._setStatus()
    except Exception, x:
      gLogger.error( "Couldn't connect to master CS server", "%s (%s)" % ( self.masterURL, str( x ) ) )
      self._setStatus( False )
Exemple #25
0
                                   diffList=processedData["diff"])
            }))

    def __showDiff(self, params):
        if not self.__authorizeAction():
            raise WErr(500, "You are not authorized to get diff's!! Bad boy!")
        try:
            fromDate = str(params['fromVersion'])
            toDate = str(params['toVersion'])
        except Exception, e:
            raise WErr(500, "Can't decode params: %s" % e)

        rpcClient = RPCClient(
            gConfig.getValue("/DIRAC/Configuration/MasterServer",
                             "Configuration/Server"))
        modCfg = Modificator(rpcClient)
        diffGen = modCfg.getVersionDiff(fromDate, toDate)
        processedData = self.__generateHTMLDiff(diffGen)
        return self.write_message(
            json.dumps({
                "success":
                1,
                "op":
                "showDiff",
                "lines":
                processedData["lines"],
                "totalLines":
                processedData["totalLines"],
                "html":
                self.render_string("ConfigurationManager/diffConfig.tpl",
                                   titles=("From version %s" % fromDate,
Exemple #26
0
class CSShellCLI(CLI):
    def __init__(self):
        CLI.__init__(self)
        self.serverURL = ""
        self.serverName = ""
        self.modificator = None
        self.connected = False
        self.dirty = False
        self.root = "/"

        self.do_connect("")

    def update_prompt(self):
        if self.connected:
            self.prompt = "[" + colorize(self.serverName,
                                         "green") + ":" + self.root + " ]% "
        else:
            self.prompt = "[" + colorize("disconnected",
                                         "red") + ":" + self.root + " ]% "

    def do_connect(self, line):
        """connect
    Connect to the CS
    Usage: connect <URL> (Connect to the CS at the specified URL)
           connect       (Connect to the default CS URL of your config)
    """
        if line == "":
            self.serverURL = gConfigurationData.getMasterServer()
            self.serverName = gConfigurationData.getName()
        else:
            self.serverURL = self.serverName = line

        if self.serverURL == None:
            print(
                "Unable to connect to the default server. Maybe you don't have a proxy ?"
            )
            return self.do_disconnect("")

        print("Trying to connect to " + self.serverURL + "...", end=' ')

        self.modificator = Modificator(RPCClient(self.serverURL))
        rv = self.modificator.loadFromRemote()
        rv2 = self.modificator.loadCredentials()

        if rv['OK'] == False or rv2['OK'] == False:
            print("failed: ", end=' ')
            if rv['OK'] is False:
                print(rv['Message'])
            else:
                print(rv2['Message'])
            self.connected = False
            self.update_prompt()
        else:
            self.connected = True
            self.update_prompt()
            print("done.")

    def do_disconnect(self, _line):
        """Disconnect from CS"""
        if self.connected and self.dirty:
            res = raw_input(
                "Do you want to commit your changes into the CS ? [y/N] ")
            if res.lower() in ["y", "yes"]:
                self.do_commit("")

        self.serverURL = self.serverName = ""
        self.modificator = None
        self.connected = False
        self.update_prompt()

    def do_ls(self, line):
        """ls
    List the sections and options of CS of the current root"""
        if self.connected:
            secs = self.modificator.getSections(self.root)
            opts = self.modificator.getOptions(self.root)
            if line.startswith("-") and "l" in line:
                for i in secs:
                    print(colorize(i, "blue") + "  ")
                for i in opts:
                    print(i + " ")
            else:
                for i in secs:
                    print(colorize(i, "blue") + "  ", end=' ')
                for i in opts:
                    print(i + " ", end=' ')
                print("")

    def do_cd(self, line):
        """cd
    Go one directory deeper in the CS"""
        # Check if invariant holds
        if self.connected:
            assert (self.root == "/" or not self.root.endswith("/"))
            assert (self.root.startswith("/"))
            secs = self.modificator.getSections(self.root)
            if line == "..":
                self.root = os.path.dirname(self.root)
                self.update_prompt()
            else:
                if os.path.normpath(line) in secs:
                    if self.root == "/":
                        self.root = self.root + os.path.normpath(line)
                    else:
                        self.root = self.root + "/" + os.path.normpath(line)
                    self.update_prompt()
                else:
                    print("cd: no such section: " + line)

    def complete_cd(self, text, _line, _begidx, _endidx):
        secs = self.modificator.getSections(self.root)
        return [(s + "/") for s in secs if s.startswith(text)]

    def do_cat(self, line):
        """cat
    Read the content of an option in the CS"""
        if self.connected:
            opts = self.modificator.getOptionsDict(self.root)

            if line in opts.keys():
                print(opts[line])
            else:
                print("cat: No such option")

    def complete_cat(self, text, _line, _begidx, _endidx):
        opts = self.modificator.getOptions(self.root)
        return [o for o in opts if o.startswith(text)]

    do_less = do_cat
    complete_less = complete_cat

    def do_mkdir(self, line):
        """mkdir
    Create a new section in the CS"""
        if self.connected:
            self.modificator.createSection(self.root + "/" + line)
            self.dirty = True

    complete_mkdir = complete_cd

    def do_rmdir(self, line):
        """rmdir
    Delete a section in the CS"""
        if self.connected:
            self.modificator.removeSection(self.root + "/" + line)
            self.dirty = True

    complete_rmdir = complete_cd

    def do_rm(self, line):
        """rm
    Delete an option in the CS"""
        if self.connected:
            self.modificator.removeOption(self.root + "/" + line)
            self.dirty = True

    complete_rm = complete_cat

    def do_set(self, line):
        """set
    Set an option in the CS (or create it if it does not exists)
    Usage: set <str> to set a string option (will be stored as a string in CS)
           set <str>,<str>,... to set a list option (will be stored as a list in CS)
    """
        if self.connected:
            line = line.split(" ", 2)
            if len(line) != 2:
                print("Usage: set <key> <value>")
            else:
                self.modificator.setOptionValue(self.root + "/" + line[0],
                                                line[1])
                self.dirty = True

    complete_set = complete_cat

    def do_unset(self, line):
        """unset
    Unset an option in the CS: Making the option equal to the
    empty string."""
        if self.connected:
            self.modificator.setOptionValue(self.root + "/" + line, "")
            self.dirty = True

    complete_unset = complete_cat

    def do_commit(self, _line):
        """commit
    Commit the modifications to the CS"""
        if self.connected and self.dirty:
            self.modificator.commit()

    def default(self, line):
        """Override [Cmd.default(line)] function."""
        if line == "EOF":
            if self.prompt:
                print()
            return self.do_quit(line)
        else:
            cmd.Cmd.default(self, line)

    def do_quit(self, _line):
        """quit
    Quit"""
        self.do_disconnect("")
        CLI.do_quit(self, _line)
Exemple #27
0
class CSAPI:
    def __init__(self):
        """
    Initialization function
    """
        self.__csModified = False
        self.__baseSecurity = "/Registry"

        self.__userDN = ''
        self.__userGroup = ''
        self.__rpcClient = None
        self.__csMod = None

        self.__initialized = self.initialize()

    def __getProxyID(self):
        proxyLocation = Locations.getProxyLocation()
        if not proxyLocation:
            gLogger.error("No proxy found!")
            return False
        chain = X509Chain()
        if not chain.loadProxyFromFile(proxyLocation):
            gLogger.error("Can't read proxy!", proxyLocation)
            return False
        retVal = chain.getIssuerCert()
        if not retVal['OK']:
            gLogger.error("Can't parse proxy!", retVal['Message'])
            return False
        idCert = retVal['Value']
        self.__userDN = idCert.getSubjectDN()['Value']
        self.__userGroup = idCert.getDIRACGroup()['Value']
        return True

    def __getCertificateID(self):
        certLocation = Locations.getHostCertificateAndKeyLocation()
        if not certLocation:
            gLogger.error("No certificate found!")
            return False
        chain = X509Chain()
        retVal = chain.loadChainFromFile(certLocation[0])
        if not retVal['OK']:
            gLogger.error("Can't parse certificate!", retVal['Message'])
            return False
        idCert = chain.getIssuerCert()['Value']
        self.__userDN = idCert.getSubjectDN()['Value']
        self.__userGroup = 'host'
        return True

    def initialize(self):
        if not gConfig.useServerCertificate():
            res = self.__getProxyID()
        else:
            res = self.__getCertificateID()
        if not res:
            return False
        retVal = gConfig.getOption("/DIRAC/Configuration/MasterServer")
        if not retVal['OK']:
            gLogger.warn(
                "Master server is not known. Is everything initialized?")
            return False
        self.__rpcClient = RPCClient(
            gConfig.getValue("/DIRAC/Configuration/MasterServer", ""))
        self.__csMod = Modificator(
            self.__rpcClient, "%s - %s" % (self.__userGroup, self.__userDN))
        retVal = self.downloadCSData()
        if not retVal['OK']:
            gLogger.error(
                "Can not download the remote cfg. Is everything initialized?")
            return False
        return True

    def downloadCSData(self):
        result = self.__csMod.loadFromRemote()
        if not result['OK']:
            return result
        self.__csMod.updateGConfigurationData()
        return S_OK()

    def listUsers(self, group=False):
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        if not group:
            return S_OK(
                self.__csMod.getSections("%s/Users" % self.__baseSecurity))
        else:
            users = self.__csMod.getValue("%s/Groups/%s/Users" %
                                          (self.__baseSecurity, group))
            if not users:
                return S_OK([])
            else:
                return S_OK(List.fromChar(users))

    def listHosts(self):
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        return S_OK(self.__csMod.getSections("%s/Hosts" % self.__baseSecurity))

    def describeUsers(self, users=False):
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        return S_OK(self.__describeEntity(users))

    def describeHosts(self, hosts=False):
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        return S_OK(self.__describeEntity(hosts, True))

    def __describeEntity(self, mask, hosts=False):
        if hosts:
            csSection = "%s/Hosts" % self.__baseSecurity
        else:
            csSection = "%s/Users" % self.__baseSecurity
        if mask:
            entities = [
                entity for entity in self.__csMod.getSections(csSection)
                if entity in mask
            ]
        else:
            entities = self.__csMod.getSections(csSection)
        entitiesDict = {}
        for entity in entities:
            entitiesDict[entity] = {}
            for option in self.__csMod.getOptions("%s/%s" %
                                                  (csSection, entity)):
                entitiesDict[entity][option] = self.__csMod.getValue(
                    "%s/%s/%s" % (csSection, entity, option))
            if not hosts:
                groupsDict = self.describeGroups()['Value']
                entitiesDict[entity]['Groups'] = []
                for group in groupsDict:
                    if 'Users' in groupsDict[group] and entity in groupsDict[
                            group]['Users']:
                        entitiesDict[entity]['Groups'].append(group)
                entitiesDict[entity]['Groups'].sort()
        return entitiesDict

    def listGroups(self):
        """
    List all groups
    """
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        return S_OK(self.__csMod.getSections("%s/Groups" %
                                             self.__baseSecurity))

    def describeGroups(self, mask=False):
        """
    List all groups that are in the mask (or all if no mask) with their properties
    """
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        groups = [
            group for group in self.__csMod.getSections("%s/Groups" %
                                                        self.__baseSecurity)
            if not mask or (mask and group in mask)
        ]
        groupsDict = {}
        for group in groups:
            groupsDict[group] = {}
            for option in self.__csMod.getOptions(
                    "%s/Groups/%s" % (self.__baseSecurity, group)):
                groupsDict[group][option] = self.__csMod.getValue(
                    "%s/Groups/%s/%s" % (self.__baseSecurity, group, option))
                if option in ("Users", "Properties"):
                    groupsDict[group][option] = List.fromChar(
                        groupsDict[group][option])
        return S_OK(groupsDict)

    def deleteUsers(self, users):
        """
    Delete a user/s can receive as a param either a string or a list
    """
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        if type(users) == types.StringType:
            users = [users]
        usersData = self.describeUsers(users)['Value']
        for username in users:
            if not username in usersData:
                gLogger.warn("User %s does not exist")
                continue
            userGroups = usersData[username]['Groups']
            for group in userGroups:
                self.__removeUserFromGroup(group, username)
                gLogger.info("Deleted user %s from group %s" %
                             (username, group))
            self.__csMod.removeSection("%s/Users/%s" %
                                       (self.__baseSecurity, username))
            gLogger.info("Deleted user %s" % username)
            self.__csModified = True
        return S_OK(True)

    def __removeUserFromGroup(self, group, username):
        """
    Remove user from a group
    """
        usersInGroup = self.__csMod.getValue("%s/Groups/%s/Users" %
                                             (self.__baseSecurity, group))
        if usersInGroup != None:
            userList = List.fromChar(usersInGroup, ",")
            userPos = userList.index(username)
            userList.pop(userPos)
            self.__csMod.setOptionValue(
                "%s/Groups/%s/Users" % (self.__baseSecurity, group),
                ",".join(userList))

    def __addUserToGroup(self, group, username):
        """
    Add user to a group
    """
        usersInGroup = self.__csMod.getValue("%s/Groups/%s/Users" %
                                             (self.__baseSecurity, group))
        if usersInGroup != None:
            userList = List.fromChar(usersInGroup)
            if username not in userList:
                userList.append(username)
                self.__csMod.setOptionValue(
                    "%s/Groups/%s/Users" % (self.__baseSecurity, group),
                    ",".join(userList))
            else:
                gLogger.warn("User %s is already in group %s" %
                             (username, group))

    def addUser(self, username, properties):
        """
    Add a user to the cs
      - username
      - properties is a dict with keys:
        - DN
        - groups
        - <extra params>
    Returns True/False
    """
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        for prop in ("DN", "Groups"):
            if prop not in properties:
                gLogger.error("Missing %s property for user %s" %
                              (prop, username))
                return S_OK(False)
        if username in self.listUsers()['Value']:
            gLogger.error("User %s is already registered" % username)
            return S_OK(False)
        groups = self.listGroups()['Value']
        for userGroup in properties['Groups']:
            if not userGroup in groups:
                gLogger.error("User %s group %s is not a valid group" %
                              (username, userGroup))
                return S_OK(False)
        self.__csMod.createSection("%s/Users/%s" %
                                   (self.__baseSecurity, username))
        for prop in properties:
            if prop == "Groups":
                continue
            self.__csMod.setOptionValue(
                "%s/Users/%s/%s" % (self.__baseSecurity, username, prop),
                properties[prop])
        for userGroup in properties['Groups']:
            gLogger.info("Added user %s to group %s" % (username, userGroup))
            self.__addUserToGroup(userGroup, username)
        gLogger.info("Registered user %s" % username)
        self.__csModified = True
        return S_OK(True)

    def modifyUser(self, username, properties, createIfNonExistant=False):
        """
    Modify a user
      - username
      - properties is a dict with keys:
        - DN
        - Groups
        - <extra params>
    Returns True/False
    """
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        modifiedUser = False
        userData = self.describeUsers([username])['Value']
        if username not in userData:
            if createIfNonExistant:
                gLogger.info("Registering user %s" % username)
                return self.addUser(username, properties)
            gLogger.error("User %s is not registered" % username)
            return S_OK(False)
        for prop in properties:
            if prop == "Groups":
                continue
            prevVal = self.__csMod.getValue(
                "%s/Users/%s/%s" % (self.__baseSecurity, username, prop))
            if not prevVal or prevVal != properties[prop]:
                gLogger.info("Setting %s property for user %s to %s" %
                             (prop, username, properties[prop]))
                self.__csMod.setOptionValue(
                    "%s/Users/%s/%s" % (self.__baseSecurity, username, prop),
                    properties[prop])
                modifiedUser = True
        if 'Groups' in properties:
            groups = self.listGroups()['Value']
            for userGroup in properties['Groups']:
                if not userGroup in groups:
                    gLogger.error("User %s group %s is not a valid group" %
                                  (username, userGroup))
                    return S_OK(False)
            groupsToBeDeletedFrom = []
            groupsToBeAddedTo = []
            for prevGroup in userData[username]['Groups']:
                if prevGroup not in properties['Groups']:
                    groupsToBeDeletedFrom.append(prevGroup)
                    modifiedUser = True
            for newGroup in properties['Groups']:
                if newGroup not in userData[username]['Groups']:
                    groupsToBeAddedTo.append(newGroup)
                    modifiedUser = True
            for group in groupsToBeDeletedFrom:
                self.__removeUserFromGroup(group, username)
                gLogger.info("Removed user %s from group %s" %
                             (username, group))
            for group in groupsToBeAddedTo:
                self.__addUserToGroup(group, username)
                gLogger.info("Added user %s to group %s" % (username, group))
        if modifiedUser:
            gLogger.info("Modified user %s" % username)
            self.__csModified = True
        else:
            gLogger.info("Nothing to modify for user %s" % username)
        return S_OK(True)

    def addGroup(self, groupname, properties):
        """
    Add a group to the cs
      - groupname
      - properties is a dict with keys:
        - Users
        - Properties
        - <extra params>
    Returns True/False
    """
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        if groupname in self.listGroups()['Value']:
            gLogger.error("Group %s is already registered" % groupname)
            return S_OK(False)
        self.__csMod.createSection("%s/Groups/%s" %
                                   (self.__baseSecurity, groupname))
        for prop in properties:
            self.__csMod.setOptionValue(
                "%s/Groups/%s/%s" % (self.__baseSecurity, groupname, prop),
                properties[prop])
        gLogger.info("Registered group %s" % groupname)
        self.__csModified = True
        return S_OK(True)

    def modifyGroup(self, groupname, properties, createIfNonExistant=False):
        """
    Modify a user
      - groupname
      - properties is a dict with keys:
        - Users
        - Properties
        - <extra params>
    Returns True/False
    """
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        modifiedGroup = False
        groupData = self.describeGroups([groupname])['Value']
        if groupname not in groupData:
            if createIfNonExistant:
                gLogger.info("Registering group %s" % groupname)
                return self.addGroup(groupname, properties)
            gLogger.error("Group %s is not registered" % groupname)
            return S_OK(False)
        for prop in properties:
            prevVal = self.__csMod.getValue(
                "%s/Groups/%s/%s" % (self.__baseSecurity, groupname, prop))
            if not prevVal or prevVal != properties[prop]:
                gLogger.info("Setting %s property for group %s to %s" %
                             (prop, groupname, properties[prop]))
                self.__csMod.setOptionValue(
                    "%s/Groups/%s/%s" % (self.__baseSecurity, groupname, prop),
                    properties[prop])
                modifiedGroup = True
        if modifiedGroup:
            gLogger.info("Modified group %s" % groupname)
            self.__csModified = True
        else:
            gLogger.info("Nothing to modify for group %s" % groupname)
        return S_OK(True)

    def addHost(self, hostname, properties):
        """
    Add a host to the cs
      - hostname
      - properties is a dict with keys:
        - DN
        - Properties
        - <extra params>
    Returns True/False
    """
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        for prop in ("DN", ):
            if prop not in properties:
                gLogger.error("Missing %s property for host %s" %
                              (prop, hostname))
                return S_OK(False)
        if hostname in self.listHosts()['Value']:
            gLogger.error("Host %s is already registered" % hostname)
            return S_OK(False)
        self.__csMod.createSection("%s/Hosts/%s" %
                                   (self.__baseSecurity, hostname))
        for prop in properties:
            self.__csMod.setOptionValue(
                "%s/Hosts/%s/%s" % (self.__baseSecurity, hostname, prop),
                properties[prop])
        gLogger.info("Registered host %s" % hostname)
        self.__csModified = True
        return S_OK(True)

    def modifyHost(self, hostname, properties, createIfNonExistant=False):
        """
    Modify a user
      - hostname
      - properties is a dict with keys:
        - DN
        - Properties
        - <extra params>
    Returns True/False
    """
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        modifiedHost = False
        hostData = self.describeHosts([hostname])['Value']
        if hostname not in hostData:
            if createIfNonExistant:
                gLogger.info("Registering host %s" % hostname)
                return self.addHost(hostname, properties)
            gLogger.error("Host %s is not registered" % hostname)
            return S_OK(False)
        for prop in properties:
            prevVal = self.__csMod.getValue(
                "%s/Hosts/%s/%s" % (self.__baseSecurity, hostname, prop))
            if not prevVal or prevVal != properties[prop]:
                gLogger.info("Setting %s property for host %s to %s" %
                             (prop, hostname, properties[prop]))
                self.__csMod.setOptionValue(
                    "%s/Hosts/%s/%s" % (self.__baseSecurity, hostname, prop),
                    properties[prop])
                modifiedHost = True
        if modifiedHost:
            gLogger.info("Modified host %s" % hostname)
            self.__csModified = True
        else:
            gLogger.info("Nothing to modify for host %s" % hostname)
        return S_OK(True)

    def syncUsersWithCFG(self, usersCFG):
        """
    Sync users with the cfg contents. Usernames have to be sections containing
    DN, Groups, and extra properties as parameters
    """
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        done = True
        for user in usersCFG.listSections():
            properties = {}
            propList = usersCFG[user].listOptions()
            for prop in propList:
                if prop == "Groups":
                    properties[prop] = List.fromChar(usersCFG[user][prop])
                else:
                    properties[prop] = usersCFG[user][prop]
            if not self.modifyUser(user, properties, createIfNonExistant=True):
                done = False
        return S_OK(done)

    def sortUsersAndGroups(self):
        self.__csMod.sortAlphabetically("%s/Users" % self.__baseSecurity)
        self.__csMod.sortAlphabetically("%s/Hosts" % self.__baseSecurity)
        for group in self.__csMod.getSections("%s/Groups" %
                                              self.__baseSecurity):
            usersOptionPath = "%s/Groups/%s/Users" % (self.__baseSecurity,
                                                      group)
            users = self.__csMod.getValue(usersOptionPath)
            usersList = List.fromChar(users)
            usersList.sort()
            sortedUsers = ", ".join(usersList)
            if users != sortedUsers:
                self.__csMod.setOptionValue(usersOptionPath, sortedUsers)

    def checkForUnexistantUsersInGroups(self):
        allUsers = self.__csMod.getSections("%s/Users" % self.__baseSecurity)
        allGroups = self.__csMod.getSections("%s/Groups" % self.__baseSecurity)
        for group in allGroups:
            usersInGroup = self.__csMod.getValue("%s/Groups/%s/Users" %
                                                 (self.__baseSecurity, group))
            if usersInGroup:
                filteredUsers = []
                usersInGroup = List.fromChar(usersInGroup)
                for user in usersInGroup:
                    if user in allUsers:
                        filteredUsers.append(user)
                self.__csMod.setOptionValue(
                    "%s/Groups/%s/Users" % (self.__baseSecurity, group),
                    ",".join(filteredUsers))

    def commitChanges(self, sortUsers=True):
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        if self.__csModified:
            self.checkForUnexistantUsersInGroups()
            if sortUsers:
                self.sortUsersAndGroups()
            retVal = self.__csMod.commit()
            if not retVal['OK']:
                gLogger.error("Can't commit new data: %s" % retVal['Message'])
                return retVal
            self.__csModified = False
            return self.downloadCSData()
        return S_OK()

    def commit(self):
        """ Commit the accumulated changes to the CS server
    """
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        if self.__csModified:
            retVal = self.__csMod.commit()
            if not retVal['OK']:
                gLogger.error("Can't commit new data: %s" % retVal['Message'])
                return retVal
            self.__csModified = False
            return self.downloadCSData()
        return S_OK()

    def mergeFromCFG(self, cfg):
        """ Merge the internal CFG data with the input
    """
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        self.__csMod.mergeFromCFG(cfg)
        self.__csModified = True
        return S_OK()

    def modifyValue(self, optionPath, newValue):
        """Modify an existing value at the specified options path.
    """
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        prevVal = self.__csMod.getValue(optionPath)
        if not prevVal:
            return S_ERROR('Trying to set %s to %s but option does not exist' %
                           (optionPath, newValue))
        gLogger.verbose("Changing %s from \n%s \nto \n%s" %
                        (optionPath, prevVal, newValue))
        self.__csMod.setOptionValue(optionPath, newValue)
        self.__csModified = True
        return S_OK('Modified %s' % optionPath)

    def setOption(self, optionPath, optionValue):
        """Create an option at the specified path.
    """
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        self.__csMod.setOptionValue(optionPath, optionValue)
        self.__csModified = True
        return S_OK('Created new option %s = %s' % (optionPath, optionValue))

    def setOptionComment(self, optionPath, comment):
        """Create an option at the specified path.
    """
        if not self.__initialized:
            return S_ERROR("CSAPI didn't initialize properly")
        self.__csMod.setComment(optionPath, comment)
        self.__csModified = True
        return S_OK('Set option comment %s : %s' % (optionPath, comment))
Exemple #28
0
class CSAPI( object ):
  """ CSAPI objects need an initialization phase
  """

  def __init__( self ):
    """
    Initialization function
    """
    self.csModified = False
    self.__baseSecurity = "/Registry"

    self.__userDN = ''
    self.__userGroup = ''
    self.__rpcClient = None
    self.__csMod = None

    self.__initialized = S_ERROR( "Not initialized" )
    self.initialize()
    if not self.__initialized[ 'OK' ]:
      gLogger.error( self.__initialized )

  def __getProxyID( self ):
    proxyLocation = Locations.getProxyLocation()
    if not proxyLocation:
      gLogger.error( "No proxy found!" )
      return False
    chain = X509Chain()
    if not chain.loadProxyFromFile( proxyLocation ):
      gLogger.error( "Can't read proxy!", proxyLocation )
      return False
    retVal = chain.getIssuerCert()
    if not retVal[ 'OK' ]:
      gLogger.error( "Can't parse proxy!", retVal[ 'Message' ] )
      return False
    idCert = retVal[ 'Value' ]
    self.__userDN = idCert.getSubjectDN()[ 'Value' ]
    self.__userGroup = chain.getDIRACGroup()[ 'Value' ]
    return True

  def __getCertificateID( self ):
    certLocation = Locations.getHostCertificateAndKeyLocation()
    if not certLocation:
      gLogger.error( "No certificate found!" )
      return False
    chain = X509Chain()
    retVal = chain.loadChainFromFile( certLocation[ 0 ] )
    if not retVal[ 'OK' ]:
      gLogger.error( "Can't parse certificate!", retVal[ 'Message' ] )
      return False
    idCert = chain.getIssuerCert()[ 'Value' ]
    self.__userDN = idCert.getSubjectDN()[ 'Value' ]
    self.__userGroup = 'host'
    return True

  def initialize( self ):
    if self.__initialized[ 'OK' ]:
      return self.__initialized
    if not gConfig.useServerCertificate():
      res = self.__getProxyID()
    else:
      res = self.__getCertificateID()
    if not res:
      self.__initialized = S_ERROR( "Cannot locate client credentials" )
      return self.__initialized
    retVal = gConfig.getOption( "/DIRAC/Configuration/MasterServer" )
    if not retVal[ 'OK' ]:
      self.__initialized = S_ERROR( "Master server is not known. Is everything initialized?" )
      return self.__initialized
    self.__rpcClient = RPCClient( gConfig.getValue( "/DIRAC/Configuration/MasterServer", "" ) )
    self.__csMod = Modificator( self.__rpcClient, "%s - %s" % ( self.__userGroup, self.__userDN ) )
    retVal = self.downloadCSData()
    if not retVal[ 'OK' ]:
      self.__initialized = S_ERROR( "Can not download the remote cfg. Is everything initialized?" )
      return self.__initialized
    self.__initialized = S_OK()
    return self.__initialized

  def downloadCSData( self ):
    if not self.__csMod:
      return S_ERROR( "CSAPI not yet initialized" )
    result = self.__csMod.loadFromRemote()
    if not result[ 'OK' ]:
      return result
    self.csModified = False
    self.__csMod.updateGConfigurationData()
    return S_OK()

  def listUsers( self , group = False ):
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    if not group:
      return S_OK( self.__csMod.getSections( "%s/Users" % self.__baseSecurity ) )
    else:
      users = self.__csMod.getValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) )
      if not users:
        return S_OK( [] )
      else:
        return S_OK( List.fromChar( users ) )

  def listHosts( self ):
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    return S_OK( self.__csMod.getSections( "%s/Hosts" % self.__baseSecurity ) )

  def describeUsers( self, users = None ):
    if users is None: users = []
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    return S_OK( self.__describeEntity( users ) )

  def describeHosts( self, hosts = None ):
    if hosts is None:
      hosts = []
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    return S_OK( self.__describeEntity( hosts, True ) )

  def __describeEntity( self, mask, hosts = False ):
    if hosts:
      csSection = "%s/Hosts" % self.__baseSecurity
    else:
      csSection = "%s/Users" % self.__baseSecurity
    if mask:
      entities = [ entity for entity in self.__csMod.getSections( csSection ) if entity in mask ]
    else:
      entities = self.__csMod.getSections( csSection )
    entitiesDict = {}
    for entity in entities:
      entitiesDict[ entity ] = {}
      for option in self.__csMod.getOptions( "%s/%s" % ( csSection, entity ) ):
        entitiesDict[ entity ][ option ] = self.__csMod.getValue( "%s/%s/%s" % ( csSection, entity, option ) )
      if not hosts:
        groupsDict = self.describeGroups()[ 'Value' ]
        entitiesDict[ entity ][ 'Groups' ] = []
        for group in groupsDict:
          if 'Users' in groupsDict[ group ] and entity in groupsDict[ group ][ 'Users' ]:
            entitiesDict[ entity ][ 'Groups' ].append( group )
        entitiesDict[ entity ][ 'Groups' ].sort()
    return entitiesDict

  def listGroups( self ):
    """
    List all groups
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    return S_OK( self.__csMod.getSections( "%s/Groups" % self.__baseSecurity ) )

  def describeGroups( self, mask = None ):
    """
    List all groups that are in the mask (or all if no mask) with their properties
    """
    if mask is None: mask = []
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    groups = [ group for group in self.__csMod.getSections( "%s/Groups" % self.__baseSecurity ) if not mask or ( mask and group in mask ) ]
    groupsDict = {}
    for group in groups:
      groupsDict[ group ] = {}
      for option in self.__csMod.getOptions( "%s/Groups/%s" % ( self.__baseSecurity, group ) ):
        groupsDict[ group ][ option ] = self.__csMod.getValue( "%s/Groups/%s/%s" % ( self.__baseSecurity, group, option ) )
        if option in ( "Users", "Properties" ):
          groupsDict[ group ][ option ] = List.fromChar( groupsDict[ group ][ option ] )
    return S_OK( groupsDict )

  def deleteUsers( self, users ):
    """
    Delete a user/s can receive as a param either a string or a list
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    if isinstance( users, basestring ):
      users = [ users ]
    usersData = self.describeUsers( users )['Value']
    for username in users:
      if not username in usersData:
        gLogger.warn( "User %s does not exist" )
        continue
      userGroups = usersData[ username ][ 'Groups' ]
      for group in userGroups:
        self.__removeUserFromGroup( group, username )
        gLogger.info( "Deleted user %s from group %s" % ( username, group ) )
      self.__csMod.removeSection( "%s/Users/%s" % ( self.__baseSecurity, username ) )
      gLogger.info( "Deleted user %s" % username )
      self.csModified = True
    return S_OK( True )

  def __removeUserFromGroup( self, group, username ):
    """
    Remove user from a group
    """
    usersInGroup = self.__csMod.getValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) )
    if usersInGroup is not None:
      userList = List.fromChar( usersInGroup, "," )
      userPos = userList.index( username )
      userList.pop( userPos )
      self.__csMod.setOptionValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ), ",".join( userList ) )

  def __addUserToGroup( self, group, username ):
    """
    Add user to a group
    """
    usersInGroup = self.__csMod.getValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) )
    if usersInGroup is not None:
      userList = List.fromChar( usersInGroup )
      if username not in userList:
        userList.append( username )
        self.__csMod.setOptionValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ), ",".join( userList ) )
      else:
        gLogger.warn( "User %s is already in group %s" % ( username, group ) )

  def addUser( self, username, properties ):
    """
    Add a user to the cs

      :param str username: group name
      :param dict properties: dictionary describing user properties:

        - DN
        - groups
        - <extra params>

      :return: True/False
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    for prop in ( "DN", "Groups" ):
      if prop not in properties:
        gLogger.error( "Missing property for user", "%s: %s" % ( prop, username ) )
        return S_OK( False )
    if username in self.listUsers()['Value']:
      gLogger.error( "User is already registered", username )
      return S_OK( False )
    groups = self.listGroups()['Value']
    for userGroup in properties[ 'Groups' ]:
      if not userGroup in groups:
        gLogger.error( "User group is not a valid group", "%s %s" % ( username, userGroup ) )
        return S_OK( False )
    self.__csMod.createSection( "%s/Users/%s" % ( self.__baseSecurity, username ) )
    for prop in properties:
      if prop == "Groups":
        continue
      self.__csMod.setOptionValue( "%s/Users/%s/%s" % ( self.__baseSecurity, username, prop ), properties[ prop ] )
    for userGroup in properties[ 'Groups' ]:
      gLogger.info( "Added user %s to group %s" % ( username, userGroup ) )
      self.__addUserToGroup( userGroup, username )
    gLogger.info( "Registered user %s" % username )
    self.csModified = True
    return S_OK( True )

  def modifyUser( self, username, properties, createIfNonExistant = False ):
    """
    Modify a user

      :param str username: group name
      :param dict properties: dictionary describing user properties:

        - DN
        - Groups
        - <extra params>

      :return: S_OK, Value = True/False
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    modifiedUser = False
    userData = self.describeUsers( [ username ] )['Value']
    if username not in userData:
      if createIfNonExistant:
        gLogger.info( "Registering user %s" % username )
        return self.addUser( username, properties )
      gLogger.error( "User is not registered", username )
      return S_OK( False )
    for prop in properties:
      if prop == "Groups":
        continue
      prevVal = self.__csMod.getValue( "%s/Users/%s/%s" % ( self.__baseSecurity, username, prop ) )
      if not prevVal or prevVal != properties[ prop ]:
        gLogger.info( "Setting %s property for user %s to %s" % ( prop, username, properties[ prop ] ) )
        self.__csMod.setOptionValue( "%s/Users/%s/%s" % ( self.__baseSecurity, username, prop ), properties[ prop ] )
        modifiedUser = True
    if 'Groups' in properties:
      groups = self.listGroups()['Value']
      for userGroup in properties[ 'Groups' ]:
        if not userGroup in groups:
          gLogger.error( "User group is not a valid group", "%s %s" % ( username, userGroup ) )
          return S_OK( False )
      groupsToBeDeletedFrom = []
      groupsToBeAddedTo = []
      for prevGroup in userData[ username ][ 'Groups' ]:
        if prevGroup not in properties[ 'Groups' ]:
          groupsToBeDeletedFrom.append( prevGroup )
          modifiedUser = True
      for newGroup in properties[ 'Groups' ]:
        if newGroup not in userData[ username ][ 'Groups' ]:
          groupsToBeAddedTo.append( newGroup )
          modifiedUser = True
      for group in groupsToBeDeletedFrom:
        self.__removeUserFromGroup( group, username )
        gLogger.info( "Removed user %s from group %s" % ( username, group ) )
      for group in groupsToBeAddedTo:
        self.__addUserToGroup( group, username )
        gLogger.info( "Added user %s to group %s" % ( username, group ) )
    modified = False
    if modifiedUser:
      modified = True
      gLogger.info( "Modified user %s" % username )
      self.csModified = True
    else:
      gLogger.info( "Nothing to modify for user %s" % username )
    return S_OK( modified )

  def addGroup( self, groupname, properties ):
    """
    Add a group to the cs

      :param str groupname: group name
      :param dict properties: dictionary describing group properties:

        - Users
        - Properties
        - <extra params>

      :return: True/False
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    if groupname in self.listGroups()['Value']:
      gLogger.error( "Group is already registered", groupname )
      return S_OK( False )
    self.__csMod.createSection( "%s/Groups/%s" % ( self.__baseSecurity, groupname ) )
    for prop in properties:
      self.__csMod.setOptionValue( "%s/Groups/%s/%s" % ( self.__baseSecurity, groupname, prop ), properties[ prop ] )
    gLogger.info( "Registered group %s" % groupname )
    self.csModified = True
    return S_OK( True )

  def modifyGroup( self, groupname, properties, createIfNonExistant = False ):
    """
    Modify a group

      :param str groupname: group name
      :param dict properties: dictionary describing group properties:

        - Users
        - Properties
        - <extra params>

      :return: True/False
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    modifiedGroup = False
    groupData = self.describeGroups( [ groupname ] )['Value']
    if groupname not in groupData:
      if createIfNonExistant:
        gLogger.info( "Registering group %s" % groupname )
        return self.addGroup( groupname, properties )
      gLogger.error( "Group is not registered", groupname )
      return S_OK( False )
    for prop in properties:
      prevVal = self.__csMod.getValue( "%s/Groups/%s/%s" % ( self.__baseSecurity, groupname, prop ) )
      if not prevVal or prevVal != properties[ prop ]:
        gLogger.info( "Setting %s property for group %s to %s" % ( prop, groupname, properties[ prop ] ) )
        self.__csMod.setOptionValue( "%s/Groups/%s/%s" % ( self.__baseSecurity, groupname, prop ), properties[ prop ] )
        modifiedGroup = True
    if modifiedGroup:
      gLogger.info( "Modified group %s" % groupname )
      self.csModified = True
    else:
      gLogger.info( "Nothing to modify for group %s" % groupname )
    return S_OK( True )

  def addHost( self, hostname, properties ):
    """
    Add a host to the cs
      :param str hostname: hostname name
      :param dict properties: dictionary describing host properties:

        - DN
        - Properties
        - <extra params>

      :return: True/False
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    for prop in ( "DN", ):
      if prop not in properties:
        gLogger.error( "Missing property for host", "%s %s" % ( prop, hostname ) )
        return S_OK( False )
    if hostname in self.listHosts()['Value']:
      gLogger.error( "Host is already registered", hostname )
      return S_OK( False )
    self.__csMod.createSection( "%s/Hosts/%s" % ( self.__baseSecurity, hostname ) )
    for prop in properties:
      self.__csMod.setOptionValue( "%s/Hosts/%s/%s" % ( self.__baseSecurity, hostname, prop ), properties[ prop ] )
    gLogger.info( "Registered host %s" % hostname )
    self.csModified = True
    return S_OK( True )

  def addShifter( self, shifters = None ):
    """
    Adds or modify one or more shifters. Also, adds the shifter section in case this is not present.
    Shifter identities are used in several places, mostly for running agents

    shifters should be in the form {'ShifterRole':{'User':'******', 'Group':'aDIRACGroup'}}

    :return: S_OK/S_ERROR
    """

    def getOpsSection():
      """
      Where is the shifters section?
      """
      vo = CSGlobals.getVO()
      setup = CSGlobals.getSetup()

      if vo:
        res = gConfig.getSections( '/Operations/%s/%s/Shifter' % (vo, setup) )
        if res['OK']:
          return S_OK( '/Operations/%s/%s/Shifter' % ( vo, setup ) )

        res = gConfig.getSections( '/Operations/%s/Defaults/Shifter' % vo )
        if res['OK']:
          return S_OK( '/Operations/%s/Defaults/Shifter' % vo )

      else:
        res = gConfig.getSections( '/Operations/%s/Shifter' % setup )
        if res['OK']:
          return S_OK( '/Operations/%s/Shifter' % setup )

        res = gConfig.getSections( '/Operations/Defaults/Shifter' )
        if res['OK']:
          return S_OK( '/Operations/Defaults/Shifter' )

      return S_ERROR( "No shifter section" )

    if shifters is None: shifters = {}
    if not self.__initialized['OK']:
      return self.__initialized

    # get current shifters
    opsH = Operations( )
    currentShifterRoles = opsH.getSections( 'Shifter' )
    if not currentShifterRoles['OK']:
      # we assume the shifter section is not present
      currentShifterRoles = []
    else:
      currentShifterRoles = currentShifterRoles['Value']
    currentShiftersDict = {}
    for currentShifterRole in currentShifterRoles:
      currentShifter = opsH.getOptionsDict( 'Shifter/%s' % currentShifterRole )
      if not currentShifter['OK']:
        return currentShifter
      currentShifter = currentShifter['Value']
      currentShiftersDict[currentShifterRole] = currentShifter

    # Removing from shifters what does not need to be changed
    for sRole in shifters:
      if sRole in currentShiftersDict:
        if currentShiftersDict[sRole] == shifters[sRole]:
          shifters.pop( sRole )

    # get shifters section to modify
    section = getOpsSection()

    # Is this section present?
    if not section['OK']:
      if section['Message'] == "No shifter section":
        gLogger.warn( section['Message'] )
        gLogger.info( "Adding shifter section" )
        vo = CSGlobals.getVO()
        if vo:
          section = '/Operations/%s/Defaults/Shifter' % vo
        else:
          section = '/Operations/Defaults/Shifter'
        res = self.__csMod.createSection( section )
        if not res:
          gLogger.error( "Section %s not created" % section )
          return S_ERROR( "Section %s not created" % section )
      else:
        gLogger.error( section['Message'] )
        return section
    else:
      section = section['Value']


    #add or modify shifters
    for shifter in shifters:
      self.__csMod.removeSection( section + '/' + shifter )
      self.__csMod.createSection( section + '/' + shifter )
      self.__csMod.createSection( section + '/' + shifter + '/' + 'User' )
      self.__csMod.createSection( section + '/' + shifter + '/' + 'Group' )
      self.__csMod.setOptionValue( section + '/' + shifter + '/' + 'User', shifters[shifter]['User'] )
      self.__csMod.setOptionValue( section + '/' + shifter + '/' + 'Group', shifters[shifter]['Group'] )

    self.csModified = True
    return S_OK( True )


  def modifyHost( self, hostname, properties, createIfNonExistant = False ):
    """
    Modify a host
      :param str hostname: hostname name
      :param dict properties: dictionary describing host properties:

        - DN
        - Properties
        - <extra params>

      :return: True/False
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    modifiedHost = False
    hostData = self.describeHosts( [ hostname ] )['Value']
    if hostname not in hostData:
      if createIfNonExistant:
        gLogger.info( "Registering host %s" % hostname )
        return self.addHost( hostname, properties )
      gLogger.error( "Host is not registered", hostname )
      return S_OK( False )
    for prop in properties:
      prevVal = self.__csMod.getValue( "%s/Hosts/%s/%s" % ( self.__baseSecurity, hostname, prop ) )
      if not prevVal or prevVal != properties[ prop ]:
        gLogger.info( "Setting %s property for host %s to %s" % ( prop, hostname, properties[ prop ] ) )
        self.__csMod.setOptionValue( "%s/Hosts/%s/%s" % ( self.__baseSecurity, hostname, prop ), properties[ prop ] )
        modifiedHost = True
    if modifiedHost:
      gLogger.info( "Modified host %s" % hostname )
      self.csModified = True
    else:
      gLogger.info( "Nothing to modify for host %s" % hostname )
    return S_OK( True )

  def syncUsersWithCFG( self, usersCFG ):
    """
    Sync users with the cfg contents. Usernames have to be sections containing
    DN, Groups, and extra properties as parameters
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    done = True
    for user in usersCFG.listSections():
      properties = {}
      propList = usersCFG[ user ].listOptions()
      for prop in propList:
        if prop == "Groups":
          properties[ prop ] = List.fromChar( usersCFG[ user ][ prop ] )
        else:
          properties[ prop ] = usersCFG[ user ][ prop ]
      if not self.modifyUser( user, properties, createIfNonExistant = True ):
        done = False
    return S_OK( done )

  def sortUsersAndGroups( self ):
    self.__csMod.sortAlphabetically( "%s/Users" % self.__baseSecurity )
    self.__csMod.sortAlphabetically( "%s/Hosts" % self.__baseSecurity )
    for group in self.__csMod.getSections( "%s/Groups" % self.__baseSecurity ):
      usersOptionPath = "%s/Groups/%s/Users" % ( self.__baseSecurity, group )
      users = self.__csMod.getValue( usersOptionPath )
      if users:
        usersList = List.fromChar( users )
        usersList.sort()
        sortedUsers = ", ".join( usersList )
        if users != sortedUsers:
          self.__csMod.setOptionValue( usersOptionPath, sortedUsers )

  def checkForUnexistantUsersInGroups( self ):
    allUsers = self.__csMod.getSections( "%s/Users" % self.__baseSecurity )
    allGroups = self.__csMod.getSections( "%s/Groups" % self.__baseSecurity )
    for group in allGroups:
      usersInGroup = self.__csMod.getValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ) )
      if usersInGroup:
        filteredUsers = []
        usersInGroup = List.fromChar( usersInGroup )
        for user in usersInGroup:
          if user in allUsers:
            filteredUsers.append( user )
        self.__csMod.setOptionValue( "%s/Groups/%s/Users" % ( self.__baseSecurity, group ),
                                     ",".join( filteredUsers ) )

  def commitChanges( self, sortUsers = True ):
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    if self.csModified:
      self.checkForUnexistantUsersInGroups()
      if sortUsers:
        self.sortUsersAndGroups()
      retVal = self.__csMod.commit()
      if not retVal[ 'OK' ]:
        gLogger.error( "Can't commit new configuration data", "%s" % retVal[ 'Message' ] )
        return retVal
      return self.downloadCSData()
    return S_OK()

  def commit( self ):
    """ Commit the accumulated changes to the CS server
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    if self.csModified:
      retVal = self.__csMod.commit()
      if not retVal[ 'OK' ]:
        gLogger.error( "Can't commit new configuration data", "%s" % retVal[ 'Message' ] )
        return retVal
      return self.downloadCSData()
    return S_OK()

  def mergeFromCFG( self, cfg ):
    """ Merge the internal CFG data with the input
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    self.__csMod.mergeFromCFG( cfg )
    self.csModified = True
    return S_OK()

  def modifyValue( self, optionPath, newValue ):
    """Modify an existing value at the specified options path.
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    prevVal = self.__csMod.getValue( optionPath )
    if prevVal is None:
      return S_ERROR( 'Trying to set %s to %s but option does not exist' % ( optionPath, newValue ) )
    gLogger.verbose( "Changing %s from \n%s \nto \n%s" % ( optionPath, prevVal, newValue ) )
    self.__csMod.setOptionValue( optionPath, newValue )
    self.csModified = True
    return S_OK( 'Modified %s' % optionPath )

  def setOption( self, optionPath, optionValue ):
    """Create an option at the specified path.
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    self.__csMod.setOptionValue( optionPath, optionValue )
    self.csModified = True
    return S_OK( 'Created new option %s = %s' % ( optionPath, optionValue ) )


  def setOptionComment( self, optionPath, comment ):
    """Create an option at the specified path.
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    self.__csMod.setComment( optionPath, comment )
    self.csModified = True
    return S_OK( 'Set option comment %s : %s' % ( optionPath, comment ) )

  def delOption( self, optionPath ):
    """ Delete an option
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    if not self.__csMod.removeOption( optionPath ):
      return S_ERROR( "Couldn't delete option %s" % optionPath )
    self.csModified = True
    return S_OK( 'Deleted option %s' % optionPath )

  def createSection( self, sectionPath, comment = "" ):
    """ Create a new section
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    self.__csMod.createSection( sectionPath )
    self.csModified = True
    if comment:
      self.__csMod.setComment( sectionPath, comment )
    return S_OK()

  def delSection( self, sectionPath ):
    """ Delete a section
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    if not self.__csMod.removeSection( sectionPath ):
      return S_ERROR( "Could not delete section %s " % sectionPath )
    self.csModified = True
    return S_OK( )

  def copySection( self, originalPath, targetPath ):
    """ Copy a whole section to a new location
    """
    if not self.__initialized['OK']:
      return self.__initialized
    cfg = self.__csMod.getCFG( )
    sectionCfg = cfg[originalPath]
    result = self.createSection( targetPath )
    if not result['OK']:
      return result
    if not self.__csMod.mergeSectionFromCFG( targetPath, sectionCfg ):
      return S_ERROR( "Could not merge cfg into section %s" % targetPath )
    self.csModified = True
    return S_OK( )

  def moveSection( self, originalPath, targetPath ):
    """  Move a whole section to a new location
    """
    result = self.copySection( originalPath, targetPath )
    if not result['OK']:
      return result
    result = self.delSection( originalPath )
    if not result['OK']:
      return result
    self.csModified = True
    return S_OK()

  def mergeCFGUnderSection( self, sectionPath, cfg ):
    """ Merge the given cfg under a certain section
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    result = self.createSection( sectionPath )
    if not result[ 'OK' ]:
      return result
    if not self.__csMod.mergeSectionFromCFG( sectionPath, cfg ):
      return S_ERROR( "Could not merge cfg into section %s" % sectionPath )
    self.csModified = True
    return S_OK()

  def mergeWithCFG( self, cfg ):
    """ Merge the given cfg with the current config
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    self.__csMod.mergeFromCFG( cfg )
    self.csModified = True
    return S_OK()

  def getCurrentCFG( self ):
    """ Get the current CFG as it is
    """
    if not self.__initialized[ 'OK' ]:
      return self.__initialized
    return S_OK( self.__csMod.getCFG() )

  def showDiff( self ):
    """ Just shows the differences accumulated within the Modificator object
    """
    diffData = self.__csMod.showCurrentDiff()
    gLogger.notice( "Accumulated diff with master CS" )
    for line in diffData:
      if line[0] in ( '+', '-' ):
        gLogger.notice( line )
Exemple #29
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()
Exemple #30
0
class CSShellCLI(CLI):
    def __init__(self):
        CLI.__init__(self)
        self.serverURL = ""
        self.serverName = ""
        self.modificator = None
        self.connected = False
        self.dirty = False
        self.root = "/"

        self.do_connect("")

    def update_prompt(self):
        if self.connected:
            self.prompt = "[" + colorize(self.serverName, "green") + ":" + self.root + " ]% "
        else:
            self.prompt = "[" + colorize("disconnected", "red") + ":" + self.root + " ]% "

    def do_connect(self, line):
        """connect
    Connect to the CS
    Usage: connect <URL> (Connect to the CS at the specified URL)
           connect       (Connect to the default CS URL of your config)
    """
        if line == "":
            self.serverURL = gConfigurationData.getMasterServer()
            self.serverName = gConfigurationData.getName()
        else:
            self.serverURL = self.serverName = line

        if self.serverURL == None:
            print "Unable to connect to the default server. Maybe you don't have a proxy ?"
            return self.do_disconnect("")

        print "Trying to connect to " + self.serverURL + "...",

        self.modificator = Modificator(RPCClient(self.serverURL))
        rv = self.modificator.loadFromRemote()
        rv2 = self.modificator.loadCredentials()

        if rv["OK"] == False or rv2["OK"] == False:
            print "failed: ",
            if rv["OK"] == False:
                print rv["Message"]
            else:
                print rv2["Message"]
            self.connected = False
            self.update_prompt()
        else:
            self.connected = True
            self.update_prompt()
            print "done."

    def do_disconnect(self, _line):
        """Disconnect from CS"""
        if self.connected and self.dirty:
            res = raw_input("Do you want to commit your changes into the CS ? [y/N] ")
            if res.lower() in ["y", "yes"]:
                self.do_commit("")

        self.serverURL = self.serverName = ""
        self.modificator = None
        self.connected = False
        self.update_prompt()

    def do_ls(self, line):
        """ls
    List the sections and options of CS of the current root"""
        if self.connected:
            secs = self.modificator.getSections(self.root)
            opts = self.modificator.getOptions(self.root)
            if line.startswith("-") and "l" in line:
                for i in secs:
                    print colorize(i, "blue") + "  "
                for i in opts:
                    print i + " "
            else:
                for i in secs:
                    print colorize(i, "blue") + "  ",
                for i in opts:
                    print i + " ",
                print ""

    def do_cd(self, line):
        """cd
    Go one directory deeper in the CS"""
        # Check if invariant holds
        if self.connected:
            assert self.root == "/" or not self.root.endswith("/")
            assert self.root.startswith("/")
            secs = self.modificator.getSections(self.root)
            if line == "..":
                self.root = os.path.dirname(self.root)
                self.update_prompt()
            else:
                if os.path.normpath(line) in secs:
                    if self.root == "/":
                        self.root = self.root + os.path.normpath(line)
                    else:
                        self.root = self.root + "/" + os.path.normpath(line)
                    self.update_prompt()
                else:
                    print "cd: no such section: " + line

    def complete_cd(self, text, _line, _begidx, _endidx):
        secs = self.modificator.getSections(self.root)
        return [(s + "/") for s in secs if s.startswith(text)]

    def do_cat(self, line):
        """cat
    Read the content of an option in the CS"""
        if self.connected:
            opts = self.modificator.getOptionsDict(self.root)

            if line in opts.keys():
                print opts[line]
            else:
                print "cat: No such option"

    def complete_cat(self, text, _line, _begidx, _endidx):
        opts = self.modificator.getOptions(self.root)
        return [o for o in opts if o.startswith(text)]

    do_less = do_cat
    complete_less = complete_cat

    def do_mkdir(self, line):
        """mkdir
    Create a new section in the CS"""
        if self.connected:
            self.modificator.createSection(self.root + "/" + line)
            self.dirty = True

    complete_mkdir = complete_cd

    def do_rmdir(self, line):
        """rmdir
    Delete a section in the CS"""
        if self.connected:
            self.modificator.removeSection(self.root + "/" + line)
            self.dirty = True

    complete_rmdir = complete_cd

    def do_rm(self, line):
        """rm
    Delete an option in the CS"""
        if self.connected:
            self.modificator.removeOption(self.root + "/" + line)
            self.dirty = True

    complete_rm = complete_cat

    def do_set(self, line):
        """set
    Set an option in the CS (or create it if it does not exists)
    Usage: set <str> to set a string option (will be stored as a string in CS)
           set <str>,<str>,... to set a list option (will be stored as a list in CS)
    """
        if self.connected:
            line = line.split(" ", 2)
            if len(line) != 2:
                print "Usage: set <key> <value>"
            else:
                self.modificator.setOptionValue(self.root + "/" + line[0], line[1])
                self.dirty = True

    complete_set = complete_cat

    def do_unset(self, line):
        """unset
    Unset an option in the CS: Making the option equal to the
    empty string."""
        if self.connected:
            self.modificator.setOptionValue(self.root + "/" + line, "")
            self.dirty = True

    complete_unset = complete_cat

    def do_commit(self, _line):
        """commit
    Commit the modifications to the CS"""
        if self.connected and self.dirty:
            self.modificator.commit()

    def default(self, line):
        """Override [Cmd.default(line)] function."""
        if line == "EOF":
            if self.prompt:
                print
            return self.do_quit(line)
        else:
            cmd.Cmd.default(self, line)

    def do_quit(self, _line):
        """quit
    Quit"""
        self.do_disconnect("")
        CLI.do_quit(self, _line)
Exemple #31
0
class CSCLI(cmd.Cmd):
    def __init__(self):
        cmd.Cmd.__init__(self)
        self.connected = False
        self.masterURL = "unset"
        self.writeEnabled = False
        self.modifiedData = False
        self.rpcClient = None
        self.do_connect()
        if self.connected:
            self.modificator = Modificator(self.rpcClient)
        else:
            self.modificator = Modificator()
        self.identSpace = 20
        self.backupFilename = "dataChanges"
        self._initSignals()
        #User friendly hack
        self.do_exit = self.do_quit

    def start(self):
        if self.connected:
            self.modificator.loadFromRemote()
            retVal = self.modificator.loadCredentials()
            if not retVal['OK']:
                print "There was an error gathering your credentials"
                print retVal['Message']
                self._setStatus(False)
        try:
            self.cmdloop()
        except KeyboardInterrupt:
            gLogger.warn("Received a keyboard interrupt.")
            self.do_quit("")

    def _initSignals(self):
        """
    Registers signal handlers
    """
        for sigNum in (signal.SIGINT, signal.SIGQUIT, signal.SIGKILL,
                       signal.SIGTERM):
            try:
                signal.signal(sigNum, self.do_quit)
            except:
                pass

    def _setConnected(self, connected, writeEnabled):
        self.connected = connected
        self.modifiedData = False
        self.writeEnabled = writeEnabled
        if connected:
            if writeEnabled:
                self.prompt = "(%s)-%s> " % (self.masterURL,
                                             colorize("Connected", "green"))
            else:
                self.prompt = "(%s)-%s> " % (
                    self.masterURL, colorize("Connected (RO)", "yellow"))
        else:
            self.prompt = "(%s)-%s> " % (self.masterURL,
                                         colorize("Disconnected", "red"))

    def _printPair(self, key, value, separator=":"):
        valueList = value.split("\n")
        print "%s%s%s %s" % (key, " " * (self.identSpace - len(key)),
                             separator, valueList[0].strip())
        for valueLine in valueList[1:-1]:
            print "%s  %s" % (" " * self.identSpace, valueLine.strip())

    def do_quit(self, dummy):
        """
    Exits the application without sending changes to server

    Usage: quit
    """
        if self.modifiedData:
            print "Changes are about to be written to file for later use."
            self.do_writeToFile(self.backupFilename)
            print "Changes written to %s.cfg" % self.backupFilename
        sys.exit(0)

    def do_EOF(self, args):
        """
    Accepts ctrl^D to quit CLI
    """
        print ""
        self.do_quit(args)

    def do_help(self, args):
        """
    Shows help information

    Usage: help <command>

    If no command is specified all commands are shown

    """
        if len(args) == 0:
            print "\nAvailable commands:\n"
            attrList = dir(self)
            attrList.sort()
            for attribute in attrList:
                if attribute.find("do_") == 0:
                    self._printPair(attribute[3:],
                                    getattr(self, attribute).__doc__[1:])
                    print ""
        else:
            command = args.split()[0].strip()
            try:
                obj = getattr(self, "do_%s" % command)
            except:
                print "There's no such %s command" % command
                return
            self._printPair(command, obj.__doc__[1:])


#  def retrieveData( self ):
#    if not self.connected:
#      return False
#    response = self.rpcClient.dumpCompressed()
#    if response[ 'Status' ] == 'OK':
#      self.cDataHolder.loadFromCompressedSource( response[ 'Value' ] )
#      gLogger.info( "Data retrieved from server." )
#      return True
#    else:
#      gLogger.error( "Can't retrieve updated data from server." )
#      return False

    def _setStatus(self, connected=True):
        if not connected:
            self.masterURL = "unset"
            self._setConnected(False, False)
        else:
            retVal = self.rpcClient.writeEnabled()
            if retVal['OK']:
                if retVal['Value'] == True:
                    self._setConnected(True, True)
                else:
                    self._setConnected(True, False)
            else:
                print "Server returned an error: %s" % retVal['Message']
                self._setConnected(True, False)

    def _tryConnection(self):
        print "Trying connection to %s" % self.masterURL
        try:
            self.rpcClient = RPCClient(self.masterURL)
            self._setStatus()
        except Exception, x:
            gLogger.error("Couldn't connect to %s (%s)" %
                          (self.masterURL, str(x)))
            self._setStatus(False)
    processedData = self.__generateHTMLDiff( diffGen )
    return self.write_message( json.dumps( {"success":1, "op":"showCurrentDiff", "lines":processedData["lines"], "totalLines": processedData["totalLines"], "html":self.render_string( "ConfigurationManager/diffConfig.tpl",
                                                                                                         titles = ( "Server's version", "User's current version" ),
                                                                                                         diffList = processedData["diff"] )} ) )

  def __showDiff( self, params ):
    if not self.__authorizeAction():
      raise WErr( 500, "You are not authorized to get diff's!! Bad boy!" )
    try:
      fromDate = str( params[ 'fromVersion' ] )
      toDate = str( params[ 'toVersion' ] )
    except Exception, e:
      raise WErr( 500, "Can't decode params: %s" % e )

    rpcClient = RPCClient( gConfig.getValue( "/DIRAC/Configuration/MasterServer", "Configuration/Server" ) )
    modCfg = Modificator( rpcClient )
    diffGen = modCfg.getVersionDiff( fromDate, toDate )
    processedData = self.__generateHTMLDiff( diffGen )
    return self.write_message( json.dumps( {"success":1, "op":"showDiff", "lines":processedData["lines"], "totalLines": processedData["totalLines"], "html":self.render_string( "ConfigurationManager/diffConfig.tpl",
                                                                                                         titles = ( "From version %s" % fromDate, "To version %s" % toDate ),
                                                                                                         diffList = processedData["diff"] )} ) )

  def __rollback( self, params ):
    rollbackVersion = ""
    if not self.__authorizeAction():
      raise WErr( 500, "You are not authorized to get diff's!! Bad boy!" )
    try:
      rollbackVersion = str( params[ 'rollbackToVersion' ] )
    except Exception, e:
      raise WErr( 500, "Can't decode params: %s" % e )
    rpcClient = RPCClient( gConfig.getValue( "/DIRAC/Configuration/MasterServer", "Configuration/Server" ) )
Exemple #33
0
class CSCLI( cmd.Cmd ):

  def __init__( self ):
    cmd.Cmd.__init__( self )
    self.connected = False
    self.masterURL = "unset"
    self.writeEnabled = False
    self.modifiedData = False
    self.rpcClient = None
    self.do_connect()
    if self.connected:
      self.modificator = Modificator ( self.rpcClient )
    else:
      self.modificator = Modificator()
    self.identSpace = 20
    self.backupFilename = "dataChanges"
    self._initSignals()
    #User friendly hack
    self.do_exit = self.do_quit
    # store history
    histfilename = os.path.basename(sys.argv[0])
    historyFile = os.path.expanduser( "~/.dirac/%s.history" % histfilename[0:-3])
    if not os.path.exists( os.path.dirname(historyFile) ):
      os.makedirs( os.path.dirname(historyFile) )
    if os.path.isfile( historyFile ):
      readline.read_history_file( historyFile )
    readline.set_history_length(1000)
    atexit.register( readline.write_history_file, historyFile )


  def start( self ):
    if self.connected:
      self.modificator.loadFromRemote()
      retVal = self.modificator.loadCredentials()
      if not retVal[ 'OK' ]:
        print "There was an error gathering your credentials"
        print retVal[ 'Message' ]
        self._setStatus( False )
    try:
      self.cmdloop()
    except KeyboardInterrupt:
      gLogger.warn( "Received a keyboard interrupt." )
      self.do_quit( "" )

  def _initSignals( self ):
    """
    Registers signal handlers
    """
    for sigNum in ( signal.SIGINT, signal.SIGQUIT, signal.SIGKILL, signal.SIGTERM ):
      try:
        signal.signal( sigNum, self.do_quit )
      except:
        pass

  def _setConnected( self, connected, writeEnabled ):
    self.connected = connected
    self.modifiedData = False
    self.writeEnabled = writeEnabled
    if connected:
      if writeEnabled:
        self.prompt = "(%s)-%s> " % ( self.masterURL, colorize( "Connected", "green" ) )
      else:
        self.prompt = "(%s)-%s> " % ( self.masterURL, colorize( "Connected (RO)", "yellow" ) )
    else:
      self.prompt = "(%s)-%s> " % ( self.masterURL, colorize( "Disconnected", "red" ) )


  def _printPair( self, key, value, separator = ":" ):
    valueList = value.split( "\n" )
    print "%s%s%s %s" % ( key, " " * ( self.identSpace - len( key ) ), separator, valueList[0].strip() )
    for valueLine in valueList[ 1:-1 ]:
      print "%s  %s" % ( " " * self.identSpace, valueLine.strip() )

  def do_quit( self, dummy ):
    """
    Exits the application without sending changes to server

    Usage: quit
    """
    if self.modifiedData:
      print "Changes are about to be written to file for later use."
      self.do_writeToFile( self.backupFilename )
      print "Changes written to %s.cfg" % self.backupFilename
    sys.exit( 0 )

  def do_EOF( self, args ):
    """
    Accepts ctrl^D to quit CLI
    """
    print ""
    self.do_quit( args )

  def do_help( self, args ):
    """
    Shows help information

    Usage: help <command>

    If no command is specified all commands are shown

    """
    if len( args ) == 0:
      print "\nAvailable commands:\n"
      attrList = dir( self )
      attrList.sort()
      for attribute in attrList:
        if attribute.find( "do_" ) == 0:
          self._printPair( attribute[ 3: ], getattr( self, attribute ).__doc__[ 1: ] )
          print ""
    else:
      command = args.split()[0].strip()
      try:
        obj = getattr( self, "do_%s" % command )
      except:
        print "There's no such %s command" % command
        return
      self._printPair( command, obj.__doc__[1:] )

#  def retrieveData( self ):
#    if not self.connected:
#      return False
#    response = self.rpcClient.dumpCompressed()
#    if response[ 'Status' ] == 'OK':
#      self.cDataHolder.loadFromCompressedSource( response[ 'Value' ] )
#      gLogger.info( "Data retrieved from server." )
#      return True
#    else:
#      gLogger.error( "Can't retrieve updated data from server." )
#      return False

  def _setStatus( self, connected = True ):
    if not connected:
      self.masterURL = "unset"
      self._setConnected( False, False )
    else:
      retVal = self.rpcClient.writeEnabled()
      if retVal[ 'OK' ]:
        if retVal[ 'Value' ] == True:
          self._setConnected( True, True )
        else:
          self._setConnected( True, False )
      else:
        print "Server returned an error: %s" % retVal[ 'Message' ]
        self._setConnected( True, False )

  def _tryConnection( self ):
    print "Trying connection to %s" % self.masterURL
    try:
      self.rpcClient = RPCClient( self.masterURL )
      self._setStatus()
    except Exception, x:
      gLogger.error( "Couldn't connect to master CS server", "%s (%s)" % ( self.masterURL, str( x ) ) )
      self._setStatus( False )