Пример #1
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() )
Пример #2
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))
Пример #3
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()
Пример #4
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 )