def updateFlag():
    # Here now setting the flag as the following inside /Operations/Defaults:
    # in Operations/Defaults/Services/JobMonitoring/useESForJobParametersFlag

    from DIRAC.ConfigurationSystem.Client.CSAPI import CSAPI
    csAPI = CSAPI()

    res = csAPI.createSection('Operations/Defaults/Services/')
    if not res['OK']:
        print(res['Message'])
        exit(1)

    res = csAPI.createSection('Operations/Defaults/Services/JobMonitoring/')
    if not res['OK']:
        print(res['Message'])
        exit(1)
    csAPI.setOption(
        'Operations/Defaults/Services/JobMonitoring/useESForJobParametersFlag',
        True)

    csAPI.commit()

    # Now we need to restart the services for the new configuration to be picked up

    time.sleep(2)

    os.system("dirac-restart-component WorkloadManagement JobMonitoring")
    os.system("dirac-restart-component WorkloadManagement JobStateUpdate")

    time.sleep(5)
예제 #2
0
def updatePilot(version, vo):
    """
  Update in the CS the pilot version used,
  If only one version present in CS it's overwritten.
  If two versions present, the new one is added and the last removed

  :param version: version vArBpC of pilot you want to use
  :param vo: Location of pilot version in CS /Operations/<vo>/Pilot/Version
  """
    setup = vo
    if not vo:
        setup = gConfig.getValue('/DIRAC/DefaultSetup')
    if not setup:
        return S_ERROR("No value set for /DIRAC/DefaultSetup in CS")

    pilotVersion = gConfig.getValue('Operations/%s/Pilot/Version' % setup, [])
    if not pilotVersion:
        return S_ERROR(
            "No pilot version set under Operations/%s/Pilot/Version in CS" %
            setup)

    pilotVersion.pop()
    pilotVersion.insert(0, version)
    api = CSAPI()
    api.setOption('Operations/%s/Pilot/Version' % setup,
                  ", ".join(pilotVersion))
    result = api.commit()
    if not result['OK']:
        gLogger.fatal('Could not commit new version of pilot!')
        return result

    newVersion = gConfig.getValue('Operations/%s/Pilot/Version' % setup)
    return S_OK("New version of pilot set to %s" % newVersion)
예제 #3
0
def updateCS(changeSet):

    global vo, dry, ceBdiiDict

    changeList = sorted(changeSet)
    if dry:
        gLogger.notice('The following needed changes are detected:\n')
    else:
        gLogger.notice('We are about to make the following changes to CS:\n')
    for entry in changeList:
        gLogger.notice("%s/%s %s -> %s" % entry)

    if not dry:
        csAPI = CSAPI()
        csAPI.initialize()
        result = csAPI.downloadCSData()
        if not result['OK']:
            gLogger.error('Failed to initialize CSAPI object',
                          result['Message'])
            DIRACExit(-1)
        for section, option, value, new_value in changeSet:
            if value == 'Unknown' or not value:
                csAPI.setOption(cfgPath(section, option), new_value)
            else:
                csAPI.modifyValue(cfgPath(section, option), new_value)

        yn = six.moves.input(
            'Do you want to commit changes to CS ? [default yes] [yes|no]: ')
        if yn == '' or yn.lower().startswith('y'):
            result = csAPI.commit()
            if not result['OK']:
                gLogger.error("Error while commit to CS", result['Message'])
            else:
                gLogger.notice("Successfully committed %d changes to CS" %
                               len(changeSet))
예제 #4
0
def updateCS( changeSet ):
  
  global vo, dry, ceBdiiDict
  
  changeList = list( changeSet )
  changeList.sort()
  if dry:
    gLogger.notice( 'The following needed changes are detected:\n' )
  else:  
    gLogger.notice( 'We are about to make the following changes to CS:\n' )
  for entry in changeList:
    gLogger.notice( "%s/%s %s -> %s" % entry )

  if not dry:
    csAPI = CSAPI()
    csAPI.initialize()
    result = csAPI.downloadCSData()
    if not result['OK']:
      gLogger.error( 'Failed to initialize CSAPI object', result['Message'] )
      DIRACExit( -1 )
    for section, option, value, new_value in changeSet:
      if value == 'Unknown' or not value:
        csAPI.setOption( cfgPath( section, option ), new_value )
      else:
        csAPI.modifyValue( cfgPath( section, option ), new_value )
        
    yn = raw_input( 'Do you want to commit changes to CS ? [default yes] [yes|no]: ' )
    if yn == '' or yn.lower().startswith( 'y' ):    
      result = csAPI.commit()
      if not result['OK']:
        gLogger.error( "Error while commit to CS", result['Message'] )
      else:
        gLogger.notice( "Successfully committed %d changes to CS" % len( changeSet ) )  
예제 #5
0
def main():
    Script.parseCommandLine(ignoreErrors=True)
    args = Script.getPositionalArgs()

    csAPI = CSAPI()

    if len(args) < 3:
        Script.showHelp(exitCode=1)

    shifterRole = args[0]
    userName = args[1]
    diracGroup = args[2]

    res = csAPI.addShifter(
        {shifterRole: {
            'User': userName,
            'Group': diracGroup
        }})
    if not res['OK']:
        gLogger.error("Could not add shifter", ": " + res['Message'])
        DIRACExit(1)
    res = csAPI.commit()
    if not res['OK']:
        gLogger.error("Could not add shifter", ": " + res['Message'])
        DIRACExit(1)
    gLogger.notice("Added shifter %s as user %s with group %s" %
                   (shifterRole, userName, diracGroup))
예제 #6
0
def main():
    # Registering arguments will automatically add their description to the help menu
    Script.registerArgument(
        "ShifterRole:  Name of the shifter role, e.g. DataManager")
    Script.registerArgument(
        "UserName:     A user name, as registered in Registry section")
    Script.registerArgument(
        "DIRACGroup:   DIRAC Group, e.g. diracAdmin (the user has to have this role)"
    )
    Script.parseCommandLine(ignoreErrors=True)

    csAPI = CSAPI()

    shifterRole, userName, diracGroup = Script.getPositionalArgs(group=True)
    res = csAPI.addShifter(
        {shifterRole: {
            "User": userName,
            "Group": diracGroup
        }})
    if not res["OK"]:
        gLogger.error("Could not add shifter", ": " + res["Message"])
        DIRACExit(1)
    res = csAPI.commit()
    if not res["OK"]:
        gLogger.error("Could not add shifter", ": " + res["Message"])
        DIRACExit(1)
    gLogger.notice("Added shifter %s as user %s with group %s" %
                   (shifterRole, userName, diracGroup))
예제 #7
0
    def commit(self):
        """Commit the changes to the configuration system."""
        # Perform all the appending operations at the end to only get from current config once.
        for path, value in chain(self._append_dict.iteritems(),
                                 self._append_unique_dict.iteritems()):
            section, option = path.rsplit('/', 1)
            self.add(section, option, value)
            self._num_changes += 1

        result = CSAPI.commit(self)
        if not result['OK']:
            gLogger.error("Error while commit to CS", result['Message'])
            raise RuntimeError("Error while commit to CS")
        if self._num_changes:
            gLogger.notice("Successfully committed %d changes to CS\n"
                           % self._num_changes)
            self._num_changes = 0
            self._append_dict.clear()
            self._append_unique_dict.clear()
        else:
            gLogger.notice("No changes to commit")
예제 #8
0
class Bdii2CSAgent(AgentModule):
    def __init__(self, *args, **kwargs):
        """ Defines default parameters
    """

        super(Bdii2CSAgent, self).__init__(*args, **kwargs)

        self.addressTo = ''
        self.addressFrom = ''
        self.voName = []
        self.subject = "Bdii2CSAgent"
        self.alternativeBDIIs = []
        self.voBdiiCEDict = {}
        self.voBdiiSEDict = {}

        self.csAPI = None

        # What to get
        self.processCEs = True
        self.processSEs = False
        # Update the CS or not?
        self.dryRun = False

    def initialize(self):
        """ Gets run paramaters from the configuration
    """

        self.addressTo = self.am_getOption('MailTo', self.addressTo)
        self.addressFrom = self.am_getOption('MailFrom', self.addressFrom)
        # Create a list of alternative bdii urls
        self.alternativeBDIIs = self.am_getOption('AlternativeBDIIs',
                                                  self.alternativeBDIIs)
        # Check if the bdii url is appended by a port number, if not append the default 2170
        for index, url in enumerate(self.alternativeBDIIs):
            if not url.split(':')[-1].isdigit():
                self.alternativeBDIIs[index] += ':2170'
        if self.addressTo and self.addressFrom:
            self.log.info("MailTo", self.addressTo)
            self.log.info("MailFrom", self.addressFrom)
        if self.alternativeBDIIs:
            self.log.info("AlternativeBDII URLs:", self.alternativeBDIIs)

        self.processCEs = self.am_getOption('ProcessCEs', self.processCEs)
        self.processSEs = self.am_getOption('ProcessSEs', self.processSEs)
        self.dryRun = self.am_getOption('DryRun', self.dryRun)

        self.voName = self.am_getOption('VirtualOrganization', self.voName)
        if not self.voName:
            self.voName = self.am_getOption('VO', [])
        if not self.voName or (len(self.voName) == 1
                               and self.voName[0].lower() == 'all'):
            # Get all VOs defined in the configuration
            self.voName = []
            result = getVOs()
            if result['OK']:
                vos = result['Value']
                for vo in vos:
                    vomsVO = getVOOption(vo, "VOMSName")
                    if vomsVO:
                        self.voName.append(vomsVO)

        if self.voName:
            self.log.info("Agent will manage VO(s) %s" % self.voName)
        else:
            self.log.fatal("VirtualOrganization option not defined for agent")
            return S_ERROR()

        self.csAPI = CSAPI()
        return self.csAPI.initialize()

    def execute(self):
        """ General agent execution method
    """
        self.voBdiiCEDict = {}
        self.voBdiiSEDict = {}

        # Get a "fresh" copy of the CS data
        result = self.csAPI.downloadCSData()
        if not result['OK']:
            self.log.warn("Could not download a fresh copy of the CS data",
                          result['Message'])

        # Refresh the configuration from the master server
        gConfig.forceRefresh(fromMaster=True)

        if self.processCEs:
            self.__lookForNewCEs()
            self.__updateCEs()
        if self.processSEs:
            self.__lookForNewSEs()
            self.__updateSEs()
        return S_OK()

    def __lookForNewCEs(self):
        """ Look up BDII for CEs not yet present in the DIRAC CS
    """

        bannedCEs = self.am_getOption('BannedCEs', [])
        result = getCEsFromCS()
        if not result['OK']:
            return result
        knownCEs = set(result['Value'])
        knownCEs = knownCEs.union(set(bannedCEs))

        for vo in self.voName:
            result = self.__getBdiiCEInfo(vo)
            if not result['OK']:
                continue
            bdiiInfo = result['Value']
            result = getGridCEs(vo, bdiiInfo=bdiiInfo, ceBlackList=knownCEs)
            if not result['OK']:
                self.log.error('Failed to get unused CEs', result['Message'])
            siteDict = result['Value']
            body = ''
            for site in siteDict:
                newCEs = set(siteDict[site].keys())  # pylint: disable=no-member
                if not newCEs:
                    continue

                ceString = ''
                for ce in newCEs:
                    queueString = ''
                    ceInfo = bdiiInfo[site]['CEs'][ce]
                    newCEString = "CE: %s, GOCDB Site Name: %s" % (ce, site)
                    systemTuple = siteDict[site][ce]['System']
                    osString = "%s_%s_%s" % (systemTuple)
                    newCEString = "\n%s\n%s\n" % (newCEString, osString)
                    for queue in ceInfo['Queues']:
                        queueStatus = ceInfo['Queues'][queue].get(
                            'GlueCEStateStatus', 'UnknownStatus')
                        if 'production' in queueStatus.lower():
                            ceType = ceInfo['Queues'][queue].get(
                                'GlueCEImplementationName', '')
                            queueString += "   %s %s %s\n" % (
                                queue, queueStatus, ceType)
                    if queueString:
                        ceString += newCEString
                        ceString += "Queues:\n"
                        ceString += queueString

                if ceString:
                    body += ceString

            if body:
                body = "\nWe are glad to inform You about new CE(s) possibly suitable for %s:\n" % vo + body
                body += "\n\nTo suppress information about CE add its name to BannedCEs list.\n"
                body += "Add new Sites/CEs for vo %s with the command:\n" % vo
                body += "dirac-admin-add-resources --vo %s --ce\n" % vo
                self.log.info(body)
                if self.addressTo and self.addressFrom:
                    notification = NotificationClient()
                    result = notification.sendMail(self.addressTo,
                                                   self.subject,
                                                   body,
                                                   self.addressFrom,
                                                   localAttempt=False)
                    if not result['OK']:
                        self.log.error(
                            'Can not send new site notification mail',
                            result['Message'])

        return S_OK()

    def __getBdiiCEInfo(self, vo):

        if vo in self.voBdiiCEDict:
            return S_OK(self.voBdiiCEDict[vo])
        self.log.info("Check for available CEs for VO", vo)
        totalResult = S_OK({})
        message = ''

        mainResult = getBdiiCEInfo(vo)
        if not mainResult['OK']:
            self.log.error("Failed getting information from default bdii",
                           mainResult['Message'])
            message = mainResult['Message']

        for bdii in reversed(self.alternativeBDIIs):
            resultAlt = getBdiiCEInfo(vo, host=bdii)
            if resultAlt['OK']:
                totalResult['Value'].update(resultAlt['Value'])
            else:
                self.log.error("Failed getting information from %s " % bdii,
                               resultAlt['Message'])
                message = (message + "\n" + resultAlt['Message']).strip()

        if mainResult['OK']:
            totalResult['Value'].update(mainResult['Value'])

        if not totalResult[
                'Value'] and message:  ## Dict is empty and we have an error message
            self.log.error("Error during BDII request", message)
            totalResult = S_ERROR(message)
        else:
            self.voBdiiCEDict[vo] = totalResult['Value']
        return totalResult

    def __getBdiiSEInfo(self, vo):

        if vo in self.voBdiiSEDict:
            return S_OK(self.voBdiiSEDict[vo])
        self.log.info("Check for available SEs for VO", vo)
        result = getBdiiSEInfo(vo)
        message = ''
        if not result['OK']:
            message = result['Message']
            for bdii in self.alternativeBDIIs:
                result = getBdiiSEInfo(vo, host=bdii)
                if result['OK']:
                    break
        if not result['OK']:
            if message:
                self.log.error("Error during BDII request", message)
            else:
                self.log.error("Error during BDII request", result['Message'])
        else:
            self.voBdiiSEDict[vo] = result['Value']
        return result

    def __updateCEs(self):
        """ Update the Site/CE/queue settings in the CS if they were changed in the BDII
    """

        bdiiChangeSet = set()

        for vo in self.voName:
            result = self.__getBdiiCEInfo(vo)
            if not result['OK']:
                continue
            ceBdiiDict = result['Value']
            result = getSiteUpdates(vo, bdiiInfo=ceBdiiDict, log=self.log)
            if not result['OK']:
                continue
            bdiiChangeSet = bdiiChangeSet.union(result['Value'])

        # We have collected all the changes, consolidate VO settings
        result = self.__updateCS(bdiiChangeSet)
        return result

    def __updateCS(self, bdiiChangeSet):

        queueVODict = {}
        changeSet = set()
        for entry in bdiiChangeSet:
            section, option, _value, new_value = entry
            if option == "VO":
                queueVODict.setdefault(section, set())
                queueVODict[section] = queueVODict[section].union(
                    set(new_value.split(',')))
            else:
                changeSet.add(entry)
        for section, VOs in queueVODict.items():
            changeSet.add((section, 'VO', '', ','.join(VOs)))

        if changeSet:
            changeList = list(changeSet)
            changeList.sort()
            body = '\n'.join(
                ["%s/%s %s -> %s" % entry for entry in changeList])
            if body and self.addressTo and self.addressFrom:
                notification = NotificationClient()
                result = notification.sendMail(self.addressTo,
                                               self.subject,
                                               body,
                                               self.addressFrom,
                                               localAttempt=False)

            if body:
                self.log.info(
                    'The following configuration changes were detected:')
                self.log.info(body)

            for section, option, value, new_value in changeSet:
                if value == 'Unknown' or not value:
                    self.csAPI.setOption(cfgPath(section, option), new_value)
                else:
                    self.csAPI.modifyValue(cfgPath(section, option), new_value)

            if self.dryRun:
                self.log.info("Dry Run: CS won't be updated")
                self.csAPI.showDiff()
            else:
                result = self.csAPI.commit()
                if not result['OK']:
                    self.log.error("Error while committing to CS",
                                   result['Message'])
                else:
                    self.log.info("Successfully committed %d changes to CS" %
                                  len(changeList))
                return result
        else:
            self.log.info("No changes found")
            return S_OK()

    def __lookForNewSEs(self):
        """ Look up BDII for SEs not yet present in the DIRAC CS
    """

        bannedSEs = self.am_getOption('BannedSEs', [])
        result = getSEsFromCS()
        if not result['OK']:
            return result
        knownSEs = set(result['Value'])
        knownSEs = knownSEs.union(set(bannedSEs))

        for vo in self.voName:
            result = self.__getBdiiSEInfo(vo)
            if not result['OK']:
                continue
            bdiiInfo = result['Value']
            result = getGridSRMs(vo, bdiiInfo=bdiiInfo, srmBlackList=knownSEs)
            if not result['OK']:
                continue
            siteDict = result['Value']
            body = ''
            for site in siteDict:
                newSEs = set(siteDict[site].keys())  # pylint: disable=no-member
                if not newSEs:
                    continue
                for se in newSEs:
                    body += '\n New SE %s available at site %s:\n' % (se, site)
                    backend = siteDict[site][se]['SE'].get(
                        'GlueSEImplementationName', 'Unknown')
                    size = siteDict[site][se]['SE'].get(
                        'GlueSESizeTotal', 'Unknown')
                    body += '  Backend %s, Size %s' % (backend, size)

            if body:
                body = "\nWe are glad to inform You about new SE(s) possibly suitable for %s:\n" % vo + body
                body += "\n\nTo suppress information about an SE add its name to BannedSEs list.\n"
                body += "Add new SEs for vo %s with the command:\n" % vo
                body += "dirac-admin-add-resources --vo %s --se\n" % vo
                self.log.info(body)
                if self.addressTo and self.addressFrom:
                    notification = NotificationClient()
                    result = notification.sendMail(self.addressTo,
                                                   self.subject,
                                                   body,
                                                   self.addressFrom,
                                                   localAttempt=False)
                    if not result['OK']:
                        self.log.error(
                            'Can not send new site notification mail',
                            result['Message'])

        return S_OK()

    def __updateSEs(self):
        """ Update the Storage Element settings in the CS if they were changed in the BDII
    """

        bdiiChangeSet = set()

        for vo in self.voName:
            result = self.__getBdiiSEInfo(vo)
            if not result['OK']:
                continue
            seBdiiDict = result['Value']
            result = getSRMUpdates(vo, bdiiInfo=seBdiiDict)
            if not result['OK']:
                continue
            bdiiChangeSet = bdiiChangeSet.union(result['Value'])

        # We have collected all the changes, consolidate VO settings
        result = self.__updateCS(bdiiChangeSet)
        return result
예제 #9
0
class CE2CSAgent( AgentModule ):

  addressTo = ''
  addressFrom = ''
  voName = ''
  subject = "CE2CSAgent"
  alternativeBDIIs = []

  def initialize( self ):

    # TODO: Have no default and if no mail is found then use the diracAdmin group
    # and resolve all associated mail addresses.
    self.addressTo = self.am_getOption( 'MailTo', self.addressTo )
    self.addressFrom = self.am_getOption( 'MailFrom', self.addressFrom )
    # create a list of alternative bdii urls
    self.alternativeBDIIs = self.am_getOption( 'AlternativeBDIIs', [] )
    # check if the bdii url is appended by a port number, if not append the default 2170
    for index, url in enumerate( self.alternativeBDIIs ):
      if not url.split( ':' )[-1].isdigit():
        self.alternativeBDIIs[index] += ':2170'
    if self.addressTo and self.addressFrom:
      self.log.info( "MailTo", self.addressTo )
      self.log.info( "MailFrom", self.addressFrom )
    if self.alternativeBDIIs :
      self.log.info( "AlternativeBDII URLs:", self.alternativeBDIIs )
    self.subject = "CE2CSAgent"

    # This sets the Default Proxy to used as that defined under
    # /Operations/Shifter/TestManager
    # the shifterProxy option in the Configuration can be used to change this default.
    self.am_setOption( 'shifterProxy', 'TestManager' )

    self.voName = self.am_getOption( 'VirtualOrganization', self.voName )
    if not self.voName:
      self.voName = getVO()

    if not self.voName:
      self.log.fatal( "VO option not defined for agent" )
      return S_ERROR()

    self.csAPI = CSAPI()
    return self.csAPI.initialize()

  def execute( self ):

    self.log.info( "Start Execution" )
    result = getProxyInfo()
    if not result[ 'OK' ]:
      return result
    infoDict = result[ 'Value' ]
    self.log.info( formatProxyInfoAsString( infoDict ) )

    #Get a "fresh" copy of the CS data
    result = self.csAPI.downloadCSData()
    if not result[ 'OK' ]:
      self.log.warn( "Could not download a fresh copy of the CS data", result[ 'Message' ] )

    self.__lookForCE()
    self.__infoFromCE()
    self.log.info( "End Execution" )
    return S_OK()

  def __checkAlternativeBDIISite( self, fun, *args ):
    if self.alternativeBDIIs:
      self.log.warn( "Trying to use alternative bdii sites" )
      for site in self.alternativeBDIIs :
        self.log.info( "Trying to contact alternative bdii ", site )
        if len( args ) == 1 :
          result = fun( args[0], host = site )
        elif len( args ) == 2 :
          result = fun( args[0], vo = args[1], host = site )
        if not result['OK'] :
          self.log.error ( "Problem contacting alternative bddii", result['Message'] )
        elif result['OK'] :
          return result
      self.log.warn( "Also checking alternative BDII sites failed" )
      return result

  def __lookForCE( self ):

    knownces = self.am_getOption( 'BannedCEs', [] )

    result = gConfig.getSections( '/Resources/Sites' )
    if not result['OK']:
      return
    grids = result['Value']

    for grid in grids:

      result = gConfig.getSections( '/Resources/Sites/%s' % grid )
      if not result['OK']:
        return
      sites = result['Value']

      for site in sites:
        opt = gConfig.getOptionsDict( '/Resources/Sites/%s/%s' % ( grid, site ) )['Value']
        ces = List.fromChar( opt.get( 'CE', '' ) )
        knownces += ces

    response = ldapCEState( '', vo = self.voName )
    if not response['OK']:
      self.log.error( "Error during BDII request", response['Message'] )
      response = self.__checkAlternativeBDIISite( ldapCEState, '', self.voName )
      return response

    newces = {}
    for queue in response['Value']:
      try:
        queuename = queue['GlueCEUniqueID']
      except:
        continue

      cename = queuename.split( ":" )[0]
      if not cename in knownces:
        newces[cename] = None
        self.log.debug( "newce", cename )

    body = ""
    possibleNewSites = []
    for ce in newces.iterkeys():
      response = ldapCluster( ce )
      if not response['OK']:
        self.log.warn( "Error during BDII request", response['Message'] )
        response = self.__checkAlternativeBDIISite( ldapCluster, ce )
        continue
      clusters = response['Value']
      if len( clusters ) != 1:
        self.log.warn( "Error in cluster length", " CE %s Length %d" % ( ce, len( clusters ) ) )
      if len( clusters ) == 0:
        continue
      cluster = clusters[0]
      fkey = cluster.get( 'GlueForeignKey', [] )
      if type( fkey ) == type( '' ):
        fkey = [fkey]
      nameBDII = None
      for entry in fkey:
        if entry.count( 'GlueSiteUniqueID' ):
          nameBDII = entry.split( '=' )[1]
          break
      if not nameBDII:
        continue

      cestring = "CE: %s, GOCDB Name: %s" % ( ce, nameBDII )
      self.log.info( cestring )

      response = ldapCE( ce )
      if not response['OK']:
        self.log.warn( "Error during BDII request", response['Message'] )
        response = self.__checkAlternativeBDIISite( ldapCE, ce )
        continue

      ceinfos = response['Value']
      if len( ceinfos ):
        ceinfo = ceinfos[0]
        systemName = ceinfo.get( 'GlueHostOperatingSystemName', 'Unknown' )
        systemVersion = ceinfo.get( 'GlueHostOperatingSystemVersion', 'Unknown' )
        systemRelease = ceinfo.get( 'GlueHostOperatingSystemRelease', 'Unknown' )
      else:
        systemName = "Unknown"
        systemVersion = "Unknown"
        systemRelease = "Unknown"

      osstring = "SystemName: %s, SystemVersion: %s, SystemRelease: %s" % ( systemName, systemVersion, systemRelease )
      self.log.info( osstring )

      response = ldapCEState( ce, vo = self.voName )
      if not response['OK']:
        self.log.warn( "Error during BDII request", response['Message'] )
        response = self.__checkAlternativeBDIISite( ldapCEState, ce, self.voName )
        continue

      newcestring = "\n\n%s\n%s" % ( cestring, osstring )
      usefull = False
      cestates = response['Value']
      for cestate in cestates:
        queuename = cestate.get( 'GlueCEUniqueID', 'UnknownName' )
        queuestatus = cestate.get( 'GlueCEStateStatus', 'UnknownStatus' )

        queuestring = "%s %s" % ( queuename, queuestatus )
        self.log.info( queuestring )
        newcestring += "\n%s" % queuestring
        if queuestatus.count( 'Production' ):
          usefull = True
      if usefull:
        body += newcestring
        possibleNewSites.append( 'dirac-admin-add-site DIRACSiteName %s %s' % ( nameBDII, ce ) )
    if body:
      body = "We are glad to inform You about new CE(s) possibly suitable for %s:\n" % self.voName + body
      body += "\n\nTo suppress information about CE add its name to BannedCEs list."
      for  possibleNewSite in  possibleNewSites:
        body = "%s\n%s" % ( body, possibleNewSite )
      self.log.info( body )
      if self.addressTo and self.addressFrom:
        notification = NotificationClient()
        result = notification.sendMail( self.addressTo, self.subject, body, self.addressFrom, localAttempt = False )

    return S_OK()

  def __infoFromCE( self ):

    sitesSection = cfgPath( 'Resources', 'Sites' )
    result = gConfig.getSections( sitesSection )
    if not result['OK']:
      return
    grids = result['Value']

    changed = False
    body = ""

    for grid in grids:

      gridSection = cfgPath( sitesSection, grid )
      result = gConfig.getSections( gridSection )
      if not result['OK']:
        return
      sites = result['Value']

      for site in sites:
        siteSection = cfgPath( gridSection, site )
        opt = gConfig.getOptionsDict( siteSection )['Value']
        name = opt.get( 'Name', '' )
        if name:
          coor = opt.get( 'Coordinates', 'Unknown' )
          mail = opt.get( 'Mail', 'Unknown' )

          result = ldapSite( name )
          if not result['OK']:
            self.log.warn( "BDII site %s: %s" % ( name, result['Message'] ) )
            result = self.__checkAlternativeBDIISite( ldapSite, name )

          if result['OK']:
            bdiisites = result['Value']
            if len( bdiisites ) == 0:
              self.log.warn( name, "Error in bdii: leng = 0" )
            else:
              if not len( bdiisites ) == 1:
                self.log.warn( name, "Warning in bdii: leng = %d" % len( bdiisites ) )

              bdiisite = bdiisites[0]

              try:
                longitude = bdiisite['GlueSiteLongitude']
                latitude = bdiisite['GlueSiteLatitude']
                newcoor = "%s:%s" % ( longitude, latitude )
              except:
                self.log.warn( "Error in bdii coor" )
                newcoor = "Unknown"

              try:
                newmail = bdiisite['GlueSiteSysAdminContact'].split( ":" )[-1].strip()
              except:
                self.log.warn( "Error in bdii mail" )
                newmail = "Unknown"

              self.log.debug( "%s %s %s" % ( name, newcoor, newmail ) )

              if newcoor != coor:
                self.log.info( "%s" % ( name ), "%s -> %s" % ( coor, newcoor ) )
                if coor == 'Unknown':
                  self.csAPI.setOption( cfgPath( siteSection, 'Coordinates' ), newcoor )
                else:
                  self.csAPI.modifyValue( cfgPath( siteSection, 'Coordinates' ), newcoor )
                changed = True

              if newmail != mail:
                self.log.info( "%s" % ( name ), "%s -> %s" % ( mail, newmail ) )
                if mail == 'Unknown':
                  self.csAPI.setOption( cfgPath( siteSection, 'Mail' ), newmail )
                else:
                  self.csAPI.modifyValue( cfgPath( siteSection, 'Mail' ), newmail )
                changed = True

        celist = List.fromChar( opt.get( 'CE', '' ) )

        if not celist:
          self.log.warn( site, 'Empty site list' )
          continue

  #      result = gConfig.getSections( cfgPath( siteSection,'CEs' )
  #      if not result['OK']:
  #        self.log.debug( "Section CEs:", result['Message'] )

        for ce in celist:
          ceSection = cfgPath( siteSection, 'CEs', ce )
          result = gConfig.getOptionsDict( ceSection )
          if not result['OK']:
            self.log.debug( "Section CE", result['Message'] )
            wnTmpDir = 'Unknown'
            arch = 'Unknown'
            os = 'Unknown'
            si00 = 'Unknown'
            pilot = 'Unknown'
            cetype = 'Unknown'
          else:
            ceopt = result['Value']
            wnTmpDir = ceopt.get( 'wnTmpDir', 'Unknown' )
            arch = ceopt.get( 'architecture', 'Unknown' )
            os = ceopt.get( 'OS', 'Unknown' )
            si00 = ceopt.get( 'SI00', 'Unknown' )
            pilot = ceopt.get( 'Pilot', 'Unknown' )
            cetype = ceopt.get( 'CEType', 'Unknown' )

          result = ldapCE( ce )
          if not result['OK']:
            self.log.warn( 'Error in bdii for %s' % ce, result['Message'] )
            result = self.__checkAlternativeBDIISite( ldapCE, ce )
            continue
          try:
            bdiice = result['Value'][0]
          except:
            self.log.warn( 'Error in bdii for %s' % ce, result )
            bdiice = None
          if bdiice:
            try:
              newwnTmpDir = bdiice['GlueSubClusterWNTmpDir']
            except:
              newwnTmpDir = 'Unknown'
            if wnTmpDir != newwnTmpDir and newwnTmpDir != 'Unknown':
              section = cfgPath( ceSection, 'wnTmpDir' )
              self.log.info( section, " -> ".join( ( wnTmpDir, newwnTmpDir ) ) )
              if wnTmpDir == 'Unknown':
                self.csAPI.setOption( section, newwnTmpDir )
              else:
                self.csAPI.modifyValue( section, newwnTmpDir )
              changed = True

            try:
              newarch = bdiice['GlueHostArchitecturePlatformType']
            except:
              newarch = 'Unknown'
            if arch != newarch and newarch != 'Unknown':
              section = cfgPath( ceSection, 'architecture' )
              self.log.info( section, " -> ".join( ( arch, newarch ) ) )
              if arch == 'Unknown':
                self.csAPI.setOption( section, newarch )
              else:
                self.csAPI.modifyValue( section, newarch )
              changed = True

            try:
              newos = '_'.join( ( bdiice['GlueHostOperatingSystemName'],
                                  bdiice['GlueHostOperatingSystemVersion'],
                                  bdiice['GlueHostOperatingSystemRelease'] ) )
            except:
              newos = 'Unknown'
            if os != newos and newos != 'Unknown':
              section = cfgPath( ceSection, 'OS' )
              self.log.info( section, " -> ".join( ( os, newos ) ) )
              if os == 'Unknown':
                self.csAPI.setOption( section, newos )
              else:
                self.csAPI.modifyValue( section, newos )
              changed = True
              body = body + "OS was changed %s -> %s for %s at %s\n" % ( os, newos, ce, site )

            try:
              newsi00 = bdiice['GlueHostBenchmarkSI00']
            except:
              newsi00 = 'Unknown'
            if si00 != newsi00 and newsi00 != 'Unknown':
              section = cfgPath( ceSection, 'SI00' )
              self.log.info( section, " -> ".join( ( si00, newsi00 ) ) )
              if si00 == 'Unknown':
                self.csAPI.setOption( section, newsi00 )
              else:
                self.csAPI.modifyValue( section, newsi00 )
              changed = True

            try:
              rte = bdiice['GlueHostApplicationSoftwareRunTimeEnvironment']
              if self.voName.lower() == 'lhcb':
                if 'VO-lhcb-pilot' in rte:
                  newpilot = 'True'
                else:
                  newpilot = 'False'
              else:
                newpilot = 'Unknown'
            except:
              newpilot = 'Unknown'
            if pilot != newpilot and newpilot != 'Unknown':
              section = cfgPath( ceSection, 'Pilot' )
              self.log.info( section, " -> ".join( ( pilot, newpilot ) ) )
              if pilot == 'Unknown':
                self.csAPI.setOption( section, newpilot )
              else:
                self.csAPI.modifyValue( section, newpilot )
              changed = True

          result = ldapCEState( ce, vo = self.voName )        #getBDIICEVOView
          if not result['OK']:
            self.log.warn( 'Error in bdii for queue %s' % ce, result['Message'] )
            result = self.__checkAlternativeBDIISite( ldapCEState, ce, self.voName )
            continue
          try:
            queues = result['Value']
          except:
            self.log.warn( 'Error in bdii for queue %s' % ce, result['Massage'] )
            continue

          newcetype = 'Unknown'
          for queue in queues:
            try:
              queuetype = queue['GlueCEImplementationName']
            except:
              queuetype = 'Unknown'
            if newcetype == 'Unknown':
              newcetype = queuetype
            else:
              if queuetype != newcetype:
                self.log.warn( 'Error in bdii for ce %s ' % ce, 'different cetypes %s %s' % ( newcetype, queuetype ) )

          if newcetype=='ARC-CE':
            newcetype = 'ARC'

          if cetype != newcetype and newcetype != 'Unknown':
            section = cfgPath( ceSection, 'CEType' )
            self.log.info( section, " -> ".join( ( cetype, newcetype ) ) )
            if cetype == 'Unknown':
              self.csAPI.setOption( section, newcetype )
            else:
              self.csAPI.modifyValue( section, newcetype )
            changed = True

          for queue in queues:
            try:
              queueName = queue['GlueCEUniqueID'].split( '/' )[-1]
            except:
              self.log.warn( 'error in queuename ', queue )
              continue

            try:
              newmaxCPUTime = queue['GlueCEPolicyMaxCPUTime']
            except:
              newmaxCPUTime = None

            newsi00 = None
            try:
              caps = queue['GlueCECapability']
              if type( caps ) == type( '' ):
                caps = [caps]
              for cap in caps:
                if cap.count( 'CPUScalingReferenceSI00' ):
                  newsi00 = cap.split( '=' )[-1]
            except:
              newsi00 = None

            queueSection = cfgPath( ceSection, 'Queues', queueName )
            result = gConfig.getOptionsDict( queueSection )
            if not result['OK']:
              self.log.warn( "Section Queues", result['Message'] )
              maxCPUTime = 'Unknown'
              si00 = 'Unknown'
            else:
              queueopt = result['Value']
              maxCPUTime = queueopt.get( 'maxCPUTime', 'Unknown' )
              si00 = queueopt.get( 'SI00', 'Unknown' )

            if newmaxCPUTime and ( maxCPUTime != newmaxCPUTime ):
              section = cfgPath( queueSection, 'maxCPUTime' )
              self.log.info( section, " -> ".join( ( maxCPUTime, newmaxCPUTime ) ) )
              if maxCPUTime == 'Unknown':
                self.csAPI.setOption( section, newmaxCPUTime )
              else:
                self.csAPI.modifyValue( section, newmaxCPUTime )
              changed = True

            if newsi00 and ( si00 != newsi00 ):
              section = cfgPath( queueSection, 'SI00' )
              self.log.info( section, " -> ".join( ( si00, newsi00 ) ) )
              if si00 == 'Unknown':
                self.csAPI.setOption( section, newsi00 )
              else:
                self.csAPI.modifyValue( section, newsi00 )
              changed = True

    if changed:
      self.log.info( body )
      if body and self.addressTo and self.addressFrom:
        notification = NotificationClient()
        result = notification.sendMail( self.addressTo, self.subject, body, self.addressFrom, localAttempt = False )

      return self.csAPI.commit()
    else:
      self.log.info( "No changes found" )
      return S_OK()
예제 #10
0
#         }
#       }
#     }

res = csAPI.createSection("Registry")
if not res["OK"]:
    print(res["Message"])
    exit(1)
res = csAPI.createSection("Registry/VO/")
if not res["OK"]:
    print(res["Message"])
    exit(1)
res = csAPI.createSection("Registry/VO/Jenkins")
if not res["OK"]:
    print(res["Message"])
    exit(1)
res = csAPI.createSection("Registry/VO/Jenkins/VOMSName")
if not res["OK"]:
    print(res["Message"])
    exit(1)
csAPI.setOption("Registry/VO/Jenkins/VOMSName", "myVOMS")

csAPI.setOption("Registry/Groups/jenkins_fcadmin/VO", "Jenkins")
csAPI.setOption("Registry/Groups/jenkins_user/VO", "Jenkins")

# Final action: commit in CS
res = csAPI.commit()
if not res["OK"]:
    print(res["Message"])
    exit(1)
class CVMFSAdder(object):
  """Container for all the objects and functions to add software to ILCDirac"""
  def __init__(self, cliParams ):
    from DIRAC.ConfigurationSystem.Client.CSAPI import CSAPI
    self.modifiedCS = False
    self.softSec = "/Operations/Defaults/AvailableTarBalls"
    self.mailadress = '*****@*****.**'
    self.cliParams = cliParams
    self.parameter = dict( softSec = self.softSec,
                           platform = cliParams.platform,
                           version = cliParams.version,
                           basepath = cliParams.basePath,
                           initsctipt = cliParams.initScriptLocation
                         )
    self.applications = cliParams.applicationSet
    self.detmodels = {}
    self.csAPI = CSAPI()

  def findDDSimDetectorModels( self ):
    """ find all detector models in lcgeo and
    fill the self.detmodels dictionary with Detmodel as key and path as value

    :returns: None
    """
    for root,dirs,_files in os.walk( os.path.join( self.parameter["basepath"], "lcgeo" ) ):
      for direct in dirs:
        if root.endswith("compact"):
          ## the main xml file must have the same name as the folder
          xmlPath = os.path.join( root, direct, direct+".xml")
          if os.path.exists( xmlPath ):
            self.detmodels[direct] = xmlPath

  def checkConsistency(self):
    """checks if platform is defined, application exists, etc."""
    gLogger.notice("Checking consistency")
    av_platforms = gConfig.getSections(self.softSec, [])
    if av_platforms['OK']:
      if not self.parameter['platform'] in av_platforms['Value']:
        gLogger.error("Platform %s unknown, available are %s." % (self.parameter['platform'], ", ".join(av_platforms['Value'])))
        gLogger.error("If yours is missing, add it in CS")
        return S_ERROR()
    else:
      gLogger.error("Could not find all platforms available in CS")
      return S_ERROR()

    for application in self.applications:
      av_apps = gConfig.getSections("%(softSec)s/%(platform)s/" % self.parameter + str(application), [])
      if not av_apps['OK']:
        gLogger.error("Could not find this application in the CS: '%s'" % application)
        gLogger.error("Add its section to the CS, if it is missing")
        return S_ERROR()

    gLogger.notice("All OK, continuing...")
    return S_OK()


  def commitToCS(self):
    """write changes to the CS to the server"""
    if self.modifiedCS and not self.cliParams.dryRun:
      gLogger.notice("Commiting changes to the CS")
      result = self.csAPI.commit()
      if not result[ 'OK' ]:
        gLogger.error('Commit failed with message = %s' % (result[ 'Message' ]))
        return S_ERROR("Failed to commit to CS")
      gLogger.info('Successfully committed changes to CS')
    else:
      gLogger.info('No modifications to CS required')
    return S_OK()

  def addAllToCS(self):
    """add all the applications to the CS, take care of special cases (mokka, ildconfig, ddsim,...)"""
    from ILCDIRAC.ILCTransformationSystem.Utilities.ReleaseHelper import insertCSSection

    for application in self.applications:
      csParameter = dict( CVMFSEnvScript = self.cliParams.initScriptLocation,
                          CVMFSPath      = self.parameter['basepath']
                        )

      if application == 'mokka':
        csParameter['CVMFSDBSlice'] = self.cliParams.dbSliceLocation

      if application == 'ddsim':
        self.findDDSimDetectorModels()

        csPathModels = "Operations/Defaults/DDSimDetectorModels"
        csModels = { self.parameter["version"] : self.detmodels }
        insertCSSection( self.csAPI, csPathModels, csModels )
        self.modifiedCS = True

      elif application.endswith('config'):
        del csParameter['CVMFSEnvScript']
        csParameter['CVMFSPath'] = self.cliParams.configPath
        if self.cliParams.dbSliceLocation:
          csParameter['CVMFSDBSlice'] = self.cliParams.dbSliceLocation

      resInsert = self.insertApplicationToCS(application, csParameter)
      if not resInsert['OK']:
        return resInsert

    return S_OK()


  def insertApplicationToCS(self, name, csParameter):
    """add given application found via CVMFS to the CS"""

    pars = dict(self.parameter)
    pars['name'] = name

    gLogger.notice("%(name)s: Adding version %(version)s to the CS" % pars)

    existingVersions = gConfig.getSections("%(softSec)s/%(platform)s/%(name)s" % pars, [])
    if not existingVersions['OK']:
      gLogger.error("Could not find all versions available in CS: %s" % existingVersions['Message'])
      dexit(255)
    if pars['version'] in existingVersions['Value']:
      gLogger.always('Application %s %s for %s already in CS, nothing to do' % (name.lower(),
                                                                                pars['version'],
                                                                                pars['platform']))
      return S_OK()

    csPath = self.softSec + ("/%(platform)s/%(name)s/%(version)s/" % pars)
    for par, val in csParameter.iteritems():
      gLogger.notice("Add: %s = %s" %(csPath+par, val))
      result = self.csAPI.setOption(csPath+par, val)
      if result['OK']:
        self.modifiedCS = True
      else:
        gLogger.error("Failure to add to CS", result['Message'])
        return S_ERROR("")

    return S_OK()

  def addSoftware(self):
    """run all the steps to add software to grid and CS"""

    resAdd = self.addAllToCS()
    if not resAdd['OK']:
      return resAdd

    resCommit = self.commitToCS()
    if not resCommit['OK']:
      return resCommit

    return S_OK()
예제 #12
0
class GOCDB2CSAgent (AgentModule):
  """ Class to retrieve information about service endpoints
      from GOCDB and update configuration stored by CS
  """

  def __init__(self, *args, **kwargs):
    """ c'tor
    """
    super(GOCDB2CSAgent, self).__init__(*args, **kwargs)
    self.GOCDBClient = None
    self.csAPI = None
    self.dryRun = False

  def initialize(self):
    """ Run at the agent initialization (normally every 500 cycles)
    """
    # client to connect to GOCDB
    self.GOCDBClient = GOCDBClient()
    self.dryRun = self.am_getOption('DryRun', self.dryRun)

    # API needed to update configuration stored by CS
    self.csAPI = CSAPI()
    return self.csAPI.initialize()

  def execute(self):
    """
    Execute GOCDB queries according to the function map
    and user request (options in configuration).
    """

    # __functionMap is at the end of the class definition
    for option, functionCall in GOCDB2CSAgent.__functionMap.iteritems():
      optionValue = self.am_getOption(option, True)
      if optionValue:
        result = functionCall(self)
        if not result['OK']:
          self.log.error("%s() failed with message: %s" % (functionCall.__name__, result['Message']))
        else:
          self.log.info("Successfully executed %s" % functionCall.__name__)

    return S_OK()

  def updatePerfSONARConfiguration(self):
    """
    Get current status of perfSONAR endpoints from GOCDB
    and update CS configuration accordingly.
    """
    log = self.log.getSubLogger('updatePerfSONAREndpoints')
    log.debug('Begin function ...')

    # get endpoints
    result = self.__getPerfSONAREndpoints()
    if not result['OK']:
      log.error("__getPerfSONAREndpoints() failed with message: %s" % result['Message'])
      return S_ERROR('Unable to fetch perfSONAR endpoints from GOCDB.')
    endpointList = result['Value']

    # add DIRAC site name
    result = self.__addDIRACSiteName(endpointList)
    if not result['OK']:
      log.error("__addDIRACSiteName() failed with message: %s" % result['Message'])
      return S_ERROR('Unable to extend the list with DIRAC site names.')
    extendedEndpointList = result['Value']

    # prepare dictionary with new configuration
    result = self.__preparePerfSONARConfiguration(extendedEndpointList)
    if not result['OK']:
      log.error("__preparePerfSONARConfiguration() failed with message: %s" % result['Message'])
      return S_ERROR('Unable to prepare a new perfSONAR configuration.')
    finalConfiguration = result['Value']

    # update configuration according to the final status of endpoints
    self.__updateConfiguration(finalConfiguration)
    log.debug("Configuration updated succesfully")

    log.debug('End function.')
    return S_OK()

  def __getPerfSONAREndpoints(self):
    """
    Retrieve perfSONAR endpoint information directly from GOCDB.

    :return: List of perfSONAR endpoints (dictionaries) as stored by GOCDB.
    """

    log = self.log.getSubLogger('__getPerfSONAREndpoints')
    log.debug('Begin function ...')

    # get perfSONAR endpoints (latency and bandwidth) form GOCDB
    endpointList = []
    for endpointType in ['Latency', 'Bandwidth']:
      result = self.GOCDBClient.getServiceEndpointInfo('service_type', 'net.perfSONAR.%s' % endpointType)

      if not result['OK']:
        log.error("getServiceEndpointInfo() failed with message: %s" % result['Message'])
        return S_ERROR('Could not fetch %s endpoints from GOCDB' % endpointType.lower())

      log.debug('Number of %s endpoints: %s' % (endpointType.lower(), len(result['Value'])))
      endpointList.extend(result['Value'])

    log.debug('Number of perfSONAR endpoints: %s' % len(endpointList))
    log.debug('End function.')
    return S_OK(endpointList)

  def __preparePerfSONARConfiguration(self, endpointList):
    """
    Prepare a dictionary with a new CS configuration of perfSONAR endpoints.

    :return: Dictionary where keys are configuration paths (options and sections)
             and values are values of corresponding options
             or None in case of a path pointing to a section.
    """

    log = self.log.getSubLogger('__preparePerfSONARConfiguration')
    log.debug('Begin function ...')

    # static elements of a path
    rootPath = '/Resources/Sites'
    extPath = 'Network'
    baseOptionName = 'Enabled'
    options = {baseOptionName: 'True', 'ServiceType': 'perfSONAR'}

    # enable GOCDB endpoints in configuration
    newConfiguration = {}
    for endpoint in endpointList:
      if endpoint['DIRACSITENAME'] is None:
        continue

      split = endpoint['DIRACSITENAME'].split('.')
      path = cfgPath(rootPath, split[0], endpoint['DIRACSITENAME'], extPath, endpoint['HOSTNAME'])
      for name, defaultValue in options.iteritems():
        newConfiguration[cfgPath(path, name)] = defaultValue

    # get current configuration
    currentConfiguration = {}
    for option in options.iterkeys():
      result = gConfig.getConfigurationTree(rootPath, extPath + '/', '/' + option)
      if not result['OK']:
        log.error("getConfigurationTree() failed with message: %s" % result['Message'])
        return S_ERROR('Unable to fetch perfSONAR endpoints from CS.')
      currentConfiguration.update(result['Value'])

    # disable endpoints that disappeared in GOCDB
    removedElements = set(currentConfiguration) - set(newConfiguration)
    newElements = set(newConfiguration) - set(currentConfiguration)

    addedEndpoints = len(newElements) / len(options)
    disabledEndpoints = 0
    for path in removedElements:
      if baseOptionName in path:
        newConfiguration[path] = 'False'
        if currentConfiguration[path] != 'False':
          disabledEndpoints = disabledEndpoints + 1

    # inform what will be changed
    if addedEndpoints > 0:
      self.log.info("%s new perfSONAR endpoints will be added to the configuration" % addedEndpoints)

    if disabledEndpoints > 0:
      self.log.info("%s old perfSONAR endpoints will be disable in the configuration" % disabledEndpoints)

    if addedEndpoints == 0 and disabledEndpoints == 0:
      self.log.info("perfSONAR configuration is up-to-date")

    log.debug('End function.')
    return S_OK(newConfiguration)

  def __addDIRACSiteName(self, inputList):
    """
    Extend given list of GOCDB endpoints with DIRAC site name, i.e.
    add an entry "DIRACSITENAME" in dictionaries that describe endpoints.
    If given site name could not be found "DIRACSITENAME" is set to 'None'.

    :return: List of perfSONAR endpoints (dictionaries).
    """

    log = self.log.getSubLogger('__addDIRACSiteName')
    log.debug('Begin function ...')

    # get site name dictionary
    result = getDIRACGOCDictionary()
    if not result['OK']:
      log.error("getDIRACGOCDictionary() failed with message: %s" % result['Message'])
      return S_ERROR('Could not get site name dictionary')

    # reverse the dictionary (assume 1 to 1 relation)
    DIRACGOCDict = result['Value']
    GOCDIRACDict = dict(zip(DIRACGOCDict.values(), DIRACGOCDict.keys()))

    # add DIRAC site names
    outputList = []
    for entry in inputList:
      try:
        entry['DIRACSITENAME'] = GOCDIRACDict[entry['SITENAME']]
      except KeyError:
        self.log.warn("No dictionary entry for %s. " % entry['SITENAME'])
        entry['DIRACSITENAME'] = None
      outputList.append(entry)

    log.debug('End function.')
    return S_OK(outputList)

  def __updateConfiguration(self, setElements=None, delElements=None):
    """
    Update configuration stored by CS.
    """
    if setElements is None:
      setElements = {}
    if delElements is None:
      delElements = []

    log = self.log.getSubLogger('__updateConfiguration')
    log.debug('Begin function ...')

    # assure existence and proper value of a section or an option
    for path, value in setElements.iteritems():

      if value is None:
        section = path
      else:
        split = path.rsplit('/', 1)
        section = split[0]

      try:
        result = self.csAPI.createSection(section)
        if not result['OK']:
          log.error("createSection() failed with message: %s" % result['Message'])
      except Exception as e:
        log.error("Exception in createSection(): %s" % repr(e).replace(',)', ')'))

      if value is not None:
        try:
          result = self.csAPI.setOption(path, value)
          if not result['OK']:
            log.error("setOption() failed with message: %s" % result['Message'])
        except Exception as e:
          log.error("Exception in setOption(): %s" % repr(e).replace(',)', ')'))

    # delete elements in the configuration
    for path in delElements:
      result = self.csAPI.delOption(path)
      if not result['OK']:
        log.warn("delOption() failed with message: %s" % result['Message'])

        result = self.csAPI.delSection(path)
        if not result['OK']:
          log.warn("delSection() failed with message: %s" % result['Message'])

    if self.dryRun:
      log.info("Dry Run: CS won't be updated")
      self.csAPI.showDiff()
    else:
      # update configuration stored by CS
      result = self.csAPI.commit()
      if not result['OK']:
        log.error("commit() failed with message: %s" % result['Message'])
        return S_ERROR("Could not commit changes to CS.")
      else:
        log.info("Committed changes to CS")

    log.debug('End function.')
    return S_OK()

  # define mapping between an agent option in the configuration and a function call
  __functionMap = {'UpdatePerfSONARS': updatePerfSONARConfiguration,
                   }
예제 #13
0
    hasRun = True
    if SORTBYNAME:
        dirty = cfg["Resources"]["Sites"][i].sortAlphabetically(
            ascending=not REVERSE)
    else:
        dirty = cfg["Resources"]["Sites"][i].sortByKey(key=country,
                                                       reverse=REVERSE)
    if dirty:
        isDirty = True

if not hasRun:
    gLogger.notice(
        "Failed to find suitable subsections with site names to sort")
    DIRAC.exit(0)

if not isDirty:
    gLogger.notice("Nothing to do, site names are already sorted")
    DIRAC.exit(0)

timestamp = toString(dateTime())
stamp = "Site names are sorted by %s script at %s" % (Script.scriptName,
                                                      timestamp)
cs.setOptionComment("/Resources/Sites", stamp)

result = cs.commit()
if not result["OK"]:
    gLogger.error("Failed to commit changes to CS", result["Message"])
    DIRAC.exit(2)
gLogger.notice("Site names are sorted and committed to CS")
DIRAC.exit(0)
  Script.setUsageMessage( '\n'.join( [__doc__.split( '\n' )[1],
                                      'Usage:',
                                      '  %s [option|cfgfile] ... ShifterRole UserName DIRACGroup ...' % Script.scriptName,
                                      'Arguments:',
                                      '  ShifterRole: Name of the shifter role, e.g. DataManager',
                                      '  UserName: A user name, as registered in Registry section',
                                      '  DIRACGroup: DIRAC Group, e.g. diracAdmin (the user has to have this role)'] ) )
  Script.parseCommandLine( ignoreErrors = True )
  args = Script.getPositionalArgs( )

  csAPI = CSAPI( )

  if len( args ) < 3:
    Script.showHelp( )
    DIRACExit( -1 )

  shifterRole = args[0]
  userName = args[1]
  diracGroup = args[2]

  res = csAPI.addShifter( {shifterRole: {'User': userName, 'Group': diracGroup}} )
  if not res['OK']:
    gLogger.error( "Could not add shifter", ": " + res['Message'] )
    DIRACExit( 1 )
  res = csAPI.commit()
  if not res['OK']:
    gLogger.error( "Could not add shifter", ": " + res['Message'] )
    DIRACExit( 1 )
  gLogger.notice( "Added shifter %s as user %s with group %s" % (shifterRole, userName, diracGroup) )
예제 #15
0
class CE2CSAgent(AgentModule):

    addressTo = ""
    addressFrom = ""
    voName = ""
    subject = "CE2CSAgent"
    alternativeBDIIs = []

    def initialize(self):

        # TODO: Have no default and if no mail is found then use the diracAdmin group
        # and resolve all associated mail addresses.
        self.addressTo = self.am_getOption("MailTo", self.addressTo)
        self.addressFrom = self.am_getOption("MailFrom", self.addressFrom)
        # Create a list of alternative bdii urls
        self.alternativeBDIIs = self.am_getOption("AlternativeBDIIs", [])
        # Check if the bdii url is appended by a port number, if not append the default 2170
        for index, url in enumerate(self.alternativeBDIIs):
            if not url.split(":")[-1].isdigit():
                self.alternativeBDIIs[index] += ":2170"
        if self.addressTo and self.addressFrom:
            self.log.info("MailTo", self.addressTo)
            self.log.info("MailFrom", self.addressFrom)
        if self.alternativeBDIIs:
            self.log.info("AlternativeBDII URLs:", self.alternativeBDIIs)
        self.subject = "CE2CSAgent"

        # This sets the Default Proxy to used as that defined under
        # /Operations/Shifter/TestManager
        # the shifterProxy option in the Configuration can be used to change this default.
        self.am_setOption("shifterProxy", "TestManager")

        self.voName = self.am_getOption("VirtualOrganization", [])
        if not self.voName:
            vo = getVO()
            if vo:
                self.voName = [vo]

        if self.voName:
            self.log.info("Agent will manage VO(s) %s" % self.voName)
        else:
            self.log.fatal("VirtualOrganization option not defined for agent")
            return S_ERROR()

        self.csAPI = CSAPI()
        return self.csAPI.initialize()

    def execute(self):

        self.log.info("Start Execution")
        result = getProxyInfo()
        if not result["OK"]:
            return result
        infoDict = result["Value"]
        self.log.info(formatProxyInfoAsString(infoDict))

        # Get a "fresh" copy of the CS data
        result = self.csAPI.downloadCSData()
        if not result["OK"]:
            self.log.warn("Could not download a fresh copy of the CS data", result["Message"])

        self.__lookForCE()
        self.__infoFromCE()
        self.log.info("End Execution")
        return S_OK()

    def __checkAlternativeBDIISite(self, fun, *args):
        if self.alternativeBDIIs:
            self.log.warn("Trying to use alternative BDII sites")
            for site in self.alternativeBDIIs:
                self.log.info("Trying to contact alternative BDII", site)
                if len(args) == 1:
                    result = fun(args[0], host=site)
                elif len(args) == 2:
                    result = fun(args[0], vo=args[1], host=site)
                if not result["OK"]:
                    self.log.error("Problem contacting alternative BDII", result["Message"])
                elif result["OK"]:
                    return result
            self.log.warn("Also checking alternative BDII sites failed")
            return result

    def __lookForCE(self):

        knownCEs = self.am_getOption("BannedCEs", [])

        result = gConfig.getSections("/Resources/Sites")
        if not result["OK"]:
            return
        grids = result["Value"]

        for grid in grids:
            result = gConfig.getSections("/Resources/Sites/%s" % grid)
            if not result["OK"]:
                return
            sites = result["Value"]

            for site in sites:
                opt = gConfig.getOptionsDict("/Resources/Sites/%s/%s" % (grid, site))["Value"]
                ces = List.fromChar(opt.get("CE", ""))
                knownCEs += ces

        response = ""
        for vo in self.voName:
            self.log.info("Check for available CEs for VO", vo)
            response = ldapCEState("", vo)
            if not response["OK"]:
                self.log.error("Error during BDII request", response["Message"])
                response = self.__checkAlternativeBDIISite(ldapCEState, "", vo)
                return response

            newCEs = {}
            for queue in response["Value"]:
                try:
                    queueName = queue["GlueCEUniqueID"]
                except:
                    continue

                ceName = queueName.split(":")[0]
                if not ceName in knownCEs:
                    newCEs[ceName] = None
                    self.log.debug("New CE", ceName)

            body = ""
            possibleNewSites = []
            for ce in newCEs.iterkeys():
                response = ldapCluster(ce)
                if not response["OK"]:
                    self.log.warn("Error during BDII request", response["Message"])
                    response = self.__checkAlternativeBDIISite(ldapCluster, ce)
                    continue
                clusters = response["Value"]
                if len(clusters) != 1:
                    self.log.warn("Error in cluster length", " CE %s Length %d" % (ce, len(clusters)))
                if len(clusters) == 0:
                    continue
                cluster = clusters[0]
                fkey = cluster.get("GlueForeignKey", [])
                if type(fkey) == type(""):
                    fkey = [fkey]
                nameBDII = None
                for entry in fkey:
                    if entry.count("GlueSiteUniqueID"):
                        nameBDII = entry.split("=")[1]
                        break
                if not nameBDII:
                    continue

                ceString = "CE: %s, GOCDB Name: %s" % (ce, nameBDII)
                self.log.info(ceString)

                response = ldapCE(ce)
                if not response["OK"]:
                    self.log.warn("Error during BDII request", response["Message"])
                    response = self.__checkAlternativeBDIISite(ldapCE, ce)
                    continue

                ceInfos = response["Value"]
                if len(ceInfos):
                    ceInfo = ceInfos[0]
                    systemName = ceInfo.get("GlueHostOperatingSystemName", "Unknown")
                    systemVersion = ceInfo.get("GlueHostOperatingSystemVersion", "Unknown")
                    systemRelease = ceInfo.get("GlueHostOperatingSystemRelease", "Unknown")
                else:
                    systemName = "Unknown"
                    systemVersion = "Unknown"
                    systemRelease = "Unknown"

                osString = "SystemName: %s, SystemVersion: %s, SystemRelease: %s" % (
                    systemName,
                    systemVersion,
                    systemRelease,
                )
                self.log.info(osString)

                response = ldapCEState(ce, vo)
                if not response["OK"]:
                    self.log.warn("Error during BDII request", response["Message"])
                    response = self.__checkAlternativeBDIISite(ldapCEState, ce, vo)
                    continue

                newCEString = "\n\n%s\n%s" % (ceString, osString)
                usefull = False
                ceStates = response["Value"]
                for ceState in ceStates:
                    queueName = ceState.get("GlueCEUniqueID", "UnknownName")
                    queueStatus = ceState.get("GlueCEStateStatus", "UnknownStatus")

                    queueString = "%s %s" % (queueName, queueStatus)
                    self.log.info(queueString)
                    newCEString += "\n%s" % queueString
                    if queueStatus.count("Production"):
                        usefull = True
                if usefull:
                    body += newCEString
                    possibleNewSites.append("dirac-admin-add-site DIRACSiteName %s %s" % (nameBDII, ce))
            if body:
                body = "We are glad to inform You about new CE(s) possibly suitable for %s:\n" % vo + body
                body += "\n\nTo suppress information about CE add its name to BannedCEs list."
                for possibleNewSite in possibleNewSites:
                    body = "%s\n%s" % (body, possibleNewSite)
                self.log.info(body)
                if self.addressTo and self.addressFrom:
                    notification = NotificationClient()
                    result = notification.sendMail(
                        self.addressTo, self.subject, body, self.addressFrom, localAttempt=False
                    )

        return S_OK()

    def __infoFromCE(self):

        sitesSection = cfgPath("Resources", "Sites")
        result = gConfig.getSections(sitesSection)
        if not result["OK"]:
            return
        grids = result["Value"]

        changed = False
        body = ""

        for grid in grids:
            gridSection = cfgPath(sitesSection, grid)
            result = gConfig.getSections(gridSection)
            if not result["OK"]:
                return
            sites = result["Value"]

            for site in sites:
                siteSection = cfgPath(gridSection, site)
                opt = gConfig.getOptionsDict(siteSection)["Value"]
                name = opt.get("Name", "")
                if name:
                    coor = opt.get("Coordinates", "Unknown")
                    mail = opt.get("Mail", "Unknown")

                    result = ldapSite(name)
                    if not result["OK"]:
                        self.log.warn("BDII site %s: %s" % (name, result["Message"]))
                        result = self.__checkAlternativeBDIISite(ldapSite, name)

                    if result["OK"]:
                        bdiiSites = result["Value"]
                        if len(bdiiSites) == 0:
                            self.log.warn(name, "Error in BDII: leng = 0")
                        else:
                            if not len(bdiiSites) == 1:
                                self.log.warn(name, "Warning in BDII: leng = %d" % len(bdiiSites))

                            bdiiSite = bdiiSites[0]

                            try:
                                longitude = bdiiSite["GlueSiteLongitude"]
                                latitude = bdiiSite["GlueSiteLatitude"]
                                newcoor = "%s:%s" % (longitude, latitude)
                            except:
                                self.log.warn("Error in BDII coordinates")
                                newcoor = "Unknown"

                            try:
                                newmail = bdiiSite["GlueSiteSysAdminContact"].split(":")[-1].strip()
                            except:
                                self.log.warn("Error in BDII mail")
                                newmail = "Unknown"

                            self.log.debug("%s %s %s" % (name, newcoor, newmail))

                            if newcoor != coor:
                                self.log.info("%s" % (name), "%s -> %s" % (coor, newcoor))
                                if coor == "Unknown":
                                    self.csAPI.setOption(cfgPath(siteSection, "Coordinates"), newcoor)
                                else:
                                    self.csAPI.modifyValue(cfgPath(siteSection, "Coordinates"), newcoor)
                                changed = True

                            if newmail != mail:
                                self.log.info("%s" % (name), "%s -> %s" % (mail, newmail))
                                if mail == "Unknown":
                                    self.csAPI.setOption(cfgPath(siteSection, "Mail"), newmail)
                                else:
                                    self.csAPI.modifyValue(cfgPath(siteSection, "Mail"), newmail)
                                changed = True

                ceList = List.fromChar(opt.get("CE", ""))

                if not ceList:
                    self.log.warn(site, "Empty site list")
                    continue

                #      result = gConfig.getSections( cfgPath( siteSection,'CEs' )
                #      if not result['OK']:
                #        self.log.debug( "Section CEs:", result['Message'] )

                for ce in ceList:
                    ceSection = cfgPath(siteSection, "CEs", ce)
                    result = gConfig.getOptionsDict(ceSection)
                    if not result["OK"]:
                        self.log.debug("Section CE", result["Message"])
                        wnTmpDir = "Unknown"
                        arch = "Unknown"
                        os = "Unknown"
                        si00 = "Unknown"
                        pilot = "Unknown"
                        ceType = "Unknown"
                    else:
                        ceopt = result["Value"]
                        wnTmpDir = ceopt.get("wnTmpDir", "Unknown")
                        arch = ceopt.get("architecture", "Unknown")
                        os = ceopt.get("OS", "Unknown")
                        si00 = ceopt.get("SI00", "Unknown")
                        pilot = ceopt.get("Pilot", "Unknown")
                        ceType = ceopt.get("CEType", "Unknown")

                    result = ldapCE(ce)
                    if not result["OK"]:
                        self.log.warn("Error in BDII for %s" % ce, result["Message"])
                        result = self.__checkAlternativeBDIISite(ldapCE, ce)
                        continue
                    try:
                        bdiiCE = result["Value"][0]
                    except:
                        self.log.warn("Error in BDII for %s" % ce, result)
                        bdiiCE = None
                    if bdiiCE:
                        try:
                            newWNTmpDir = bdiiCE["GlueSubClusterWNTmpDir"]
                        except:
                            newWNTmpDir = "Unknown"
                        if wnTmpDir != newWNTmpDir and newWNTmpDir != "Unknown":
                            section = cfgPath(ceSection, "wnTmpDir")
                            self.log.info(section, " -> ".join((wnTmpDir, newWNTmpDir)))
                            if wnTmpDir == "Unknown":
                                self.csAPI.setOption(section, newWNTmpDir)
                            else:
                                self.csAPI.modifyValue(section, newWNTmpDir)
                            changed = True

                        try:
                            newArch = bdiiCE["GlueHostArchitecturePlatformType"]
                        except:
                            newArch = "Unknown"
                        if arch != newArch and newArch != "Unknown":
                            section = cfgPath(ceSection, "architecture")
                            self.log.info(section, " -> ".join((arch, newArch)))
                            if arch == "Unknown":
                                self.csAPI.setOption(section, newArch)
                            else:
                                self.csAPI.modifyValue(section, newArch)
                            changed = True

                        try:
                            newOS = "_".join(
                                (
                                    bdiiCE["GlueHostOperatingSystemName"],
                                    bdiiCE["GlueHostOperatingSystemVersion"],
                                    bdiiCE["GlueHostOperatingSystemRelease"],
                                )
                            )
                        except:
                            newOS = "Unknown"
                        if os != newOS and newOS != "Unknown":
                            section = cfgPath(ceSection, "OS")
                            self.log.info(section, " -> ".join((os, newOS)))
                            if os == "Unknown":
                                self.csAPI.setOption(section, newOS)
                            else:
                                self.csAPI.modifyValue(section, newOS)
                            changed = True
                            body = body + "OS was changed %s -> %s for %s at %s\n" % (os, newOS, ce, site)

                        try:
                            newSI00 = bdiiCE["GlueHostBenchmarkSI00"]
                        except:
                            newSI00 = "Unknown"
                        if si00 != newSI00 and newSI00 != "Unknown":
                            section = cfgPath(ceSection, "SI00")
                            self.log.info(section, " -> ".join((si00, newSI00)))
                            if si00 == "Unknown":
                                self.csAPI.setOption(section, newSI00)
                            else:
                                self.csAPI.modifyValue(section, newSI00)
                            changed = True

                        try:
                            rte = bdiiCE["GlueHostApplicationSoftwareRunTimeEnvironment"]
                            for vo in self.voName:
                                if vo.lower() == "lhcb":
                                    if "VO-lhcb-pilot" in rte:
                                        newPilot = "True"
                                    else:
                                        newPilot = "False"
                                else:
                                    newPilot = "Unknown"
                        except:
                            newPilot = "Unknown"
                        if pilot != newPilot and newPilot != "Unknown":
                            section = cfgPath(ceSection, "Pilot")
                            self.log.info(section, " -> ".join((pilot, newPilot)))
                            if pilot == "Unknown":
                                self.csAPI.setOption(section, newPilot)
                            else:
                                self.csAPI.modifyValue(section, newPilot)
                            changed = True

                    newVO = ""
                    for vo in self.voName:
                        result = ldapCEState(ce, vo)  # getBDIICEVOView
                        if not result["OK"]:
                            self.log.warn("Error in BDII for queue %s" % ce, result["Message"])
                            result = self.__checkAlternativeBDIISite(ldapCEState, ce, vo)
                            continue
                        try:
                            queues = result["Value"]
                        except:
                            self.log.warn("Error in BDII for queue %s" % ce, result["Massage"])
                            continue

                        newCEType = "Unknown"
                        for queue in queues:
                            try:
                                queueType = queue["GlueCEImplementationName"]
                            except:
                                queueType = "Unknown"
                            if newCEType == "Unknown":
                                newCEType = queueType
                            else:
                                if queueType != newCEType:
                                    self.log.warn(
                                        "Error in BDII for CE %s " % ce,
                                        "different CE types %s %s" % (newCEType, queueType),
                                    )

                        if newCEType == "ARC-CE":
                            newCEType = "ARC"

                        if ceType != newCEType and newCEType != "Unknown":
                            section = cfgPath(ceSection, "CEType")
                            self.log.info(section, " -> ".join((ceType, newCEType)))
                            if ceType == "Unknown":
                                self.csAPI.setOption(section, newCEType)
                            else:
                                self.csAPI.modifyValue(section, newCEType)
                            changed = True

                        for queue in queues:
                            try:
                                queueName = queue["GlueCEUniqueID"].split("/")[-1]
                            except:
                                self.log.warn("Error in queueName ", queue)
                                continue

                            try:
                                newMaxCPUTime = queue["GlueCEPolicyMaxCPUTime"]
                            except:
                                newMaxCPUTime = None

                            newSI00 = None
                            try:
                                caps = queue["GlueCECapability"]
                                if type(caps) == type(""):
                                    caps = [caps]
                                for cap in caps:
                                    if cap.count("CPUScalingReferenceSI00"):
                                        newSI00 = cap.split("=")[-1]
                            except:
                                newSI00 = None

                            queueSection = cfgPath(ceSection, "Queues", queueName)
                            result = gConfig.getOptionsDict(queueSection)
                            if not result["OK"]:
                                self.log.warn("Section Queues", result["Message"])
                                maxCPUTime = "Unknown"
                                si00 = "Unknown"
                                allowedVOs = [""]
                            else:
                                queueOpt = result["Value"]
                                maxCPUTime = queueOpt.get("maxCPUTime", "Unknown")
                                si00 = queueOpt.get("SI00", "Unknown")
                                if newVO == "":  # Remember previous iteration, if none - read from conf
                                    allowedVOs = queueOpt.get("VO", "").split(",")
                                else:  # Else use newVO, as it can contain changes, which aren't in conf yet
                                    allowedVOs = newVO.split(",")
                            if newMaxCPUTime and (maxCPUTime != newMaxCPUTime):
                                section = cfgPath(queueSection, "maxCPUTime")
                                self.log.info(section, " -> ".join((maxCPUTime, newMaxCPUTime)))
                                if maxCPUTime == "Unknown":
                                    self.csAPI.setOption(section, newMaxCPUTime)
                                else:
                                    self.csAPI.modifyValue(section, newMaxCPUTime)
                                changed = True

                            if newSI00 and (si00 != newSI00):
                                section = cfgPath(queueSection, "SI00")
                                self.log.info(section, " -> ".join((si00, newSI00)))
                                if si00 == "Unknown":
                                    self.csAPI.setOption(section, newSI00)
                                else:
                                    self.csAPI.modifyValue(section, newSI00)
                                changed = True

                            modifyVO = True  # Flag saying if we need VO option to change
                            newVO = ""
                            if allowedVOs != [""]:
                                for allowedVO in allowedVOs:
                                    allowedVO = allowedVO.strip()  # Get rid of spaces
                                    newVO += allowedVO
                                    if allowedVO == vo:  # Current VO has been already in list
                                        newVO = ""
                                        modifyVO = False  # Don't change anything
                                        break  # Skip next 'if', proceed to next VO
                                    newVO += ", "

                            if modifyVO:
                                section = cfgPath(queueSection, "VO")
                                newVO += vo
                                self.log.info(section, " -> ".join(("%s" % allowedVOs, newVO)))
                                if allowedVOs == [""]:
                                    self.csAPI.setOption(section, newVO)
                                else:
                                    self.csAPI.modifyValue(section, newVO)
                                changed = True

        if changed:
            self.log.info(body)
            if body and self.addressTo and self.addressFrom:
                notification = NotificationClient()
                result = notification.sendMail(self.addressTo, self.subject, body, self.addressFrom, localAttempt=False)

            return self.csAPI.commit()
        else:
            self.log.info("No changes found")
            return S_OK()
res = csAPI.createSection( 'Operations/Defaults/ResourceStatus/Policies/AlwaysBannedForSite' )
if not res['OK']:
  print res['Message']
  exit( 1 )
res = csAPI.createSection( 'Operations/Defaults/ResourceStatus/Policies/AlwaysBannedForSite/matchParams' )
csAPI.setOption( 'Operations/Defaults/ResourceStatus/Policies/AlwaysBannedForSite/policyType', 'AlwaysBanned' )
csAPI.setOption( 'Operations/Defaults/ResourceStatus/Policies/AlwaysBannedForSite/matchParams/element', 'Site' )


# Now setting the catalog list in Operations/Defults/Services/Catalogs/CatalogList

res = csAPI.createSection( 'Operations/Defaults/Services' )
if not res['OK']:
  print res['Message']
  exit( 1 )
res = csAPI.createSection( 'Operations/Defaults/Services/Catalogs' )
if not res['OK']:
  print res['Message']
  exit( 1 )
res = csAPI.createSection( 'Operations/Defaults/Services/Catalogs/CatalogList' )
if not res['OK']:
  print res['Message']
  exit( 1 )
csAPI.setOption('Operations/Defaults/Services/Catalogs/CatalogList', 'FileCatalog')



# Final action: commit in CS
csAPI.commit()
class CVMFSAdder(object):
    """Container for all the objects and functions to add software to ILCDirac"""
    def __init__(self, cliParams):
        from DIRAC.ConfigurationSystem.Client.CSAPI import CSAPI
        self.modifiedCS = False
        self.softSec = "/Operations/Defaults/AvailableTarBalls"
        self.mailadress = '*****@*****.**'
        self.cliParams = cliParams
        self.parameter = dict(softSec=self.softSec,
                              platform=cliParams.platform,
                              version=cliParams.version,
                              basepath=cliParams.basePath,
                              initsctipt=cliParams.initScriptLocation)
        self.applications = cliParams.applicationSet
        self.detmodels = {}
        self.csAPI = CSAPI()

    def findDDSimDetectorModels(self):
        """ find all detector models in lcgeo and
    fill the self.detmodels dictionary with Detmodel as key and path as value

    :returns: None
    """
        for root, dirs, _files in os.walk(
                os.path.join(self.parameter["basepath"], "lcgeo")):
            for direct in dirs:
                if root.endswith("compact"):
                    ## the main xml file must have the same name as the folder
                    xmlPath = os.path.join(root, direct, direct + ".xml")
                    if os.path.exists(xmlPath):
                        self.detmodels[direct] = xmlPath

    def checkConsistency(self):
        """checks if platform is defined, application exists, etc."""
        gLogger.notice("Checking consistency")
        av_platforms = gConfig.getSections(self.softSec, [])
        if av_platforms['OK']:
            if not self.parameter['platform'] in av_platforms['Value']:
                gLogger.error("Platform %s unknown, available are %s." %
                              (self.parameter['platform'], ", ".join(
                                  av_platforms['Value'])))
                gLogger.error("If yours is missing, add it in CS")
                return S_ERROR()
        else:
            gLogger.error("Could not find all platforms available in CS")
            return S_ERROR()

        for application in self.applications:
            av_apps = gConfig.getSections(
                "%(softSec)s/%(platform)s/" % self.parameter +
                str(application), [])
            if not av_apps['OK']:
                gLogger.error(
                    "Could not find this application in the CS: '%s'" %
                    application)
                gLogger.error("Add its section to the CS, if it is missing")
                return S_ERROR()

        gLogger.notice("All OK, continuing...")
        return S_OK()

    def commitToCS(self):
        """write changes to the CS to the server"""
        if self.modifiedCS and not self.cliParams.dryRun:
            gLogger.notice("Commiting changes to the CS")
            result = self.csAPI.commit()
            if not result['OK']:
                gLogger.error('Commit failed with message = %s' %
                              (result['Message']))
                return S_ERROR("Failed to commit to CS")
            gLogger.info('Successfully committed changes to CS')
        else:
            gLogger.info('No modifications to CS required')
        return S_OK()

    def addAllToCS(self):
        """add all the applications to the CS, take care of special cases (mokka, ildconfig, ddsim,...)"""
        from ILCDIRAC.ILCTransformationSystem.Utilities.ReleaseHelper import insertCSSection

        for application in self.applications:
            csParameter = dict(
                CVMFSEnvScript=self.cliParams.initScriptLocation,
                CVMFSPath=self.parameter['basepath'])

            if application == 'mokka':
                csParameter['CVMFSDBSlice'] = self.cliParams.dbSliceLocation

            if application == 'ddsim':
                self.findDDSimDetectorModels()

                csPathModels = "Operations/Defaults/DDSimDetectorModels"
                csModels = {self.parameter["version"]: self.detmodels}
                insertCSSection(self.csAPI, csPathModels, csModels)
                self.modifiedCS = True

            elif application.endswith('config'):
                del csParameter['CVMFSEnvScript']
                csParameter['CVMFSPath'] = self.cliParams.configPath
                if self.cliParams.dbSliceLocation:
                    csParameter[
                        'CVMFSDBSlice'] = self.cliParams.dbSliceLocation

            resInsert = self.insertApplicationToCS(application, csParameter)
            if not resInsert['OK']:
                return resInsert

        return S_OK()

    def insertApplicationToCS(self, name, csParameter):
        """add given application found via CVMFS to the CS"""

        pars = dict(self.parameter)
        pars['name'] = name

        gLogger.notice("%(name)s: Adding version %(version)s to the CS" % pars)

        existingVersions = gConfig.getSections(
            "%(softSec)s/%(platform)s/%(name)s" % pars, [])
        if not existingVersions['OK']:
            gLogger.error("Could not find all versions available in CS: %s" %
                          existingVersions['Message'])
            dexit(255)
        if pars['version'] in existingVersions['Value']:
            gLogger.always(
                'Application %s %s for %s already in CS, nothing to do' %
                (name.lower(), pars['version'], pars['platform']))
            return S_OK()

        csPath = self.softSec + ("/%(platform)s/%(name)s/%(version)s/" % pars)
        for par, val in csParameter.iteritems():
            gLogger.notice("Add: %s = %s" % (csPath + par, val))
            result = self.csAPI.setOption(csPath + par, val)
            if result['OK']:
                self.modifiedCS = True
            else:
                gLogger.error("Failure to add to CS", result['Message'])
                return S_ERROR("")

        return S_OK()

    def addSoftware(self):
        """run all the steps to add software to grid and CS"""

        resAdd = self.addAllToCS()
        if not resAdd['OK']:
            return resAdd

        resCommit = self.commitToCS()
        if not resCommit['OK']:
            return resCommit

        return S_OK()
예제 #18
0
class DDSimTarMaker(object):
    """ create a tarball of the DDSim release """
    def __init__(self):
        from DIRAC.ConfigurationSystem.Client.CSAPI import CSAPI

        self.detmodels = {}
        self.lcgeo_env = "lcgeo_DIR"
        self.ddhep_env = "DD4HEP"
        self.softSec = "/Operations/Defaults/AvailableTarBalls"
        self.version = ''
        self.platform = 'x86_64-slc5-gcc43-opt'
        self.comment = ""
        self.name = "ddsim"
        self.csapi = CSAPI()
        self.tarBallName = None
        self.md5sum = None

    def copyDetectorModels(self, basePath, folder, targetFolder):
        """copy the compact folders to the targetFolder """
        from ILCDIRAC.ILCTransformationSystem.Utilities.ReleaseHelper import copyFolder
        for root, dirs, _files in os.walk(os.path.join(basePath, folder)):
            for direct in dirs:
                if root.endswith("compact"):
                    ## the main xml file must have the same name as the folder, ILD and CLIC follow this convention already
                    xmlPath = os.path.join(root, direct, direct + ".xml")
                    if os.path.exists(xmlPath):
                        self.detmodels[
                            direct] = "detectors/" + direct + "/" + direct + ".xml"
                        copyFolder(os.path.join(root, direct), targetFolder)

    def createTarBall(self, folder):
        """create a tar ball from the folder
      tar zcf $TARBALLNAME $LIBFOLDER/*
    """
        ##Create the Tarball
        if os.path.exists(self.tarBallName):
            os.remove(self.tarBallName)
        gLogger.notice("Creating Tarball...")
        myappTar = tarfile.open(self.tarBallName, "w:gz")
        myappTar.add(folder, self.tarBallName[:-4])
        myappTar.close()

        self.md5sum = md5.md5(open(self.tarBallName, 'r').read()).hexdigest()

        gLogger.notice("...Done")
        return S_OK("Created Tarball")

    def parseArgs(self):
        """ parse the command line arguments"""
        if len(sys.argv) != 3:
            raise RuntimeError("Wrong number of arguments in call: '%s'" %
                               " ".join(sys.argv))
        self.name = sys.argv[1]
        self.version = sys.argv[2]
        self.tarBallName = "%s%s.tgz" % (self.name, self.version)

    def checkEnvironment(self):
        """ check if dd4hep and lcgeo are in the environment """
        for var in [self.ddhep_env, self.lcgeo_env, 'ROOTSYS']:
            if var not in os.environ:
                raise RuntimeError("%s is not set" % var)
        return os.environ[self.ddhep_env], os.environ[
            self.lcgeo_env], os.environ['ROOTSYS']

    def createCSEntry(self):
        """add the entries for this version into the Configuration System

    .. code::

      <version>
              {
                TarBall = ddsim<version>.tgz
                AdditionalEnvVar
                {
                  ROOTSYS = /cvmfs/ilc.desy.de/sw/x86_64_gcc44_sl6/root/5.34.30
                  G4INSTALL = /cvmfs/ilc.desy.de/sw/x86_64_gcc44_sl6/geant4/10.01
                  G4DATA = /cvmfs/ilc.desy.de/sw/x86_64_gcc44_sl6/geant4/10.01/share/Geant4-10.1.0/data
                }
                Overwrite = True
              }
      Operations/DDSimDetectorModels/<Version>
                {
                  CLIC_o2_v03 = detectors/CLIC_o2_v03/CLIC_o2_v03.xml
                  ...
                }

    """
        from ILCDIRAC.Core.Utilities.CheckAndGetProdProxy import checkOrGetGroupProxy
        from ILCDIRAC.ILCTransformationSystem.Utilities.ReleaseHelper import insertCSSection
        #FIXME: Get root and geant4 location from environment, make sure it is cvmfs
        csParameter = {
            "TarBall": self.tarBallName,
            "AdditionalEnvVar": {
                "ROOTSYS": os.path.realpath(os.environ.get("ROOTSYS")),
                "G4INSTALL": os.path.realpath(os.environ.get("G4INSTALL")),
            },
            "Md5Sum": self.md5sum,
        }

        g4datavariables = [
            "G4RADIOACTIVEDATA",
            "G4NEUTRONHPDATA",
            "G4LEDATA",
            "G4LEVELGAMMADATA",
            "G4RADIOACTIVEDATA",
            "G4NEUTRONXSDATA",
            "G4PIIDATA",
            "G4REALSURFACEDATA",
            "G4SAIDXSDATA",
            "G4ABLADATA",
            "G4ENSDFSTATEDATA",
        ]
        for g4data in g4datavariables:
            if os.environ.get(g4data):
                csParameter["AdditionalEnvVar"][g4data] = os.path.realpath(
                    os.environ.get(g4data))

        pars = dict(platform=self.platform, name="ddsim", version=self.version)

        csPath = os.path.join(self.softSec,
                              "%(platform)s/%(name)s/%(version)s/" % pars)
        pprint(csParameter)
        result = insertCSSection(self.csapi, csPath, csParameter)

        csPathModels = "Operations/Defaults/DDSimDetectorModels"
        csModels = {self.version: self.detmodels}
        pprint(csModels)

        result = insertCSSection(self.csapi, csPathModels, csModels)

        if self.csapi is not None:
            resProxy = checkOrGetGroupProxy("diracAdmin")
            if not resProxy['OK']:
                gLogger.error("Failed to get AdminProxy", resProxy['Message'])
                raise RuntimeError("Failed to get diracAdminProxy")
            self.csapi.commit()

        if not result['OK']:
            gLogger.error("Failed to create CS Section", result['Message'])
            raise RuntimeError("Failed to create CS Section")

    def createDDSimTarBall(self):
        """ do everything to create the DDSim tarball"""

        from ILCDIRAC.ILCTransformationSystem.Utilities.ReleaseHelper import copyFolder, getLibraryPath, getFiles, getDependentLibraries, copyLibraries, getPythonStuff, killRPath, resolveLinks, removeSystemLibraries

        self.parseArgs()
        ddBase, lcgeoBase, _rootsys = self.checkEnvironment()

        realTargetFolder = os.path.join(os.getcwd(), self.name + self.version)
        targetFolder = os.path.join(os.getcwd(), "temp",
                                    self.name + self.version)
        for folder in (targetFolder, targetFolder + "/lib"):
            try:
                os.makedirs(folder)
            except OSError:
                pass

        libraries = set()
        rootmaps = set()

        dd4hepLibPath = getLibraryPath(ddBase)
        lcgeoPath = getLibraryPath(lcgeoBase)

        ## FIXME: Automatically pick up folders with /compact/ in them
        self.copyDetectorModels(lcgeoBase, "CLIC", targetFolder + "/detectors")
        self.copyDetectorModels(lcgeoBase, "ILD", targetFolder + "/detectors")
        self.copyDetectorModels(lcgeoBase, "SiD", targetFolder + "/detectors")

        copyFolder(ddBase + "/DDDetectors/compact",
                   realTargetFolder.rstrip("/") + "/DDDetectors")

        copyFolder(ddBase + "/include", realTargetFolder.rstrip("/") + "")

        libraries.update(getFiles(dd4hepLibPath, ".so"))
        libraries.update(getFiles(lcgeoPath, ".so"))

        rootmaps.update(getFiles(dd4hepLibPath, ".rootmap"))
        rootmaps.update(getFiles(lcgeoPath, ".rootmap"))

        rootmaps.update(getFiles(dd4hepLibPath, ".pcm"))
        rootmaps.update(getFiles(lcgeoPath, ".pcm"))

        rootmaps.update(getFiles(dd4hepLibPath, ".components"))
        rootmaps.update(getFiles(lcgeoPath, ".components"))

        pprint(libraries)
        pprint(rootmaps)

        allLibs = set()
        for lib in libraries:
            allLibs.update(getDependentLibraries(lib))
        ### remote root and geant4 libraries, we pick them up from
        allLibs = set([
            lib for lib in allLibs
            if not ("/geant4/" in lib.lower() or "/root/" in lib.lower())
        ])

        print allLibs

        copyLibraries(libraries, targetFolder + "/lib")
        copyLibraries(allLibs, targetFolder + "/lib")
        copyLibraries(rootmaps, targetFolder + "/lib")

        getPythonStuff(ddBase + "/python", targetFolder + "/lib/")
        getPythonStuff(lcgeoBase + "/lib/python", targetFolder + "/lib/")
        getPythonStuff(lcgeoBase + "/bin/ddsim", targetFolder + "/bin/")

        ##Should get this from CVMFS
        #getRootStuff( rootsys, targetFolder+"/ROOT" )

        copyFolder(targetFolder + "/", realTargetFolder.rstrip("/"))

        killRPath(realTargetFolder)
        resolveLinks(realTargetFolder + "/lib")
        removeSystemLibraries(realTargetFolder + "/lib")
        #removeSystemLibraries( realTargetFolder+"/ROOT/lib" )
        print self.detmodels

        self.createTarBall(realTargetFolder)

        self.createCSEntry()
예제 #19
0
def main():
    # Registering arguments will automatically add their description to the help menu
    Script.registerArgument(
        "DIRACSiteName: Name of the site for DIRAC in the form GRID.LOCATION.COUNTRY "
        "(ie: LCG.CERN.ch)")
    Script.registerArgument(
        "GridSiteName:  Name of the site in the Grid (ie: CERN-PROD)")
    Script.registerArgument([
        "CE:           Name of the CE to be included in the site (ie: ce111.cern.ch)"
    ])
    Script.parseCommandLine(ignoreErrors=True)

    diracSiteName, gridSiteName, ces = Script.getPositionalArgs(group=True)

    try:
        diracGridType, place, country = diracSiteName.split(".")
    except ValueError:
        gLogger.error(
            "The DIRACSiteName should be of the form GRID.LOCATION.COUNTRY for example LCG.CERN.ch"
        )
        DIRACExit(-1)

    result = getDIRACSiteName(gridSiteName)
    newSite = True
    if result["OK"] and result["Value"]:
        if len(result["Value"]) > 1:
            gLogger.notice(
                "%s GOC site name is associated with several DIRAC sites:" %
                gridSiteName)
            for i, dSite in enumerate(result["Value"]):
                gLogger.notice("%d: %s" % (i, dSite))
            inp = six.moves.input("Enter your choice number: ")
            try:
                inp = int(inp)
            except ValueError:
                gLogger.error("You should enter an integer number")
                DIRACExit(-1)
            if 0 <= inp < len(result["Value"]):
                diracCSSite = result["Value"][inp]
            else:
                gLogger.error("Number out of range: %d" % inp)
                DIRACExit(-1)
        else:
            diracCSSite = result["Value"][0]
        if diracCSSite == diracSiteName:
            gLogger.notice("Site with GOC name %s is already defined as %s" %
                           (gridSiteName, diracSiteName))
            newSite = False
        else:
            gLogger.error(
                "ERROR: Site with GOC name %s is already defined as %s" %
                (gridSiteName, diracCSSite))
            DIRACExit(-1)
    else:
        gLogger.error("ERROR getting DIRAC site name of %s" % gridSiteName,
                      result.get("Message"))

    csAPI = CSAPI()

    if newSite:
        gLogger.notice("Site to CS: %s" % diracSiteName)
        res = csAPI.addSite(diracSiteName, {"Name": gridSiteName})
        if not res["OK"]:
            gLogger.error("Failed adding site to CS", res["Message"])
            DIRACExit(1)
        res = csAPI.commit()
        if not res["OK"]:
            gLogger.error("Failure committing to CS", res["Message"])
            DIRACExit(3)

    for ce in ces:
        gLogger.notice("Adding CE %s" % ce)
        res = csAPI.addCEtoSite(diracSiteName, ce)
        if not res["OK"]:
            gLogger.error("Failed adding CE %s to CS" % ce, res["Message"])
            DIRACExit(2)
        res = csAPI.commit()
        if not res["OK"]:
            gLogger.error("Failure committing to CS", res["Message"])
            DIRACExit(3)
예제 #20
0
class ComponentSupervisionAgent(AgentModule):
    """ComponentSupervisionAgent class."""
    def __init__(self, *args, **kwargs):
        """Initialize the agent, clients, default values."""
        AgentModule.__init__(self, *args, **kwargs)
        self.name = "ComponentSupervisionAgent"
        self.setup = "DIRAC-Production"
        self.enabled = False
        self.restartAgents = False
        self.restartExecutors = False
        self.restartServices = False
        self.controlComponents = False
        self.commitURLs = False
        self.doNotRestartInstancePattern = ["RequestExecutingAgent"]
        self.diracLocation = rootPath

        self.sysAdminClient = SystemAdministratorClient(socket.getfqdn())
        self.jobMonClient = JobMonitoringClient()
        self.nClient = NotificationClient()
        self.csAPI = None
        self.agents = dict()
        self.executors = dict()
        self.services = dict()
        self._tornadoPort = "8443"
        self.errors = list()
        self.accounting = defaultdict(dict)

        self.addressTo = []
        self.addressFrom = ""
        self.emailSubject = "ComponentSupervisionAgent on %s" % socket.getfqdn(
        )

    def logError(self, errStr, varMsg=""):
        """Append errors to a list, which is sent in email notification."""
        self.log.error(errStr, varMsg)
        self.errors.append(errStr + " " + varMsg)

    def beginExecution(self):
        """Reload the configurations before every cycle."""
        self.setup = self.am_getOption("Setup", self.setup)
        self.enabled = self.am_getOption("EnableFlag", self.enabled)
        self.restartAgents = self.am_getOption("RestartAgents",
                                               self.restartAgents)
        self.restartExecutors = self.am_getOption("RestartExecutors",
                                                  self.restartExecutors)
        self.restartServices = self.am_getOption("RestartServices",
                                                 self.restartServices)
        self.addressTo = self.am_getOption("MailTo", self.addressTo)
        self.addressFrom = self.am_getOption("MailFrom", self.addressFrom)
        self.controlComponents = self.am_getOption("ControlComponents",
                                                   self.controlComponents)
        self.commitURLs = self.am_getOption("CommitURLs", self.commitURLs)
        self.doNotRestartInstancePattern = self.am_getOption(
            "DoNotRestartInstancePattern", self.doNotRestartInstancePattern)

        self.csAPI = CSAPI()

        res = self.getRunningInstances(instanceType="Agents")
        if not res["OK"]:
            return S_ERROR("Failure to get running agents")
        self.agents = res["Value"]

        res = self.getRunningInstances(instanceType="Executors")
        if not res["OK"]:
            return S_ERROR("Failure to get running executors")
        self.executors = res["Value"]

        res = self.getRunningInstances(instanceType="Services")
        if not res["OK"]:
            return S_ERROR("Failure to get running services")
        self.services = res["Value"]

        self.accounting.clear()
        return S_OK()

    def sendNotification(self):
        """Send email notification about changes done in the last cycle."""
        if not (self.errors or self.accounting):
            return S_OK()

        emailBody = ""
        rows = []
        for instanceName, val in self.accounting.items():
            rows.append([[instanceName],
                         [val.get("Treatment", "No Treatment")],
                         [str(val.get("LogAge", "Not Relevant"))]])

        if rows:
            columns = ["Instance", "Treatment", "Log File Age (Minutes)"]
            emailBody += printTable(columns,
                                    rows,
                                    printOut=False,
                                    numbering=False,
                                    columnSeparator=" | ")

        if self.errors:
            emailBody += "\n\nErrors:"
            emailBody += "\n".join(self.errors)

        self.log.notice("Sending Email:\n" + emailBody)
        for address in self.addressTo:
            res = self.nClient.sendMail(address,
                                        self.emailSubject,
                                        emailBody,
                                        self.addressFrom,
                                        localAttempt=False)
            if not res["OK"]:
                self.log.error("Failure to send Email notification to ",
                               address)
                continue

        self.errors = []
        self.accounting.clear()

        return S_OK()

    def getRunningInstances(self, instanceType="Agents", runitStatus="Run"):
        """Return a dict of running agents, executors or services.

        Key is component's name, value contains dict with PollingTime, PID, Port, Module, RunitStatus, LogFileLocation

        :param str instanceType: 'Agents', 'Executors', 'Services'
        :param str runitStatus: Return only those instances with given RunitStatus or 'All'
        :returns: Dictionary of running instances
        """
        res = self.sysAdminClient.getOverallStatus()
        if not res["OK"]:
            self.logError(
                "Failure to get %s from system administrator client" %
                instanceType, res["Message"])
            return res

        val = res["Value"][instanceType]
        runningComponents = defaultdict(dict)
        for system, components in val.items():
            for componentName, componentInfo in components.items():
                if componentInfo["Setup"] and componentInfo["Installed"]:
                    if runitStatus != "All" and componentInfo[
                            "RunitStatus"] != runitStatus:
                        continue
                    for option, default in (("PollingTime", HOUR),
                                            ("Port", None), ("Protocol",
                                                             None)):
                        runningComponents[componentName][
                            option] = self._getComponentOption(
                                instanceType, system, componentName, option,
                                default)
                        # remove empty values so we can use defaults in _getURL
                        if not runningComponents[componentName][option]:
                            runningComponents[componentName].pop(option)
                    runningComponents[componentName][
                        "LogFileLocation"] = os.path.join(
                            self.diracLocation, "runit", system, componentName,
                            "log", "current")
                    runningComponents[componentName]["PID"] = componentInfo[
                        "PID"]
                    runningComponents[componentName]["Module"] = componentInfo[
                        "Module"]
                    runningComponents[componentName][
                        "RunitStatus"] = componentInfo["RunitStatus"]
                    runningComponents[componentName]["System"] = system

        return S_OK(runningComponents)

    def _getComponentOption(self, instanceType, system, componentName, option,
                            default):
        """Get component option from DIRAC CS, using components' base classes methods."""
        componentPath = PathFinder.getComponentSection(
            system=system,
            component=componentName,
            setup=self.setup,
            componentCategory=instanceType,
        )
        if instanceType != "Agents":
            return gConfig.getValue(Path.cfgPath(componentPath, option),
                                    default)
        # deal with agent configuration
        componentLoadModule = gConfig.getValue(
            Path.cfgPath(componentPath, "Module"), componentName)
        fullComponentName = Path.cfgPath(system, componentName)
        fullComponentLoadName = Path.cfgPath(system, componentLoadModule)
        return AgentModule(fullComponentName,
                           fullComponentLoadName).am_getOption(
                               option, default)

    def on_terminate(self, componentName, process):
        """Execute callback when a process terminates gracefully."""
        self.log.info(
            "%s's process with ID: %s has been terminated successfully" %
            (componentName, process.pid))

    def execute(self):
        """Execute checks for agents, executors, services."""
        for instanceType in ("executor", "agent", "service"):
            for name, options in getattr(self, instanceType + "s").items():
                # call checkAgent, checkExecutor, checkService
                res = getattr(self,
                              "check" + instanceType.capitalize())(name,
                                                                   options)
                if not res["OK"]:
                    self.logError("Failure when checking %s" % instanceType,
                                  "%s, %s" % (name, res["Message"]))

        res = self.componentControl()
        if not res["OK"]:
            if "Stopped does not exist" not in res[
                    "Message"] and "Running does not exist" not in res[
                        "Message"]:
                self.logError("Failure to control components", res["Message"])

        if not self.errors:
            res = self.checkURLs()
            if not res["OK"]:
                self.logError("Failure to check URLs", res["Message"])
        else:
            self.logError(
                "Something was wrong before, not checking URLs this time")

        self.sendNotification()

        if self.errors:
            return S_ERROR("Error during this cycle, check log")

        return S_OK()

    @staticmethod
    def getLastAccessTime(logFileLocation):
        """Return the age of log file."""
        lastAccessTime = 0
        try:
            lastAccessTime = os.path.getmtime(logFileLocation)
            lastAccessTime = datetime.fromtimestamp(lastAccessTime)
        except OSError as e:
            return S_ERROR("Failed to access logfile %s: %r" %
                           (logFileLocation, e))

        now = datetime.now()
        age = now - lastAccessTime
        return S_OK(age)

    def restartInstance(self, pid, instanceName, enabled):
        """Kill a process which is then restarted automatically."""
        if not (self.enabled and enabled):
            self.log.info(
                "Restarting is disabled, please restart %s manually" %
                instanceName)
            self.accounting[instanceName][
                "Treatment"] = "Please restart it manually"
            return S_OK(NO_RESTART)

        if any(pattern in instanceName
               for pattern in self.doNotRestartInstancePattern):
            self.log.info(
                "Restarting for %s is disabled, please restart it manually" %
                instanceName)
            self.accounting[instanceName][
                "Treatment"] = "Please restart it manually"
            return S_OK(NO_RESTART)

        try:
            componentProc = psutil.Process(int(pid))
            processesToTerminate = componentProc.children(recursive=True)
            processesToTerminate.append(componentProc)

            for proc in processesToTerminate:
                proc.terminate()

            _gone, alive = psutil.wait_procs(processesToTerminate,
                                             timeout=5,
                                             callback=partial(
                                                 self.on_terminate,
                                                 instanceName))
            for proc in alive:
                self.log.info("Forcefully killing process %s" % proc.pid)
                proc.kill()

            return S_OK()

        except psutil.Error as err:
            self.logError("Exception occurred in terminating processes",
                          "%s" % err)
            return S_ERROR()

    def checkService(self, serviceName, options):
        """Ping the service, restart if the ping does not respond."""
        url = self._getURL(serviceName, options)
        self.log.info("Pinging service", url)
        pingRes = Client().ping(url=url)
        if not pingRes["OK"]:
            self.log.info("Failure pinging service: %s: %s" %
                          (url, pingRes["Message"]))
            res = self.restartInstance(int(options["PID"]), serviceName,
                                       self.restartServices)
            if not res["OK"]:
                return res
            if res["Value"] != NO_RESTART:
                self.accounting[serviceName][
                    "Treatment"] = "Successfully Restarted"
                self.log.info("Service %s has been successfully restarted" %
                              serviceName)
        self.log.info("Service responded OK")
        return S_OK()

    def checkAgent(self, agentName, options):
        """Check the age of agent's log file, if it is too old then restart the agent."""
        pollingTime, currentLogLocation, pid = (options["PollingTime"],
                                                options["LogFileLocation"],
                                                options["PID"])
        self.log.info("Checking Agent: %s" % agentName)
        self.log.info("Polling Time: %s" % pollingTime)
        self.log.info("Current Log File location: %s" % currentLogLocation)

        res = self.getLastAccessTime(currentLogLocation)
        if not res["OK"]:
            return res

        age = res["Value"]
        self.log.info("Current log file for %s is %d minutes old" %
                      (agentName, (age.seconds / MINUTES)))

        maxLogAge = max(pollingTime + HOUR, 2 * HOUR)
        if age.seconds < maxLogAge:
            return S_OK()

        self.log.info("Current log file is too old for Agent %s" % agentName)
        self.accounting[agentName]["LogAge"] = age.seconds / MINUTES

        res = self.restartInstance(int(pid), agentName, self.restartAgents)
        if not res["OK"]:
            return res
        if res["Value"] != NO_RESTART:
            self.accounting[agentName]["Treatment"] = "Successfully Restarted"
            self.log.info("Agent %s has been successfully restarted" %
                          agentName)

        return S_OK()

    def checkExecutor(self, executor, options):
        """Check the age of executor log file, if too old check for jobs in checking status, then restart the executors."""
        currentLogLocation = options["LogFileLocation"]
        pid = options["PID"]
        self.log.info("Checking executor: %s" % executor)
        self.log.info("Current Log File location: %s" % currentLogLocation)

        res = self.getLastAccessTime(currentLogLocation)
        if not res["OK"]:
            return res

        age = res["Value"]
        self.log.info("Current log file for %s is %d minutes old" %
                      (executor, (age.seconds / MINUTES)))

        if age.seconds < 2 * HOUR:
            return S_OK()

        self.log.info("Current log file is too old for Executor %s" % executor)
        self.accounting[executor]["LogAge"] = age.seconds / MINUTES

        res = self.checkForCheckingJobs(executor)
        if not res["OK"]:
            return res
        if res["OK"] and res["Value"] == NO_CHECKING_JOBS:
            self.accounting.pop(executor, None)
            return S_OK(NO_RESTART)

        res = self.restartInstance(int(pid), executor, self.restartExecutors)
        if not res["OK"]:
            return res
        elif res["OK"] and res["Value"] != NO_RESTART:
            self.accounting[executor]["Treatment"] = "Successfully Restarted"
            self.log.info("Executor %s has been successfully restarted" %
                          executor)

        return S_OK()

    def checkForCheckingJobs(self, executorName):
        """Check if there are checking jobs with the **executorName** as current MinorStatus."""
        attrDict = {"Status": "Checking", "MinorStatus": executorName}

        # returns list of jobs IDs
        resJobs = self.jobMonClient.getJobs(attrDict)
        if not resJobs["OK"]:
            self.logError("Could not get jobs for this executor",
                          "%s: %s" % (executorName, resJobs["Message"]))
            return resJobs
        if resJobs["Value"]:
            self.log.info('Found %d jobs in "Checking" status for %s' %
                          (len(resJobs["Value"]), executorName))
            return S_OK(CHECKING_JOBS)
        self.log.info('Found no jobs in "Checking" status for %s' %
                      executorName)
        return S_OK(NO_CHECKING_JOBS)

    def componentControl(self):
        """Monitor and control component status as defined in the CS.

        Check for running and stopped components and ensure they have the proper status as defined in the CS
        Registry/Hosts/_HOST_/[Running|Stopped] sections

        :returns: :func:`~DIRAC:DIRAC.Core.Utilities.ReturnValues.S_OK`,
           :func:`~DIRAC:DIRAC.Core.Utilities.ReturnValues.S_ERROR`
        """
        # get the current status of the components

        resCurrent = self._getCurrentComponentStatus()
        if not resCurrent["OK"]:
            return resCurrent
        currentStatus = resCurrent["Value"]

        resDefault = self._getDefaultComponentStatus()
        if not resDefault["OK"]:
            return resDefault
        defaultStatus = resDefault["Value"]

        # ensure instances are in the right state
        shouldBe = {}
        shouldBe["Run"] = defaultStatus["Run"].intersection(
            currentStatus["Down"])
        shouldBe["Down"] = defaultStatus["Down"].intersection(
            currentStatus["Run"])
        shouldBe["Unknown"] = defaultStatus["All"].symmetric_difference(
            currentStatus["All"])

        self._ensureComponentRunning(shouldBe["Run"])
        self._ensureComponentDown(shouldBe["Down"])

        for instance in shouldBe["Unknown"]:
            self.logError("Unknown instance",
                          "%r, either uninstall or add to config" % instance)

        return S_OK()

    def _getCurrentComponentStatus(self):
        """Get current status for components."""
        resOverall = self.sysAdminClient.getOverallStatus()
        if not resOverall["OK"]:
            return resOverall
        currentStatus = {"Down": set(), "Run": set(), "All": set()}
        informationDict = resOverall["Value"]
        for systemsDict in informationDict.values():
            for system, instancesDict in systemsDict.items():
                for instanceName, instanceInfoDict in instancesDict.items():
                    identifier = "%s__%s" % (system, instanceName)
                    runitStatus = instanceInfoDict.get("RunitStatus")
                    if runitStatus in ("Run", "Down"):
                        currentStatus[runitStatus].add(identifier)

        currentStatus["All"] = currentStatus["Run"] | currentStatus["Down"]
        return S_OK(currentStatus)

    def _getDefaultComponentStatus(self):
        """Get the configured status of the components."""
        host = socket.getfqdn()
        defaultStatus = {"Down": set(), "Run": set(), "All": set()}
        resRunning = gConfig.getOptionsDict(
            Path.cfgPath("/Registry/Hosts/", host, "Running"))
        resStopped = gConfig.getOptionsDict(
            Path.cfgPath("/Registry/Hosts/", host, "Stopped"))
        if not resRunning["OK"]:
            return resRunning
        if not resStopped["OK"]:
            return resStopped
        defaultStatus["Run"] = set(resRunning["Value"])
        defaultStatus["Down"] = set(resStopped["Value"])
        defaultStatus["All"] = defaultStatus["Run"] | defaultStatus["Down"]

        if defaultStatus["Run"].intersection(defaultStatus["Down"]):
            self.logError(
                "Overlap in configuration",
                str(defaultStatus["Run"].intersection(defaultStatus["Down"])))
            return S_ERROR("Bad host configuration")

        return S_OK(defaultStatus)

    def _ensureComponentRunning(self, shouldBeRunning):
        """Ensure the correct components are running."""
        for instance in shouldBeRunning:
            self.log.info("Starting instance %s" % instance)
            system, name = instance.split("__")
            if self.controlComponents:
                res = self.sysAdminClient.startComponent(system, name)
                if not res["OK"]:
                    self.logError("Failed to start component:",
                                  "%s: %s" % (instance, res["Message"]))
                else:
                    self.accounting[instance][
                        "Treatment"] = "Instance was down, started instance"
            else:
                self.accounting[instance][
                    "Treatment"] = "Instance is down, should be started"

    def _ensureComponentDown(self, shouldBeDown):
        """Ensure the correct components are not running."""
        for instance in shouldBeDown:
            self.log.info("Stopping instance %s" % instance)
            system, name = instance.split("__")
            if self.controlComponents:
                res = self.sysAdminClient.stopComponent(system, name)
                if not res["OK"]:
                    self.logError("Failed to stop component:",
                                  "%s: %s" % (instance, res["Message"]))
                else:
                    self.accounting[instance][
                        "Treatment"] = "Instance was running, stopped instance"
            else:
                self.accounting[instance][
                    "Treatment"] = "Instance is running, should be stopped"

    def checkURLs(self):
        """Ensure that the running services have their URL in the Config."""
        self.log.info("Checking URLs")
        # get services again, in case they were started/stop in controlComponents
        gConfig.forceRefresh(fromMaster=True)

        # get port used for https based services
        try:
            tornadoSystemInstance = PathFinder.getSystemInstance(
                system="Tornado",
                setup=self.setup,
            )
            self._tornadoPort = gConfig.getValue(
                Path.cfgPath("/System/Tornado/", tornadoSystemInstance,
                             "Port"),
                self._tornadoPort,
            )
        except RuntimeError:
            pass

        self.log.debug("Using Tornado Port:", self._tornadoPort)

        res = self.getRunningInstances(instanceType="Services",
                                       runitStatus="All")
        if not res["OK"]:
            return S_ERROR("Failure to get running services")
        self.services = res["Value"]
        for service, options in sorted(self.services.items()):
            self.log.debug("Checking URL for %s with options %s" %
                           (service, options))
            # ignore SystemAdministrator, does not have URLs
            if "SystemAdministrator" in service:
                continue
            self._checkServiceURL(service, options)

        if self.csAPI.csModified and self.commitURLs:
            self.log.info("Commiting changes to the CS")
            result = self.csAPI.commit()
            if not result["OK"]:
                self.logError("Commit to CS failed", result["Message"])
                return S_ERROR("Failed to commit to CS")
        return S_OK()

    def _checkServiceURL(self, serviceName, options):
        """Ensure service URL is properly configured in the CS."""
        url = self._getURL(serviceName, options)
        system = options["System"]
        module = options["Module"]
        self.log.info("Checking URLs for %s/%s" % (system, module))
        urlsConfigPath = Path.cfgPath(
            PathFinder.getSystemURLSection(system=system, setup=self.setup),
            module)
        urls = gConfig.getValue(urlsConfigPath, [])
        self.log.debug("Found configured URLs for %s: %s" % (module, urls))
        self.log.debug("This URL is %s" % url)
        runitStatus = options["RunitStatus"]
        wouldHave = "Would have " if not self.commitURLs else ""
        if runitStatus == "Run" and url not in urls:
            urls.append(url)
            message = "%sAdded URL %s to URLs for %s/%s" % (wouldHave, url,
                                                            system, module)
            self.log.info(message)
            self.accounting[serviceName + "/URL"]["Treatment"] = message
            self.csAPI.modifyValue(urlsConfigPath, ",".join(urls))
        if runitStatus == "Down" and url in urls:
            urls.remove(url)
            message = "%sRemoved URL %s from URLs for %s/%s" % (wouldHave, url,
                                                                system, module)
            self.log.info(message)
            self.accounting[serviceName + "/URL"]["Treatment"] = message
            self.csAPI.modifyValue(urlsConfigPath, ",".join(urls))

    def _getURL(self, serviceName, options):
        """Return URL for the service."""
        system = options["System"]
        port = options.get("Port", self._tornadoPort)
        host = socket.getfqdn()
        protocol = options.get("Protocol", "dips")
        url = "%s://%s:%s/%s/%s" % (protocol, host, port, system, serviceName)
        return url
예제 #21
0
class Bdii2CSAgent(AgentModule):

  def __init__(self, *args, **kwargs):
    """ Defines default parameters
    """

    super(Bdii2CSAgent, self).__init__(*args, **kwargs)

    self.addressTo = ''
    self.addressFrom = ''
    self.voName = []
    self.subject = "Bdii2CSAgent"
    self.alternativeBDIIs = []
    self.voBdiiCEDict = {}
    self.voBdiiSEDict = {}
    self.host = 'lcg-bdii.cern.ch:2170'
    self.glue2URLs = []
    self.glue2Only = False

    self.csAPI = None

    # What to get
    self.processCEs = True
    self.processSEs = False
    self.selectedSites = []

    # Update the CS or not?
    self.dryRun = False

  def initialize(self):
    """ Gets run paramaters from the configuration
    """

    self.addressTo = self.am_getOption('MailTo', self.addressTo)
    self.addressFrom = self.am_getOption('MailFrom', self.addressFrom)
    # Create a list of alternative bdii urls
    self.alternativeBDIIs = self.am_getOption('AlternativeBDIIs', self.alternativeBDIIs)
    self.host = self.am_getOption('Host', self.host)
    self.glue2URLs = self.am_getOption('GLUE2URLs', self.glue2URLs)
    self.glue2Only = self.am_getOption('GLUE2Only', self.glue2Only)

    # Check if the bdii url is appended by a port number, if not append the default 2170
    for index, url in enumerate(self.alternativeBDIIs):
      if not url.split(':')[-1].isdigit():
        self.alternativeBDIIs[index] += ':2170'
    if self.addressTo and self.addressFrom:
      self.log.info("MailTo", self.addressTo)
      self.log.info("MailFrom", self.addressFrom)
    if self.alternativeBDIIs:
      self.log.info("AlternativeBDII URLs:", self.alternativeBDIIs)

    self.processCEs = self.am_getOption('ProcessCEs', self.processCEs)
    self.processSEs = self.am_getOption('ProcessSEs', self.processSEs)
    self.selectedSites = self.am_getOption('SelectedSites', [])
    self.dryRun = self.am_getOption('DryRun', self.dryRun)

    self.voName = self.am_getOption('VirtualOrganization', self.voName)
    if not self.voName:
      self.voName = self.am_getOption('VO', [])
    if not self.voName or (len(self.voName) == 1 and self.voName[0].lower() == 'all'):
      # Get all VOs defined in the configuration
      self.voName = []
      result = getVOs()
      if result['OK']:
        vos = result['Value']
        for vo in vos:
          vomsVO = getVOOption(vo, "VOMSName")
          if vomsVO:
            self.voName.append(vomsVO)

    if self.voName:
      self.log.info("Agent will manage VO(s) %s" % self.voName)
    else:
      self.log.fatal("VirtualOrganization option not defined for agent")
      return S_ERROR()

    self.csAPI = CSAPI()
    return self.csAPI.initialize()

  def execute(self):
    """ General agent execution method
    """
    self.voBdiiCEDict = {}
    self.voBdiiSEDict = {}

    # Get a "fresh" copy of the CS data
    result = self.csAPI.downloadCSData()
    if not result['OK']:
      self.log.warn("Could not download a fresh copy of the CS data", result['Message'])

    # Refresh the configuration from the master server
    gConfig.forceRefresh(fromMaster=True)

    if self.processCEs:
      self.__lookForNewCEs()
      self.__updateCEs()
    if self.processSEs:
      self.__lookForNewSEs()
      self.__updateSEs()
    return S_OK()

  def __lookForNewCEs(self):
    """ Look up BDII for CEs not yet present in the DIRAC CS
    """

    bannedCEs = self.am_getOption('BannedCEs', [])
    result = getCEsFromCS()
    if not result['OK']:
      return result
    knownCEs = set(result['Value'])
    knownCEs = knownCEs.union(set(bannedCEs))

    for vo in self.voName:
      result = self.__getBdiiCEInfo(vo)
      if not result['OK']:
        continue
      bdiiInfo = result['Value']
      result = getGridCEs(vo, bdiiInfo=bdiiInfo, ceBlackList=knownCEs)
      if not result['OK']:
        self.log.error('Failed to get unused CEs', result['Message'])
      siteDict = result['Value']
      body = ''
      for site in siteDict:
        newCEs = set(siteDict[site].keys())  # pylint: disable=no-member
        if not newCEs:
          continue

        ceString = ''
        for ce in newCEs:
          queueString = ''
          ceInfo = bdiiInfo[site]['CEs'][ce]
          newCEString = "CE: %s, GOCDB Site Name: %s" % (ce, site)
          systemTuple = siteDict[site][ce]['System']
          osString = "%s_%s_%s" % (systemTuple)
          newCEString = "\n%s\n%s\n" % (newCEString, osString)
          for queue in ceInfo['Queues']:
            queueStatus = ceInfo['Queues'][queue].get('GlueCEStateStatus', 'UnknownStatus')
            if 'production' in queueStatus.lower():
              ceType = ceInfo['Queues'][queue].get('GlueCEImplementationName', '')
              queueString += "   %s %s %s\n" % (queue, queueStatus, ceType)
          if queueString:
            ceString += newCEString
            ceString += "Queues:\n"
            ceString += queueString

        if ceString:
          body += ceString

      if body:
        body = "\nWe are glad to inform You about new CE(s) possibly suitable for %s:\n" % vo + body
        body += "\n\nTo suppress information about CE add its name to BannedCEs list.\n"
        body += "Add new Sites/CEs for vo %s with the command:\n" % vo
        body += "dirac-admin-add-resources --vo %s --ce\n" % vo
        self.log.info(body)
        if self.addressTo and self.addressFrom:
          notification = NotificationClient()
          result = notification.sendMail(self.addressTo, self.subject, body, self.addressFrom,
                                         localAttempt=False, avoidSpam=True)
          if not result['OK']:
            self.log.error('Can not send new site notification mail', result['Message'])

    return S_OK()

  def __getBdiiCEInfo(self, vo):

    if vo in self.voBdiiCEDict:
      return S_OK(self.voBdiiCEDict[vo])
    self.log.info("Check for available CEs for VO", vo)
    totalResult = S_OK({})
    message = ''

    mainResult = getBdiiCEInfo(vo, host=self.host, glue2=self.glue2Only)
    if not mainResult['OK']:
      self.log.error("Failed getting information from default bdii", mainResult['Message'])
      message = mainResult['Message']

    for bdii in reversed(self.alternativeBDIIs):
      resultAlt = getBdiiCEInfo(vo, host=bdii, glue2=self.glue2Only)
      if resultAlt['OK']:
        totalResult['Value'].update(resultAlt['Value'])
      else:
        self.log.error("Failed getting information from %s " % bdii, resultAlt['Message'])
        message = (message + "\n" + resultAlt['Message']).strip()

    for glue2URL in self.glue2URLs:
      if self.glue2Only:
        break
      resultGlue2 = getBdiiCEInfo(vo, host=glue2URL, glue2=True)
      if resultGlue2['OK']:
        totalResult['Value'].update(resultGlue2['Value'])
      else:
        self.log.error("Failed getting GLUE2 information for", "%s, %s: %s" %
                       (glue2URL, vo, resultGlue2['Message']))
        message = (message + "\n" + resultGlue2['Message']).strip()

    if mainResult['OK']:
      totalResult['Value'].update(mainResult['Value'])

    if not totalResult['Value'] and message:  # Dict is empty and we have an error message
      self.log.error("Error during BDII request", message)
      totalResult = S_ERROR(message)
    else:
      self.voBdiiCEDict[vo] = totalResult['Value']
    return totalResult

  def __getBdiiSEInfo(self, vo):

    if vo in self.voBdiiSEDict:
      return S_OK(self.voBdiiSEDict[vo])
    self.log.info("Check for available SEs for VO", vo)
    result = getBdiiSEInfo(vo)
    message = ''
    if not result['OK']:
      message = result['Message']
      for bdii in self.alternativeBDIIs:
        result = getBdiiSEInfo(vo, host=bdii)
        if result['OK']:
          break
    if not result['OK']:
      if message:
        self.log.error("Error during BDII request", message)
      else:
        self.log.error("Error during BDII request", result['Message'])
    else:
      self.voBdiiSEDict[vo] = result['Value']
    return result

  def __updateCEs(self):
    """ Update the Site/CE/queue settings in the CS if they were changed in the BDII
    """

    bdiiChangeSet = set()

    for vo in self.voName:
      result = self.__getBdiiCEInfo(vo)
      if not result['OK']:
        continue
      ceBdiiDict = result['Value']
      self.__purgeSites(ceBdiiDict)
      result = getSiteUpdates(vo, bdiiInfo=ceBdiiDict, log=self.log)
      if not result['OK']:
        continue
      bdiiChangeSet = bdiiChangeSet.union(result['Value'])

    # We have collected all the changes, consolidate VO settings
    result = self.__updateCS(bdiiChangeSet)
    return result

  def __purgeSites(self, ceBdiiDict):
    """Remove all sites that are not in self.selectedSites.

    Modifies the ceBdiiDict!
    """
    if not self.selectedSites:
      return
    for site in list(ceBdiiDict):
      ces = list(ceBdiiDict[site]['CEs'])
      if not ces:
        self.log.error("No CE information for site:", site)
        continue
      diracSiteName = getSiteForCE(ces[0])
      if not diracSiteName['OK']:
        self.log.error("Failed to get DIRAC site name for ce", "%s: %s" % (ces[0], diracSiteName['Message']))
        continue
      self.log.debug("Checking site %s (%s), aka %s" % (site, ces, diracSiteName['Value']))
      if diracSiteName['Value'] in self.selectedSites:
        continue
      self.log.info("Dropping site %s, aka %s" % (site, diracSiteName))
      ceBdiiDict.pop(site)
    return

  def __updateCS(self, bdiiChangeSet):

    queueVODict = {}
    changeSet = set()
    for entry in bdiiChangeSet:
      section, option, _value, new_value = entry
      if option == "VO":
        queueVODict.setdefault(section, set())
        queueVODict[section] = queueVODict[section].union(set(new_value.split(',')))
      else:
        changeSet.add(entry)
    for section, VOs in queueVODict.items():
      changeSet.add((section, 'VO', '', ','.join(VOs)))

    if changeSet:
      changeList = sorted(changeSet)
      body = '\n'.join(["%s/%s %s -> %s" % entry for entry in changeList])
      if body and self.addressTo and self.addressFrom:
        notification = NotificationClient()
        result = notification.sendMail(self.addressTo, self.subject, body, self.addressFrom, localAttempt=False)

      if body:
        self.log.info('The following configuration changes were detected:')
        self.log.info(body)

      for section, option, value, new_value in changeSet:
        if value == 'Unknown' or not value:
          self.csAPI.setOption(cfgPath(section, option), new_value)
        else:
          self.csAPI.modifyValue(cfgPath(section, option), new_value)

      if self.dryRun:
        self.log.info("Dry Run: CS won't be updated")
        self.csAPI.showDiff()
      else:
        result = self.csAPI.commit()
        if not result['OK']:
          self.log.error("Error while committing to CS", result['Message'])
        else:
          self.log.info("Successfully committed %d changes to CS" % len(changeList))
        return result
    else:
      self.log.info("No changes found")
      return S_OK()

  def __lookForNewSEs(self):
    """ Look up BDII for SEs not yet present in the DIRAC CS
    """

    bannedSEs = self.am_getOption('BannedSEs', [])
    result = getSEsFromCS()
    if not result['OK']:
      return result
    knownSEs = set(result['Value'])
    knownSEs = knownSEs.union(set(bannedSEs))

    for vo in self.voName:
      result = self.__getBdiiSEInfo(vo)
      if not result['OK']:
        continue
      bdiiInfo = result['Value']
      result = getGridSRMs(vo, bdiiInfo=bdiiInfo, srmBlackList=knownSEs)
      if not result['OK']:
        continue
      siteDict = result['Value']
      body = ''
      for site in siteDict:
        newSEs = set(siteDict[site].keys())  # pylint: disable=no-member
        if not newSEs:
          continue
        for se in newSEs:
          body += '\n New SE %s available at site %s:\n' % (se, site)
          backend = siteDict[site][se]['SE'].get('GlueSEImplementationName', 'Unknown')
          size = siteDict[site][se]['SE'].get('GlueSESizeTotal', 'Unknown')
          body += '  Backend %s, Size %s' % (backend, size)

      if body:
        body = "\nWe are glad to inform You about new SE(s) possibly suitable for %s:\n" % vo + body
        body += "\n\nTo suppress information about an SE add its name to BannedSEs list.\n"
        body += "Add new SEs for vo %s with the command:\n" % vo
        body += "dirac-admin-add-resources --vo %s --se\n" % vo
        self.log.info(body)
        if self.addressTo and self.addressFrom:
          notification = NotificationClient()
          result = notification.sendMail(self.addressTo, self.subject, body, self.addressFrom, localAttempt=False)
          if not result['OK']:
            self.log.error('Can not send new site notification mail', result['Message'])

    return S_OK()

  def __updateSEs(self):
    """ Update the Storage Element settings in the CS if they were changed in the BDII
    """

    bdiiChangeSet = set()

    for vo in self.voName:
      result = self.__getBdiiSEInfo(vo)
      if not result['OK']:
        continue
      seBdiiDict = result['Value']
      result = getSRMUpdates(vo, bdiiInfo=seBdiiDict)
      if not result['OK']:
        continue
      bdiiChangeSet = bdiiChangeSet.union(result['Value'])

    # We have collected all the changes, consolidate VO settings
    result = self.__updateCS(bdiiChangeSet)
    return result
예제 #22
0
for i in resultList:
    if not cfg.isSection("Resources/Sites/%s" % i):
        gLogger.error("Subsection /Resources/Sites/%s does not exists" % i)
        continue
    hasRun = True
    if SORTBYNAME:
        dirty = cfg["Resources"]["Sites"][i].sortAlphabetically(ascending=not REVERSE)
    else:
        dirty = cfg["Resources"]["Sites"][i].sortByKey(key=country, reverse=REVERSE)
    if dirty:
        isDirty = True

if not hasRun:
    gLogger.notice("Failed to find suitable subsections with site names to sort")
    DIRAC.exit(0)

if not isDirty:
    gLogger.notice("Nothing to do, site names are already sorted")
    DIRAC.exit(0)

timestamp = toString(dateTime())
stamp = "Site names are sorted by %s script at %s" % (Script.scriptName, timestamp)
cs.setOptionComment("/Resources/Sites", stamp)

result = cs.commit()
if not result["OK"]:
    gLogger.error("Failed to commit changes to CS", result["Message"])
    DIRAC.exit(2)
gLogger.notice("Site names are sorted and committed to CS")
DIRAC.exit(0)
예제 #23
0
def checkUnusedSEs():
  
  global vo, dry
  
  result = getGridSRMs( vo, unUsed = True )
  if not result['OK']:
    gLogger.error( 'Failed to look up SRMs in BDII', result['Message'] )
  siteSRMDict = result['Value']

  # Evaluate VOs
  result = getVOs()
  if result['OK']:
    csVOs = set( result['Value'] )
  else:
    csVOs = set( [vo] ) 

  changeSetFull = set()

  for site in siteSRMDict:
    for gridSE in siteSRMDict[site]:
      changeSet = set()
      seDict = siteSRMDict[site][gridSE]['SE']
      srmDict = siteSRMDict[site][gridSE]['SRM']
      # Check the SRM version
      version = srmDict.get( 'GlueServiceVersion', '' )
      if not ( version and version.startswith( '2' ) ):
        gLogger.debug( 'Skipping SRM service with version %s' % version )
        continue 
      result = getDIRACSiteName( site )
      if not result['OK']:
        gLogger.notice( 'Unused se %s is detected at unused site %s' % ( gridSE, site ) )
        gLogger.notice( 'Consider adding site %s to the DIRAC CS' % site ) 
        continue
      diracSites = result['Value']
      yn = raw_input( '\nDo you want to add new SRM SE %s at site(s) %s ? default yes [yes|no]: ' % ( gridSE, str( diracSites ) ) )
      if not yn or yn.lower().startswith( 'y' ):
        if len( diracSites ) > 1:
          prompt = 'Which DIRAC site the new SE should be attached to ?'
          for i, s in enumerate( diracSites ):
            prompt += '\n[%d] %s' % ( i, s )
          prompt += '\nEnter your choice number: '    
          inp = raw_input( prompt )
          try:
            ind = int( inp )
          except:
            gLogger.notice( 'Can not interpret your choice: %s, try again later' % inp )
            continue
          diracSite = diracSites[ind]
        else:
          diracSite = diracSites[0]       
      
        domain, siteName, country = diracSite.split( '.' )
        recName = '%s-disk' % siteName
        inp = raw_input( 'Give a DIRAC name to the grid SE %s, default %s : ' % ( gridSE, recName ) )
        diracSEName = inp
        if not inp:
          diracSEName = recName  
          
        gLogger.notice( 'Adding new SE %s at site %s' % ( diracSEName, diracSite ) )        
        seSection = cfgPath( '/Resources/StorageElements', diracSEName )
        changeSet.add( ( seSection, 'BackendType', seDict.get( 'GlueSEImplementationName', 'Unknown' ) ) )
        changeSet.add( ( seSection, 'Description', seDict.get( 'GlueSEName', 'Unknown' ) ) )  
        bdiiVOs = set( [ re.sub( '^VO:', '', rule ) for rule in srmDict.get( 'GlueServiceAccessControlBaseRule', [] ) ] )
        seVOs = csVOs.intersection( bdiiVOs )  
        changeSet.add( ( seSection, 'VO', ','.join( seVOs ) ) )
        accessSection = cfgPath( seSection, 'AccessProtocol.1' )
        changeSet.add( ( accessSection, 'Protocol', 'srm' ) )
        changeSet.add( ( accessSection, 'ProtocolName', 'SRM2' ) )
        endPoint = srmDict.get( 'GlueServiceEndpoint', '' )
        result = pfnparse( endPoint )
        if not result['OK']:
          gLogger.error( 'Can not get the SRM service end point. Skipping ...' )
          continue
        host = result['Value']['Host']
        port = result['Value']['Port']
        changeSet.add( ( accessSection, 'Host', host ) ) 
        changeSet.add( ( accessSection, 'Port', port ) ) 
        changeSet.add( ( accessSection, 'Access', 'remote' ) )
        voPathSection = cfgPath( accessSection, 'VOPath' )
        if 'VOPath' in seDict:
          path = seDict['VOPath']
          voFromPath = os.path.basename( path )
          if voFromPath != diracVO:
            gLogger.notice( '\n!!! Warning: non-conventional VO path: %s\n' % path )
            changeSet.add( ( voPathSection, diracVO, path ) )
          path = os.path.dirname( path )  
        else:  
          # Try to guess the Path
          domain = '.'.join( host.split( '.' )[-2:] )
          path = '/dpm/%s/home' % domain
        changeSet.add( ( accessSection, 'Path', path ) ) 
        changeSet.add( ( accessSection, 'SpaceToken', '' ) )
        changeSet.add( ( accessSection, 'WSUrl', '/srm/managerv2?SFN=' ) ) 
        
        gLogger.notice( 'SE %s will be added with the following parameters' % diracSEName )
        changeList = list( changeSet )
        changeList.sort()
        for entry in changeList:
          gLogger.notice( entry )
        yn = raw_input( 'Do you want to add new SE %s ? default yes [yes|no]: ' % diracSEName )   
        if not yn or yn.lower().startswith( 'y' ):
          changeSetFull = changeSetFull.union( changeSet )        
          
  if dry:
    if changeSetFull:
      gLogger.notice( 'Skipping commit of the new SE data in a dry run' )
    else:
      gLogger.notice( "No new SE to be added" )
    return S_OK()

  if changeSetFull:
    csAPI = CSAPI()
    csAPI.initialize()
    result = csAPI.downloadCSData()
    if not result['OK']:
      gLogger.error( 'Failed to initialize CSAPI object', result['Message'] )
      DIRACExit( -1 )
    changeList = list( changeSetFull )
    changeList.sort()
    for section, option, value in changeList:
      csAPI.setOption( cfgPath( section, option ), value )

    yn = raw_input( 'New SE data is accumulated\n Do you want to commit changes to CS ? default yes [yes|no]: ' )
    if not yn or yn.lower().startswith( 'y' ):
      result = csAPI.commit()
      if not result['OK']:
        gLogger.error( "Error while commit to CS", result['Message'] )
      else:
        gLogger.notice( "Successfully committed %d changes to CS" % len( changeSetFull ) )
  else:
    gLogger.notice( "No new SE to be added" )

  return S_OK()
class DDSimTarMaker( object ):
  """ create a tarball of the DDSim release """
  def __init__( self ):
    from DIRAC.ConfigurationSystem.Client.CSAPI import CSAPI

    self.detmodels = {}
    self.lcgeo_env="lcgeo_DIR"
    self.ddhep_env="DD4HEP"
    self.softSec = "/Operations/Defaults/AvailableTarBalls"
    self.version = ''
    self.platform = 'x86_64-slc5-gcc43-opt'
    self.comment = ""
    self.name = "ddsim"
    self.csapi = CSAPI()
    self.tarBallName = None
    self.md5sum = None

  def copyDetectorModels( self, basePath, folder, targetFolder ):
    """copy the compact folders to the targetFolder """
    from ILCDIRAC.ILCTransformationSystem.Utilities.ReleaseHelper import copyFolder
    for root,dirs,_files in os.walk( os.path.join(basePath, folder) ):
      for direct in dirs:
        if root.endswith("compact"):
          ## the main xml file must have the same name as the folder, ILD and CLIC follow this convention already
          xmlPath = os.path.join( root, direct, direct+".xml")
          if os.path.exists( xmlPath ):
            self.detmodels[direct] = "detectors/"+direct+"/"+direct+".xml"
            copyFolder( os.path.join(root, direct), targetFolder )

  def createTarBall( self, folder ):
    """create a tar ball from the folder
      tar zcf $TARBALLNAME $LIBFOLDER/*
    """
    ##Create the Tarball
    if os.path.exists(self.tarBallName):
      os.remove(self.tarBallName)
    gLogger.notice("Creating Tarball...")
    myappTar = tarfile.open(self.tarBallName, "w:gz")
    myappTar.add(folder, self.tarBallName[:-4])
    myappTar.close()

    self.md5sum = md5.md5(open( self.tarBallName, 'r' ).read()).hexdigest()

    gLogger.notice("...Done")
    return S_OK( "Created Tarball")

  def parseArgs( self ):
    """ parse the command line arguments"""
    if len(sys.argv) != 3:
      raise RuntimeError( "Wrong number of arguments in call: '%s'" % " ".join(sys.argv) )
    self.name = sys.argv[1]
    self.version = sys.argv[2]
    self.tarBallName = "%s%s.tgz" % (self.name, self.version)


  def checkEnvironment( self ):
    """ check if dd4hep and lcgeo are in the environment """
    for var in [ self.ddhep_env, self.lcgeo_env , 'ROOTSYS' ]:
      if var not in os.environ:
        raise RuntimeError( "%s is not set" % var )
    return os.environ[self.ddhep_env], os.environ[self.lcgeo_env], os.environ['ROOTSYS']

  def createCSEntry( self ):
    """add the entries for this version into the Configuration System

    .. code::

      <version>
              {
                TarBall = ddsim<version>.tgz
                AdditionalEnvVar
                {
                  ROOTSYS = /cvmfs/ilc.desy.de/sw/x86_64_gcc44_sl6/root/5.34.30
                  G4INSTALL = /cvmfs/ilc.desy.de/sw/x86_64_gcc44_sl6/geant4/10.01
                  G4DATA = /cvmfs/ilc.desy.de/sw/x86_64_gcc44_sl6/geant4/10.01/share/Geant4-10.1.0/data
                }
                Overwrite = True
              }
      Operations/DDSimDetectorModels/<Version>
                {
                  CLIC_o2_v03 = detectors/CLIC_o2_v03/CLIC_o2_v03.xml
                  ...
                }

    """
    from ILCDIRAC.Core.Utilities.CheckAndGetProdProxy import checkOrGetGroupProxy
    from ILCDIRAC.ILCTransformationSystem.Utilities.ReleaseHelper import insertCSSection
    #FIXME: Get root and geant4 location from environment, make sure it is cvmfs
    csParameter = { "TarBall": self.tarBallName,
                    "AdditionalEnvVar": {
                      "ROOTSYS" :   os.path.realpath( os.environ.get("ROOTSYS") ),
                      "G4INSTALL" : os.path.realpath( os.environ.get("G4INSTALL") ),
                    },
                    "Md5Sum": self.md5sum,
                  }

    g4datavariables = [ "G4RADIOACTIVEDATA",
                        "G4NEUTRONHPDATA",
                        "G4LEDATA",
                        "G4LEVELGAMMADATA",
                        "G4RADIOACTIVEDATA",
                        "G4NEUTRONXSDATA",
                        "G4PIIDATA",
                        "G4REALSURFACEDATA",
                        "G4SAIDXSDATA",
                        "G4ABLADATA",
                        "G4ENSDFSTATEDATA",
                      ]
    for g4data in  g4datavariables:
      if os.environ.get(g4data) :
        csParameter["AdditionalEnvVar"][g4data] = os.path.realpath( os.environ.get(g4data) )

    pars = dict( platform=self.platform,
                 name="ddsim",
                 version=self.version
               )

    csPath = os.path.join( self.softSec , "%(platform)s/%(name)s/%(version)s/" % pars )
    pprint(csParameter)
    result = insertCSSection( self.csapi, csPath, csParameter )

    csPathModels = "Operations/Defaults/DDSimDetectorModels"
    csModels = { self.version : self.detmodels }
    pprint(csModels)

    result = insertCSSection( self.csapi, csPathModels, csModels )

    if self.csapi is not None:
      resProxy = checkOrGetGroupProxy( "diracAdmin" )
      if not resProxy['OK']:
        gLogger.error( "Failed to get AdminProxy", resProxy['Message'] )
        raise RuntimeError( "Failed to get diracAdminProxy" )
      self.csapi.commit()

    if not result['OK']:
      gLogger.error( "Failed to create CS Section", result['Message'] )
      raise RuntimeError( "Failed to create CS Section" )

  def createDDSimTarBall( self ):
    """ do everything to create the DDSim tarball"""

    from ILCDIRAC.ILCTransformationSystem.Utilities.ReleaseHelper import copyFolder, getLibraryPath, getFiles, getDependentLibraries, copyLibraries, getPythonStuff, killRPath, resolveLinks, removeSystemLibraries

    self.parseArgs()
    ddBase, lcgeoBase, _rootsys = self.checkEnvironment()

    realTargetFolder = os.path.join( os.getcwd(), self.name+self.version )
    targetFolder = os.path.join( os.getcwd(), "temp", self.name+self.version )
    for folder in (targetFolder, targetFolder+"/lib"):
      try:
        os.makedirs( folder )
      except OSError:
        pass

    libraries = set()
    rootmaps = set()

    dd4hepLibPath = getLibraryPath( ddBase )
    lcgeoPath = getLibraryPath( lcgeoBase )

    ## FIXME: Automatically pick up folders with /compact/ in them
    self.copyDetectorModels( lcgeoBase, "CLIC" , targetFolder+"/detectors" )
    self.copyDetectorModels( lcgeoBase, "ILD"  , targetFolder+"/detectors" )
    self.copyDetectorModels( lcgeoBase, "SiD"  , targetFolder+"/detectors" )

    copyFolder( ddBase+"/DDDetectors/compact", realTargetFolder.rstrip("/")+"/DDDetectors")

    copyFolder( ddBase+"/include", realTargetFolder.rstrip("/")+"")

    libraries.update( getFiles( dd4hepLibPath, ".so") )
    libraries.update( getFiles( lcgeoPath, ".so" ) )

    rootmaps.update( getFiles( dd4hepLibPath, ".rootmap") )
    rootmaps.update( getFiles( lcgeoPath, ".rootmap" ) )

    rootmaps.update( getFiles( dd4hepLibPath, ".pcm") )
    rootmaps.update( getFiles( lcgeoPath, ".pcm" ) )

    rootmaps.update( getFiles( dd4hepLibPath, ".components") )
    rootmaps.update( getFiles( lcgeoPath, ".components" ) )


    pprint( libraries )
    pprint( rootmaps )


    allLibs = set()
    for lib in libraries:
      allLibs.update( getDependentLibraries(lib) )
    ### remote root and geant4 libraries, we pick them up from
    allLibs = set( [ lib for lib in allLibs if not ( "/geant4/" in lib.lower() or "/root/" in lib.lower()) ] )

    print(allLibs)

    copyLibraries( libraries, targetFolder+"/lib" )
    copyLibraries( allLibs, targetFolder+"/lib" )
    copyLibraries( rootmaps, targetFolder+"/lib" )

    getPythonStuff( ddBase+"/python"       , targetFolder+"/lib/")
    getPythonStuff( lcgeoBase+"/lib/python", targetFolder+"/lib/" )
    getPythonStuff( lcgeoBase+"/bin/ddsim", targetFolder+"/bin/" )


    ##Should get this from CVMFS
    #getRootStuff( rootsys, targetFolder+"/ROOT" )

    copyFolder( targetFolder+"/", realTargetFolder.rstrip("/") )

    killRPath( realTargetFolder )
    resolveLinks( realTargetFolder+"/lib" )
    removeSystemLibraries( realTargetFolder+"/lib" )
    #removeSystemLibraries( realTargetFolder+"/ROOT/lib" )
    print(self.detmodels)


    self.createTarBall( realTargetFolder )

    self.createCSEntry()
예제 #25
0
def main():
    Script.registerSwitch(
        "C", "country",
        "Sort site names by country postfix (i.e. LCG.IHEP.cn, LCG.IN2P3.fr, LCG.IHEP.su)",
        sortBy)
    Script.registerSwitch("R", "reverse", "Reverse the sort order", isReverse)
    # Registering arguments will automatically add their description to the help menu
    Script.registerArgument([
        "Section: Name of the subsection in '/Resources/Sites/' for sort (i.e. LCG DIRAC)"
    ],
                            mandatory=False)

    Script.parseCommandLine(ignoreErrors=True)
    args = Script.getPositionalArgs()

    result = getProxyInfo()
    if not result["OK"]:
        gLogger.error("Failed to get proxy information", result["Message"])
        DIRACExit(2)
    proxy = result["Value"]
    if proxy["secondsLeft"] < 1:
        gLogger.error("Your proxy has expired, please create new one")
        DIRACExit(2)
    group = proxy["group"]
    if "CSAdministrator" not in getPropertiesForGroup(group):
        gLogger.error(
            "You must be CSAdministrator user to execute this script")
        gLogger.notice(
            "Please issue 'dirac-proxy-init -g [group with CSAdministrator Property]'"
        )
        DIRACExit(2)

    cs = CSAPI()
    result = cs.getCurrentCFG()
    if not result["OK"]:
        gLogger.error("Failed to get copy of CS", result["Message"])
        DIRACExit(2)
    cfg = result["Value"]

    if not cfg.isSection("Resources"):
        gLogger.error("Section '/Resources' is absent in CS")
        DIRACExit(2)

    if not cfg.isSection("Resources/Sites"):
        gLogger.error("Subsection '/Resources/Sites' is absent in CS")
        DIRACExit(2)

    if args and len(args) > 0:
        resultList = args[:]
    else:
        resultList = cfg["Resources"]["Sites"].listSections()

    hasRun = False
    isDirty = False
    for i in resultList:
        if not cfg.isSection("Resources/Sites/%s" % i):
            gLogger.error("Subsection /Resources/Sites/%s does not exists" % i)
            continue
        hasRun = True
        if SORTBYNAME:
            dirty = cfg["Resources"]["Sites"][i].sortAlphabetically(
                ascending=not REVERSE)
        else:
            dirty = cfg["Resources"]["Sites"][i].sortByKey(key=country,
                                                           reverse=REVERSE)
        if dirty:
            isDirty = True

    if not hasRun:
        gLogger.notice(
            "Failed to find suitable subsections with site names to sort")
        DIRACExit(0)

    if not isDirty:
        gLogger.notice("Nothing to do, site names are already sorted")
        DIRACExit(0)

    timestamp = toString(dateTime())
    stamp = "Site names are sorted by %s script at %s" % (Script.scriptName,
                                                          timestamp)
    cs.setOptionComment("/Resources/Sites", stamp)

    result = cs.commit()
    if not result["OK"]:
        gLogger.error("Failed to commit changes to CS", result["Message"])
        DIRACExit(2)
    gLogger.notice("Site names are sorted and committed to CS")
    DIRACExit(0)
예제 #26
0
class MonitorAgents(AgentModule):
  """MonitorAgents class."""

  def __init__(self, *args, **kwargs):
    """Initialize the agent, clients, default values."""
    AgentModule.__init__(self, *args, **kwargs)
    self.name = 'MonitorAgents'
    self.setup = "Production"
    self.enabled = False
    self.restartAgents = False
    self.restartExecutors = False
    self.restartServices = False
    self.controlComponents = False
    self.commitURLs = False
    self.diracLocation = "/opt/dirac/pro"

    self.sysAdminClient = SystemAdministratorClient(socket.gethostname())
    self.jobMonClient = JobMonitoringClient()
    self.nClient = NotificationClient()
    self.csAPI = None
    self.agents = dict()
    self.executors = dict()
    self.services = dict()
    self.errors = list()
    self.accounting = defaultdict(dict)

    self.addressTo = ["*****@*****.**"]
    self.addressFrom = "*****@*****.**"
    self.emailSubject = "MonitorAgents on %s" % socket.gethostname()

  def logError(self, errStr, varMsg=''):
    """Append errors to a list, which is sent in email notification."""
    self.log.error(errStr, varMsg)
    self.errors.append(errStr + " " + varMsg)

  def beginExecution(self):
    """Reload the configurations before every cycle."""
    self.setup = self.am_getOption("Setup", self.setup)
    self.enabled = self.am_getOption("EnableFlag", self.enabled)
    self.restartAgents = self.am_getOption("RestartAgents", self.restartAgents)
    self.restartExecutors = self.am_getOption("RestartExecutors", self.restartExecutors)
    self.restartServices = self.am_getOption("RestartServices", self.restartServices)
    self.diracLocation = os.environ.get("DIRAC", self.diracLocation)
    self.addressTo = self.am_getOption('MailTo', self.addressTo)
    self.addressFrom = self.am_getOption('MailFrom', self.addressFrom)
    self.controlComponents = self.am_getOption('ControlComponents', self.controlComponents)
    self.commitURLs = self.am_getOption('CommitURLs', self.commitURLs)

    self.csAPI = CSAPI()

    res = self.getRunningInstances(instanceType='Agents')
    if not res["OK"]:
      return S_ERROR("Failure to get running agents")
    self.agents = res["Value"]

    res = self.getRunningInstances(instanceType='Executors')
    if not res["OK"]:
      return S_ERROR("Failure to get running executors")
    self.executors = res["Value"]

    res = self.getRunningInstances(instanceType='Services')
    if not res["OK"]:
      return S_ERROR("Failure to get running services")
    self.services = res["Value"]

    self.accounting.clear()
    return S_OK()

  def sendNotification(self):
    """Send email notification about changes done in the last cycle."""
    if not(self.errors or self.accounting):
      return S_OK()

    emailBody = ""
    rows = []
    for instanceName, val in self.accounting.iteritems():
      rows.append([[instanceName],
                   [val.get('Treatment', 'No Treatment')],
                   [str(val.get('LogAge', 'Not Relevant'))]])

    if rows:
      columns = ["Instance", "Treatment", "Log File Age (Minutes)"]
      emailBody += printTable(columns, rows, printOut=False, numbering=False, columnSeparator=' | ')

    if self.errors:
      emailBody += "\n\nErrors:"
      emailBody += "\n".join(self.errors)

    self.log.notice("Sending Email:\n" + emailBody)
    for address in self.addressTo:
      res = self.nClient.sendMail(address, self.emailSubject, emailBody, self.addressFrom, localAttempt=False)
      if not res['OK']:
        self.log.error("Failure to send Email notification to ", address)
        continue

    self.errors = []
    self.accounting.clear()

    return S_OK()

  def getRunningInstances(self, instanceType='Agents', runitStatus='Run'):
    """Return a dict of running agents, executors or services.

    Key is agent's name, value contains dict with PollingTime, PID, Port, Module, RunitStatus, LogFileLocation

    :param str instanceType: 'Agents', 'Executors', 'Services'
    :param str runitStatus: Return only those instances with given RunitStatus or 'All'
    :returns: Dictionary of running instances
    """
    res = self.sysAdminClient.getOverallStatus()
    if not res["OK"]:
      self.logError("Failure to get %s from system administrator client" % instanceType, res["Message"])
      return res

    val = res['Value'][instanceType]
    runningAgents = defaultdict(dict)
    for system, agents in val.iteritems():
      for agentName, agentInfo in agents.iteritems():
        if agentInfo['Setup'] and agentInfo['Installed']:
          if runitStatus != 'All' and agentInfo['RunitStatus'] != runitStatus:
            continue
          confPath = cfgPath('/Systems/' + system + '/' + self.setup + '/%s/' % instanceType + agentName)
          for option, default in (('PollingTime', HOUR), ('Port', None)):
            optPath = os.path.join(confPath, option)
            runningAgents[agentName][option] = gConfig.getValue(optPath, default)
          runningAgents[agentName]["LogFileLocation"] = \
              os.path.join(self.diracLocation, 'runit', system, agentName, 'log', 'current')
          runningAgents[agentName]["PID"] = agentInfo["PID"]
          runningAgents[agentName]['Module'] = agentInfo['Module']
          runningAgents[agentName]['RunitStatus'] = agentInfo['RunitStatus']
          runningAgents[agentName]['System'] = system

    return S_OK(runningAgents)

  def on_terminate(self, agentName, process):
    """Execute callback when a process terminates gracefully."""
    self.log.info("%s's process with ID: %s has been terminated successfully" % (agentName, process.pid))

  def execute(self):
    """Execute checks for agents, executors, services."""
    for instanceType in ('executor', 'agent', 'service'):
      for name, options in getattr(self, instanceType + 's').iteritems():
        # call checkAgent, checkExecutor, checkService
        res = getattr(self, 'check' + instanceType.capitalize())(name, options)
        if not res['OK']:
          self.logError("Failure when checking %s" % instanceType, "%s, %s" % (name, res['Message']))

    res = self.componentControl()
    if not res['OK']:
      if "Stopped does not exist" not in res['Message'] and \
         "Running does not exist" not in res['Message']:
        self.logError("Failure to control components", res['Message'])

    if not self.errors:
      res = self.checkURLs()
      if not res['OK']:
        self.logError("Failure to check URLs", res['Message'])
    else:
      self.logError('Something was wrong before, not checking URLs this time')

    self.sendNotification()

    if self.errors:
      return S_ERROR("Error during this cycle, check log")

    return S_OK()

  @staticmethod
  def getLastAccessTime(logFileLocation):
    """Return the age of log file."""
    lastAccessTime = 0
    try:
      lastAccessTime = os.path.getmtime(logFileLocation)
      lastAccessTime = datetime.fromtimestamp(lastAccessTime)
    except OSError as e:
      return S_ERROR('Failed to access logfile %s: %r' % (logFileLocation, e))

    now = datetime.now()
    age = now - lastAccessTime
    return S_OK(age)

  def restartInstance(self, pid, instanceName, enabled):
    """Kill a process which is then restarted automatically."""
    if not (self.enabled and enabled):
      self.log.info("Restarting is disabled, please restart %s manually" % instanceName)
      self.accounting[instanceName]["Treatment"] = "Please restart it manually"
      return S_OK(NO_RESTART)

    try:
      agentProc = psutil.Process(int(pid))
      processesToTerminate = agentProc.children(recursive=True)
      processesToTerminate.append(agentProc)

      for proc in processesToTerminate:
        proc.terminate()

      _gone, alive = psutil.wait_procs(processesToTerminate, timeout=5,
                                       callback=partial(self.on_terminate, instanceName))
      for proc in alive:
        self.log.info("Forcefully killing process %s" % proc.pid)
        proc.kill()

      return S_OK()

    except psutil.Error as err:
      self.logError("Exception occurred in terminating processes", "%s" % err)
      return S_ERROR()

  def checkService(self, serviceName, options):
    """Ping the service, restart if the ping does not respond."""
    url = self._getURL(serviceName, options)
    self.log.info("Pinging service", url)
    pingRes = Client().ping(url=url)
    if not pingRes['OK']:
      self.log.info('Failure pinging service: %s: %s' % (url, pingRes['Message']))
      res = self.restartInstance(int(options['PID']), serviceName, self.restartServices)
      if not res["OK"]:
        return res
      elif res['OK'] and res['Value'] != NO_RESTART:
        self.accounting[serviceName]["Treatment"] = "Successfully Restarted"
        self.log.info("Agent %s has been successfully restarted" % serviceName)
    self.log.info("Service responded OK")
    return S_OK()

  def checkAgent(self, agentName, options):
    """Check the age of agent's log file, if it is too old then restart the agent."""
    pollingTime, currentLogLocation, pid = options['PollingTime'], options['LogFileLocation'], options['PID']
    self.log.info("Checking Agent: %s" % agentName)
    self.log.info("Polling Time: %s" % pollingTime)
    self.log.info("Current Log File location: %s" % currentLogLocation)

    res = self.getLastAccessTime(currentLogLocation)
    if not res["OK"]:
      return res

    age = res["Value"]
    self.log.info("Current log file for %s is %d minutes old" % (agentName, (age.seconds / MINUTES)))

    maxLogAge = max(pollingTime + HOUR, 2 * HOUR)
    if age.seconds < maxLogAge:
      return S_OK()

    self.log.info("Current log file is too old for Agent %s" % agentName)
    self.accounting[agentName]["LogAge"] = age.seconds / MINUTES

    res = self.restartInstance(int(pid), agentName, self.restartAgents)
    if not res["OK"]:
      return res
    elif res['OK'] and res['Value'] != NO_RESTART:
      self.accounting[agentName]["Treatment"] = "Successfully Restarted"
      self.log.info("Agent %s has been successfully restarted" % agentName)

    return S_OK()

  def checkExecutor(self, executor, options):
    """Check the age of executor log file, if too old check for jobs in checking status, then restart the executors."""
    currentLogLocation = options['LogFileLocation']
    pid = options['PID']
    self.log.info("Checking executor: %s" % executor)
    self.log.info("Current Log File location: %s" % currentLogLocation)

    res = self.getLastAccessTime(currentLogLocation)
    if not res["OK"]:
      return res

    age = res["Value"]
    self.log.info("Current log file for %s is %d minutes old" % (executor, (age.seconds / MINUTES)))

    if age.seconds < 2 * HOUR:
      return S_OK()

    self.log.info("Current log file is too old for Executor %s" % executor)
    self.accounting[executor]["LogAge"] = age.seconds / MINUTES

    res = self.checkForCheckingJobs(executor)
    if not res['OK']:
      return res
    if res['OK'] and res['Value'] == NO_CHECKING_JOBS:
      self.accounting.pop(executor, None)
      return S_OK(NO_RESTART)

    res = self.restartInstance(int(pid), executor, self.restartExecutors)
    if not res["OK"]:
      return res
    elif res['OK'] and res['Value'] != NO_RESTART:
      self.accounting[executor]["Treatment"] = "Successfully Restarted"
      self.log.info("Executor %s has been successfully restarted" % executor)

    return S_OK()

  def checkForCheckingJobs(self, executorName):
    """Check if there are checking jobs with the **executorName** as current MinorStatus."""
    attrDict = {'Status': 'Checking', 'MinorStatus': executorName}

    # returns list of jobs IDs
    resJobs = self.jobMonClient.getJobs(attrDict)
    if not resJobs['OK']:
      self.logError("Could not get jobs for this executor", "%s: %s" % (executorName, resJobs['Message']))
      return resJobs
    if resJobs['Value']:
      self.log.info("Found %d jobs in 'Checking' status for %s" % (len(resJobs['Value']), executorName))
      return S_OK(CHECKING_JOBS)
    self.log.info("Found no jobs in 'Checking' status for %s" % executorName)
    return S_OK(NO_CHECKING_JOBS)

  def componentControl(self):
    """Monitor and control component status as defined in the CS.

    Check for running and stopped components and ensure they have the proper status as defined in the CS
    Registry/Hosts/_HOST_/[Running|Stopped] sections

    :returns: :func:`~DIRAC:DIRAC.Core.Utilities.ReturnValues.S_OK`,
       :func:`~DIRAC:DIRAC.Core.Utilities.ReturnValues.S_ERROR`
    """
    # get the current status of the components

    resCurrent = self._getCurrentComponentStatus()
    if not resCurrent['OK']:
      return resCurrent
    currentStatus = resCurrent['Value']

    resDefault = self._getDefaultComponentStatus()
    if not resDefault['OK']:
      return resDefault
    defaultStatus = resDefault['Value']

    # ensure instances are in the right state
    shouldBe = {}
    shouldBe['Run'] = defaultStatus['Run'].intersection(currentStatus['Down'])
    shouldBe['Down'] = defaultStatus['Down'].intersection(currentStatus['Run'])
    shouldBe['Unknown'] = defaultStatus['All'].symmetric_difference(currentStatus['All'])

    self._ensureComponentRunning(shouldBe['Run'])
    self._ensureComponentDown(shouldBe['Down'])

    for instance in shouldBe['Unknown']:
      self.logError("Unknown instance", "%r, either uninstall or add to config" % instance)

    return S_OK()

  def _getCurrentComponentStatus(self):
    """Get current status for components."""
    resOverall = self.sysAdminClient.getOverallStatus()
    if not resOverall['OK']:
      return resOverall
    currentStatus = {'Down': set(), 'Run': set(), 'All': set()}
    informationDict = resOverall['Value']
    for systemsDict in informationDict.values():
      for system, instancesDict in systemsDict.items():
        for instanceName, instanceInfoDict in instancesDict.items():
          identifier = '%s__%s' % (system, instanceName)
          runitStatus = instanceInfoDict.get('RunitStatus')
          if runitStatus in ('Run', 'Down'):
            currentStatus[runitStatus].add(identifier)

    currentStatus['All'] = currentStatus['Run'] | currentStatus['Down']
    return S_OK(currentStatus)

  def _getDefaultComponentStatus(self):
    """Get the configured status of the components."""
    host = socket.gethostname()
    defaultStatus = {'Down': set(), 'Run': set(), 'All': set()}
    resRunning = gConfig.getOptionsDict(os.path.join('/Registry/Hosts/', host, 'Running'))
    resStopped = gConfig.getOptionsDict(os.path.join('/Registry/Hosts/', host, 'Stopped'))
    if not resRunning['OK']:
      return resRunning
    if not resStopped['OK']:
      return resStopped
    defaultStatus['Run'] = set(resRunning['Value'].keys())
    defaultStatus['Down'] = set(resStopped['Value'].keys())
    defaultStatus['All'] = defaultStatus['Run'] | defaultStatus['Down']

    if defaultStatus['Run'].intersection(defaultStatus['Down']):
      self.logError("Overlap in configuration", str(defaultStatus['Run'].intersection(defaultStatus['Down'])))
      return S_ERROR("Bad host configuration")

    return S_OK(defaultStatus)

  def _ensureComponentRunning(self, shouldBeRunning):
    """Ensure the correct components are running."""
    for instance in shouldBeRunning:
      self.log.info("Starting instance %s" % instance)
      system, name = instance.split('__')
      if self.controlComponents:
        res = self.sysAdminClient.startComponent(system, name)
        if not res['OK']:
          self.logError("Failed to start component:", "%s: %s" % (instance, res['Message']))
        else:
          self.accounting[instance]["Treatment"] = "Instance was down, started instance"
      else:
        self.accounting[instance]["Treatment"] = "Instance is down, should be started"

  def _ensureComponentDown(self, shouldBeDown):
    """Ensure the correct components are not running."""
    for instance in shouldBeDown:
      self.log.info("Stopping instance %s" % instance)
      system, name = instance.split('__')
      if self.controlComponents:
        res = self.sysAdminClient.stopComponent(system, name)
        if not res['OK']:
          self.logError("Failed to stop component:", "%s: %s" % (instance, res['Message']))
        else:
          self.accounting[instance]["Treatment"] = "Instance was running, stopped instance"
      else:
        self.accounting[instance]["Treatment"] = "Instance is running, should be stopped"

  def checkURLs(self):
    """Ensure that the running services have their URL in the Config."""
    self.log.info("Checking URLs")
    # get services again, in case they were started/stop in controlComponents
    gConfig.forceRefresh(fromMaster=True)
    res = self.getRunningInstances(instanceType='Services', runitStatus='All')
    if not res["OK"]:
      return S_ERROR("Failure to get running services")
    self.services = res["Value"]
    for service, options in self.services.iteritems():
      self.log.debug("Checking URL for %s with options %s" % (service, options))
      # ignore SystemAdministrator, does not have URLs
      if 'SystemAdministrator' in service:
        continue
      self._checkServiceURL(service, options)

    if self.csAPI.csModified and self.commitURLs:
      self.log.info("Commiting changes to the CS")
      result = self.csAPI.commit()
      if not result['OK']:
        self.logError('Commit to CS failed', result['Message'])
        return S_ERROR("Failed to commit to CS")
    return S_OK()

  def _checkServiceURL(self, serviceName, options):
    """Ensure service URL is properly configured in the CS."""
    url = self._getURL(serviceName, options)
    system = options['System']
    module = options['Module']
    self.log.info("Checking URLs for %s/%s" % (system, module))
    urlsConfigPath = os.path.join('/Systems', system, self.setup, 'URLs', module)
    urls = gConfig.getValue(urlsConfigPath, [])
    self.log.debug("Found configured URLs for %s: %s" % (module, urls))
    self.log.debug("This URL is %s" % url)
    runitStatus = options['RunitStatus']
    wouldHave = 'Would have ' if not self.commitURLs else ''
    if runitStatus == 'Run' and url not in urls:
      urls.append(url)
      message = "%sAdded URL %s to URLs for %s/%s" % (wouldHave, url, system, module)
      self.log.info(message)
      self.accounting[serviceName + "/URL"]["Treatment"] = message
      self.csAPI.modifyValue(urlsConfigPath, ",".join(urls))
    if runitStatus == 'Down' and url in urls:
      urls.remove(url)
      message = "%sRemoved URL %s from URLs for %s/%s" % (wouldHave, url, system, module)
      self.log.info(message)
      self.accounting[serviceName + "/URL"]["Treatment"] = message
      self.csAPI.modifyValue(urlsConfigPath, ",".join(urls))

  @staticmethod
  def _getURL(serviceName, options):
    """Return URL for the service."""
    system = options['System']
    port = options['Port']
    host = socket.gethostname()
    url = 'dips://%s:%s/%s/%s' % (host, port, system, serviceName)
    return url
예제 #27
0
class CE2CSAgent(AgentModule):
    """ !!!Out-dated!!! Moved to Bdii2CSAgent
  """

    addressTo = ''
    addressFrom = ''
    voName = ''
    subject = "CE2CSAgent"
    alternativeBDIIs = []

    csAPI = None

    def initialize(self):

        # TODO: Have no default and if no mail is found then use the diracAdmin group
        # and resolve all associated mail addresses.
        self.addressTo = self.am_getOption('MailTo', self.addressTo)
        self.addressFrom = self.am_getOption('MailFrom', self.addressFrom)
        # Create a list of alternative bdii urls
        self.alternativeBDIIs = self.am_getOption('AlternativeBDIIs', [])
        # Check if the bdii url is appended by a port number, if not append the default 2170
        for index, url in enumerate(self.alternativeBDIIs):
            if not url.split(':')[-1].isdigit():
                self.alternativeBDIIs[index] += ':2170'
        if self.addressTo and self.addressFrom:
            self.log.info("MailTo", self.addressTo)
            self.log.info("MailFrom", self.addressFrom)
        if self.alternativeBDIIs:
            self.log.info("AlternativeBDII URLs:", self.alternativeBDIIs)
        self.subject = "CE2CSAgent"

        # This sets the Default Proxy to used as that defined under
        # /Operations/Shifter/TestManager
        # the shifterProxy option in the Configuration can be used to change this default.
        self.am_setOption('shifterProxy', 'TestManager')

        self.voName = self.am_getOption('VirtualOrganization', [])
        if not self.voName:
            vo = getVO()
            if vo:
                self.voName = [vo]

        if self.voName:
            self.log.info("Agent will manage VO(s) %s" % self.voName)
        else:
            self.log.fatal("VirtualOrganization option not defined for agent")
            return S_ERROR()

        self.csAPI = CSAPI()
        return self.csAPI.initialize()

    def execute(self):

        self.log.info("Start Execution")
        result = getProxyInfo()
        if not result['OK']:
            return result
        infoDict = result['Value']
        self.log.info(formatProxyInfoAsString(infoDict))

        # Get a "fresh" copy of the CS data
        result = self.csAPI.downloadCSData()
        if not result['OK']:
            self.log.warn("Could not download a fresh copy of the CS data",
                          result['Message'])

        self.__lookForCE()
        self.__infoFromCE()
        self.log.info("End Execution")
        return S_OK()

    def __checkAlternativeBDIISite(self, fun, *args):
        if self.alternativeBDIIs:
            self.log.warn("Trying to use alternative BDII sites")
            for site in self.alternativeBDIIs:
                self.log.info("Trying to contact alternative BDII", site)
                if len(args) == 1:
                    result = fun(args[0], host=site)
                elif len(args) == 2:
                    result = fun(args[0], vo=args[1], host=site)
                if not result['OK']:
                    self.log.error("Problem contacting alternative BDII",
                                   result['Message'])
                elif result['OK']:
                    return result
            self.log.warn("Also checking alternative BDII sites failed")
            return result

    def __lookForCE(self):

        knownCEs = self.am_getOption('BannedCEs', [])

        result = gConfig.getSections('/Resources/Sites')
        if not result['OK']:
            return
        grids = result['Value']

        for grid in grids:
            result = gConfig.getSections('/Resources/Sites/%s' % grid)
            if not result['OK']:
                return
            sites = result['Value']

            for site in sites:
                opt = gConfig.getOptionsDict('/Resources/Sites/%s/%s' %
                                             (grid, site))['Value']
                ces = List.fromChar(opt.get('CE', ''))
                knownCEs += ces

        response = ''
        for vo in self.voName:
            self.log.info("Check for available CEs for VO", vo)
            response = ldapCEState('', vo)
            if not response['OK']:
                self.log.error("Error during BDII request",
                               response['Message'])
                response = self.__checkAlternativeBDIISite(ldapCEState, '', vo)
                return response

            newCEs = {}
            for queue in response['Value']:
                try:
                    queueName = queue['GlueCEUniqueID']
                except:
                    continue

                ceName = queueName.split(":")[0]
                if not ceName in knownCEs:
                    newCEs[ceName] = None
                    self.log.debug("New CE", ceName)

            body = ""
            possibleNewSites = []
            for ce in newCEs.iterkeys():
                response = ldapCluster(ce)
                if not response['OK']:
                    self.log.warn("Error during BDII request",
                                  response['Message'])
                    response = self.__checkAlternativeBDIISite(ldapCluster, ce)
                    continue
                clusters = {}
                clusters = response['Value']
                if len(clusters) != 1:
                    self.log.warn("Error in cluster length",
                                  " CE %s Length %d" % (ce, len(clusters)))
                if len(clusters) == 0:
                    continue
                cluster = clusters[0]
                fkey = cluster.get('GlueForeignKey', [])
                if type(fkey) == type(''):
                    fkey = [fkey]
                nameBDII = None
                for entry in fkey:
                    if entry.count('GlueSiteUniqueID'):
                        nameBDII = entry.split('=')[1]
                        break
                if not nameBDII:
                    continue

                ceString = "CE: %s, GOCDB Name: %s" % (ce, nameBDII)
                self.log.info(ceString)

                response = ldapCE(ce)
                if not response['OK']:
                    self.log.warn("Error during BDII request",
                                  response['Message'])
                    response = self.__checkAlternativeBDIISite(ldapCE, ce)
                    continue

                ceInfos = response['Value']
                if len(ceInfos):
                    ceInfo = ceInfos[0]
                    systemName = ceInfo.get('GlueHostOperatingSystemName',
                                            'Unknown')
                    systemVersion = ceInfo.get(
                        'GlueHostOperatingSystemVersion', 'Unknown')
                    systemRelease = ceInfo.get(
                        'GlueHostOperatingSystemRelease', 'Unknown')
                else:
                    systemName = "Unknown"
                    systemVersion = "Unknown"
                    systemRelease = "Unknown"

                osString = "SystemName: %s, SystemVersion: %s, SystemRelease: %s" % (
                    systemName, systemVersion, systemRelease)
                self.log.info(osString)

                response = ldapCEState(ce, vo)
                if not response['OK']:
                    self.log.warn("Error during BDII request",
                                  response['Message'])
                    response = self.__checkAlternativeBDIISite(
                        ldapCEState, ce, vo)
                    continue

                newCEString = "\n\n%s\n%s" % (ceString, osString)
                usefull = False
                ceStates = response['Value']
                for ceState in ceStates:
                    queueName = ceState.get('GlueCEUniqueID', 'UnknownName')
                    queueStatus = ceState.get('GlueCEStateStatus',
                                              'UnknownStatus')

                    queueString = "%s %s" % (queueName, queueStatus)
                    self.log.info(queueString)
                    newCEString += "\n%s" % queueString
                    if queueStatus.count('Production'):
                        usefull = True
                if usefull:
                    body += newCEString
                    possibleNewSites.append(
                        'dirac-admin-add-site DIRACSiteName %s %s' %
                        (nameBDII, ce))
            if body:
                body = "We are glad to inform You about new CE(s) possibly suitable for %s:\n" % vo + body
                body += "\n\nTo suppress information about CE add its name to BannedCEs list."
                for possibleNewSite in possibleNewSites:
                    body = "%s\n%s" % (body, possibleNewSite)
                self.log.info(body)
                if self.addressTo and self.addressFrom:
                    notification = NotificationClient()
                    result = notification.sendMail(self.addressTo,
                                                   self.subject,
                                                   body,
                                                   self.addressFrom,
                                                   localAttempt=False)

        return S_OK()

    def __infoFromCE(self):

        sitesSection = cfgPath('Resources', 'Sites')
        result = gConfig.getSections(sitesSection)
        if not result['OK']:
            return
        grids = result['Value']

        changed = False
        body = ""

        for grid in grids:
            gridSection = cfgPath(sitesSection, grid)
            result = gConfig.getSections(gridSection)
            if not result['OK']:
                return
            sites = result['Value']

            for site in sites:
                siteSection = cfgPath(gridSection, site)
                opt = gConfig.getOptionsDict(siteSection)['Value']
                name = opt.get('Name', '')
                if name:
                    coor = opt.get('Coordinates', 'Unknown')
                    mail = opt.get('Mail', 'Unknown')

                    result = ldapSite(name)
                    if not result['OK']:
                        self.log.warn("BDII site %s: %s" %
                                      (name, result['Message']))
                        result = self.__checkAlternativeBDIISite(
                            ldapSite, name)

                    if result['OK']:
                        bdiiSites = result['Value']
                        if len(bdiiSites) == 0:
                            self.log.warn(name, "Error in BDII: leng = 0")
                        else:
                            if not len(bdiiSites) == 1:
                                self.log.warn(
                                    name, "Warning in BDII: leng = %d" %
                                    len(bdiiSites))

                            bdiiSite = bdiiSites[0]

                            try:
                                longitude = bdiiSite['GlueSiteLongitude']
                                latitude = bdiiSite['GlueSiteLatitude']
                                newcoor = "%s:%s" % (longitude, latitude)
                            except:
                                self.log.warn("Error in BDII coordinates")
                                newcoor = "Unknown"

                            try:
                                newmail = bdiiSite[
                                    'GlueSiteSysAdminContact'].split(
                                        ":")[-1].strip()
                            except:
                                self.log.warn("Error in BDII mail")
                                newmail = "Unknown"

                            self.log.debug("%s %s %s" %
                                           (name, newcoor, newmail))

                            if newcoor != coor:
                                self.log.info("%s" % (name),
                                              "%s -> %s" % (coor, newcoor))
                                if coor == 'Unknown':
                                    self.csAPI.setOption(
                                        cfgPath(siteSection, 'Coordinates'),
                                        newcoor)
                                else:
                                    self.csAPI.modifyValue(
                                        cfgPath(siteSection, 'Coordinates'),
                                        newcoor)
                                changed = True

                            if newmail != mail:
                                self.log.info("%s" % (name),
                                              "%s -> %s" % (mail, newmail))
                                if mail == 'Unknown':
                                    self.csAPI.setOption(
                                        cfgPath(siteSection, 'Mail'), newmail)
                                else:
                                    self.csAPI.modifyValue(
                                        cfgPath(siteSection, 'Mail'), newmail)
                                changed = True

                ceList = List.fromChar(opt.get('CE', ''))

                if not ceList:
                    self.log.warn(site, 'Empty site list')
                    continue

    #      result = gConfig.getSections( cfgPath( siteSection,'CEs' )
    #      if not result['OK']:
    #        self.log.debug( "Section CEs:", result['Message'] )

                for ce in ceList:
                    ceSection = cfgPath(siteSection, 'CEs', ce)
                    result = gConfig.getOptionsDict(ceSection)
                    if not result['OK']:
                        self.log.debug("Section CE", result['Message'])
                        wnTmpDir = 'Unknown'
                        arch = 'Unknown'
                        os = 'Unknown'
                        si00 = 'Unknown'
                        pilot = 'Unknown'
                        ceType = 'Unknown'
                    else:
                        ceopt = result['Value']
                        wnTmpDir = ceopt.get('wnTmpDir', 'Unknown')
                        arch = ceopt.get('architecture', 'Unknown')
                        os = ceopt.get('OS', 'Unknown')
                        si00 = ceopt.get('SI00', 'Unknown')
                        pilot = ceopt.get('Pilot', 'Unknown')
                        ceType = ceopt.get('CEType', 'Unknown')

                    result = ldapCE(ce)
                    if not result['OK']:
                        self.log.warn('Error in BDII for %s' % ce,
                                      result['Message'])
                        result = self.__checkAlternativeBDIISite(ldapCE, ce)
                        continue
                    try:
                        bdiiCE = result['Value'][0]
                    except:
                        self.log.warn('Error in BDII for %s' % ce, result)
                        bdiiCE = None
                    if bdiiCE:
                        try:
                            newWNTmpDir = bdiiCE['GlueSubClusterWNTmpDir']
                        except:
                            newWNTmpDir = 'Unknown'
                        if wnTmpDir != newWNTmpDir and newWNTmpDir != 'Unknown':
                            section = cfgPath(ceSection, 'wnTmpDir')
                            self.log.info(section, " -> ".join(
                                (wnTmpDir, newWNTmpDir)))
                            if wnTmpDir == 'Unknown':
                                self.csAPI.setOption(section, newWNTmpDir)
                            else:
                                self.csAPI.modifyValue(section, newWNTmpDir)
                            changed = True

                        try:
                            newArch = bdiiCE[
                                'GlueHostArchitecturePlatformType']
                        except:
                            newArch = 'Unknown'
                        if arch != newArch and newArch != 'Unknown':
                            section = cfgPath(ceSection, 'architecture')
                            self.log.info(section, " -> ".join(
                                (arch, newArch)))
                            if arch == 'Unknown':
                                self.csAPI.setOption(section, newArch)
                            else:
                                self.csAPI.modifyValue(section, newArch)
                            changed = True

                        try:
                            newOS = '_'.join(
                                (bdiiCE['GlueHostOperatingSystemName'],
                                 bdiiCE['GlueHostOperatingSystemVersion'],
                                 bdiiCE['GlueHostOperatingSystemRelease']))
                        except:
                            newOS = 'Unknown'
                        if os != newOS and newOS != 'Unknown':
                            section = cfgPath(ceSection, 'OS')
                            self.log.info(section, " -> ".join((os, newOS)))
                            if os == 'Unknown':
                                self.csAPI.setOption(section, newOS)
                            else:
                                self.csAPI.modifyValue(section, newOS)
                            changed = True
                            body = body + "OS was changed %s -> %s for %s at %s\n" % (
                                os, newOS, ce, site)

                        try:
                            newSI00 = bdiiCE['GlueHostBenchmarkSI00']
                        except:
                            newSI00 = 'Unknown'
                        if si00 != newSI00 and newSI00 != 'Unknown':
                            section = cfgPath(ceSection, 'SI00')
                            self.log.info(section, " -> ".join(
                                (si00, newSI00)))
                            if si00 == 'Unknown':
                                self.csAPI.setOption(section, newSI00)
                            else:
                                self.csAPI.modifyValue(section, newSI00)
                            changed = True

                        try:
                            rte = bdiiCE[
                                'GlueHostApplicationSoftwareRunTimeEnvironment']
                            for vo in self.voName:
                                if vo.lower() == 'lhcb':
                                    if 'VO-lhcb-pilot' in rte:
                                        newPilot = 'True'
                                    else:
                                        newPilot = 'False'
                                else:
                                    newPilot = 'Unknown'
                        except:
                            newPilot = 'Unknown'
                        if pilot != newPilot and newPilot != 'Unknown':
                            section = cfgPath(ceSection, 'Pilot')
                            self.log.info(section, " -> ".join(
                                (pilot, newPilot)))
                            if pilot == 'Unknown':
                                self.csAPI.setOption(section, newPilot)
                            else:
                                self.csAPI.modifyValue(section, newPilot)
                            changed = True

                    newVO = ''
                    for vo in self.voName:
                        result = ldapCEState(ce, vo)  #getBDIICEVOView
                        if not result['OK']:
                            self.log.warn('Error in BDII for queue %s' % ce,
                                          result['Message'])
                            result = self.__checkAlternativeBDIISite(
                                ldapCEState, ce, vo)
                            continue
                        try:
                            queues = result['Value']
                        except:
                            self.log.warn('Error in BDII for queue %s' % ce,
                                          result['Massage'])
                            continue

                        newCEType = 'Unknown'
                        for queue in queues:
                            try:
                                queueType = queue['GlueCEImplementationName']
                            except:
                                queueType = 'Unknown'
                            if newCEType == 'Unknown':
                                newCEType = queueType
                            else:
                                if queueType != newCEType:
                                    self.log.warn(
                                        'Error in BDII for CE %s ' % ce,
                                        'different CE types %s %s' %
                                        (newCEType, queueType))

                        if newCEType == 'ARC-CE':
                            newCEType = 'ARC'

                        if ceType != newCEType and newCEType != 'Unknown':
                            section = cfgPath(ceSection, 'CEType')
                            self.log.info(section, " -> ".join(
                                (ceType, newCEType)))
                            if ceType == 'Unknown':
                                self.csAPI.setOption(section, newCEType)
                            else:
                                self.csAPI.modifyValue(section, newCEType)
                            changed = True

                        for queue in queues:
                            try:
                                queueName = queue['GlueCEUniqueID'].split(
                                    '/')[-1]
                            except:
                                self.log.warn('Error in queueName ', queue)
                                continue

                            try:
                                newMaxCPUTime = queue['GlueCEPolicyMaxCPUTime']
                            except:
                                newMaxCPUTime = None

                            newSI00 = None
                            try:
                                caps = queue['GlueCECapability']
                                if type(caps) == type(''):
                                    caps = [caps]
                                for cap in caps:
                                    if cap.count('CPUScalingReferenceSI00'):
                                        newSI00 = cap.split('=')[-1]
                            except:
                                newSI00 = None

                            queueSection = cfgPath(ceSection, 'Queues',
                                                   queueName)
                            result = gConfig.getOptionsDict(queueSection)
                            if not result['OK']:
                                self.log.warn("Section Queues",
                                              result['Message'])
                                maxCPUTime = 'Unknown'
                                si00 = 'Unknown'
                                allowedVOs = ['']
                            else:
                                queueOpt = result['Value']
                                maxCPUTime = queueOpt.get(
                                    'maxCPUTime', 'Unknown')
                                si00 = queueOpt.get('SI00', 'Unknown')
                                if newVO == '':  # Remember previous iteration, if none - read from conf
                                    allowedVOs = queueOpt.get('VO',
                                                              '').split(",")
                                else:  # Else use newVO, as it can contain changes, which aren't in conf yet
                                    allowedVOs = newVO.split(",")
                            if newMaxCPUTime and (maxCPUTime != newMaxCPUTime):
                                section = cfgPath(queueSection, 'maxCPUTime')
                                self.log.info(
                                    section, " -> ".join(
                                        (maxCPUTime, newMaxCPUTime)))
                                if maxCPUTime == 'Unknown':
                                    self.csAPI.setOption(
                                        section, newMaxCPUTime)
                                else:
                                    self.csAPI.modifyValue(
                                        section, newMaxCPUTime)
                                changed = True

                            if newSI00 and (si00 != newSI00):
                                section = cfgPath(queueSection, 'SI00')
                                self.log.info(section, " -> ".join(
                                    (si00, newSI00)))
                                if si00 == 'Unknown':
                                    self.csAPI.setOption(section, newSI00)
                                else:
                                    self.csAPI.modifyValue(section, newSI00)
                                changed = True

                            modifyVO = True  # Flag saying if we need VO option to change
                            newVO = ''
                            if allowedVOs != ['']:
                                for allowedVO in allowedVOs:
                                    allowedVO = allowedVO.strip(
                                    )  # Get rid of spaces
                                    newVO += allowedVO
                                    if allowedVO == vo:  # Current VO has been already in list
                                        newVO = ''
                                        modifyVO = False  # Don't change anything
                                        break  # Skip next 'if', proceed to next VO
                                    newVO += ', '

                            if modifyVO:
                                section = cfgPath(queueSection, 'VO')
                                newVO += vo
                                self.log.info(
                                    section, " -> ".join(
                                        ('%s' % allowedVOs, newVO)))
                                if allowedVOs == ['']:
                                    self.csAPI.setOption(section, newVO)
                                else:
                                    self.csAPI.modifyValue(section, newVO)
                                changed = True

        if changed:
            self.log.info(body)
            if body and self.addressTo and self.addressFrom:
                notification = NotificationClient()
                result = notification.sendMail(self.addressTo,
                                               self.subject,
                                               body,
                                               self.addressFrom,
                                               localAttempt=False)

            return self.csAPI.commit()
        else:
            self.log.info("No changes found")
            return S_OK()
예제 #28
0
def main():
    Script.parseCommandLine(ignoreErrors=True)
    args = Script.getPositionalArgs()

    if len(args) < 3:
        Script.showHelp(exitCode=1)

    diracSiteName = args[0]
    gridSiteName = args[1]
    ces = args[2:]
    try:
        diracGridType, place, country = diracSiteName.split('.')
    except ValueError:
        gLogger.error(
            "The DIRACSiteName should be of the form GRID.LOCATION.COUNTRY for example LCG.CERN.ch"
        )
        DIRACExit(-1)

    result = getDIRACSiteName(gridSiteName)
    newSite = True
    if result['OK'] and result['Value']:
        if len(result['Value']) > 1:
            gLogger.notice(
                '%s GOC site name is associated with several DIRAC sites:' %
                gridSiteName)
            for i, dSite in enumerate(result['Value']):
                gLogger.notice('%d: %s' % (i, dSite))
            inp = six.moves.input('Enter your choice number: ')
            try:
                inp = int(inp)
            except ValueError:
                gLogger.error('You should enter an integer number')
                DIRACExit(-1)
            if 0 <= inp < len(result['Value']):
                diracCSSite = result['Value'][inp]
            else:
                gLogger.error('Number out of range: %d' % inp)
                DIRACExit(-1)
        else:
            diracCSSite = result['Value'][0]
        if diracCSSite == diracSiteName:
            gLogger.notice('Site with GOC name %s is already defined as %s' %
                           (gridSiteName, diracSiteName))
            newSite = False
        else:
            gLogger.error(
                'ERROR: Site with GOC name %s is already defined as %s' %
                (gridSiteName, diracCSSite))
            DIRACExit(-1)
    else:
        gLogger.error("ERROR getting DIRAC site name of %s" % gridSiteName,
                      result.get('Message'))

    csAPI = CSAPI()

    if newSite:
        gLogger.notice("Site to CS: %s" % diracSiteName)
        res = csAPI.addSite(diracSiteName, {"Name": gridSiteName})
        if not res['OK']:
            gLogger.error("Failed adding site to CS", res['Message'])
            DIRACExit(1)
        res = csAPI.commit()
        if not res['OK']:
            gLogger.error("Failure committing to CS", res['Message'])
            DIRACExit(3)

    for ce in ces:
        gLogger.notice("Adding CE %s" % ce)
        res = csAPI.addCEtoSite(diracSiteName, ce)
        if not res['OK']:
            gLogger.error("Failed adding CE %s to CS" % ce, res['Message'])
            DIRACExit(2)
        res = csAPI.commit()
        if not res['OK']:
            gLogger.error("Failure committing to CS", res['Message'])
            DIRACExit(3)
예제 #29
0
class Bdii2CSAgent( AgentModule ):

  addressTo = ''
  addressFrom = ''
  voName = ''
  subject = "CE2CSAgent"
  alternativeBDIIs = []

  def initialize( self ):

    self.addressTo = self.am_getOption( 'MailTo', self.addressTo )
    self.addressFrom = self.am_getOption( 'MailFrom', self.addressFrom )
    # Create a list of alternative bdii urls
    self.alternativeBDIIs = self.am_getOption( 'AlternativeBDIIs', [] )
    # Check if the bdii url is appended by a port number, if not append the default 2170
    for index, url in enumerate( self.alternativeBDIIs ):
      if not url.split( ':' )[-1].isdigit():
        self.alternativeBDIIs[index] += ':2170'
    if self.addressTo and self.addressFrom:
      self.log.info( "MailTo", self.addressTo )
      self.log.info( "MailFrom", self.addressFrom )
    if self.alternativeBDIIs :
      self.log.info( "AlternativeBDII URLs:", self.alternativeBDIIs )
    self.subject = "CE2CSAgent"
    
    self.processCEs = self.am_getOption( 'ProcessCEs', True )
    self.processSEs = self.am_getOption( 'ProcessSEs', False )

    self.voName = self.am_getOption( 'VirtualOrganization', [] )
    if not self.voName:
      self.voName = self.am_getOption( 'VO', [] )
    if not self.voName or ( len( self.voName ) == 1 and self.voName[0].lower() == 'all' ):
      # Get all VOs defined in the configuration
      self.voName = []
      result = getVOs()
      if result['OK']:
        vos = result['Value']
        for vo in vos:
          vomsVO = getVOOption( vo, "VOMSName" )
          if vomsVO:
            self.voName.append( vomsVO )

    if self.voName:
      self.log.info( "Agent will manage VO(s) %s" % self.voName )
    else:
      self.log.fatal( "VirtualOrganization option not defined for agent" )
      return S_ERROR()
    self.voBdiiCEDict = {}
    self.voBdiiSEDict = {}

    self.csAPI = CSAPI()
    return self.csAPI.initialize()

  def execute( self ):
    """ General agent execution method
    """
   
    # Get a "fresh" copy of the CS data
    result = self.csAPI.downloadCSData()
    if not result['OK']:
      self.log.warn( "Could not download a fresh copy of the CS data", result[ 'Message' ] )

    if self.processCEs:
      self.__lookForNewCEs()
      self.__updateCEs()
    if self.processSEs:  
      self.__lookForNewSEs()
      self.__updateSEs()
    return S_OK()

  def __lookForNewCEs( self ):
    """ Look up BDII for CEs not yet present in the DIRAC CS
    """

    bannedCEs = self.am_getOption( 'BannedCEs', [] )
    result = getCEsFromCS()
    if not result['OK']:
      return result
    knownCEs = set( result['Value'] )
    knownCEs = knownCEs.union( set( bannedCEs ) )

    for vo in self.voName:
      result = self.__getBdiiCEInfo( vo )
      if not result['OK']:
        continue
      bdiiInfo = result['Value']
      result = getGridCEs( vo, bdiiInfo = bdiiInfo, ceBlackList = knownCEs )
      if not result['OK']:
        self.log.error( 'Failed to get unused CEs', result['Message'] )
      siteDict = result['Value']  
      body = ''
      for site in siteDict:
        newCEs = set( siteDict[site].keys() )
        if not newCEs:
          continue
        
        ceString = ''
        for ce in newCEs:
          queueString = ''
          ceInfo = bdiiInfo[site]['CEs'][ce]
          ceString = "CE: %s, GOCDB Site Name: %s" % ( ce, site )
          systemTuple = siteDict[site][ce]['System']
          osString = "%s_%s_%s" % ( systemTuple )
          newCEString = "\n%s\n%s\n" % ( ceString, osString )
          for queue in ceInfo['Queues']:
            queueStatus = ceInfo['Queues'][queue].get( 'GlueCEStateStatus', 'UnknownStatus' )
            if 'production' in queueStatus.lower():
              ceType = ceInfo['Queues'][queue].get( 'GlueCEImplementationName', '' )
              queueString += "   %s %s %s\n" % ( queue, queueStatus, ceType )
          if queueString:
            ceString = newCEString
            ceString += "Queues:\n"
            ceString += queueString

        if ceString:
          body += ceString

      if body:
        body = "\nWe are glad to inform You about new CE(s) possibly suitable for %s:\n" % vo + body
        body += "\n\nTo suppress information about CE add its name to BannedCEs list.\n"
        body += "Add new Sites/CEs for vo %s with the command:\n" % vo
        body += "dirac-admin-add-resources --vo %s --ce\n" % vo
        self.log.info( body )
        if self.addressTo and self.addressFrom:
          notification = NotificationClient()
          result = notification.sendMail( self.addressTo, self.subject, body, self.addressFrom, localAttempt = False )
          if not result['OK']:
            self.log.error( 'Can not send new site notification mail', result['Message'] )

    return S_OK()

  def __getBdiiCEInfo( self, vo ):

    if vo in self.voBdiiCEDict:
      return S_OK( self.voBdiiCEDict[vo] )
    self.log.info( "Check for available CEs for VO", vo )
    result = getBdiiCEInfo( vo )
    message = ''
    if not result['OK']:
      message = result['Message']
      for bdii in self.alternativeBDIIs :
        result = getBdiiCEInfo( vo, host = bdii )
        if result['OK']:
          break
    if not result['OK']:
      if message:
        self.log.error( "Error during BDII request", message )
      else:
        self.log.error( "Error during BDII request", result['Message'] )
    else:
      self.voBdiiCEDict[vo] = result['Value']
    return result
  
  def __getBdiiSEInfo( self, vo ):

    if vo in self.voBdiiSEDict:
      return S_OK( self.voBdiiSEDict[vo] )
    self.log.info( "Check for available SEs for VO", vo )
    result = getBdiiSEInfo( vo )
    message = ''
    if not result['OK']:
      message = result['Message']
      for bdii in self.alternativeBDIIs :
        result = getBdiiSEInfo( vo, host = bdii )
        if result['OK']:
          break
    if not result['OK']:
      if message:
        self.log.error( "Error during BDII request", message )
      else:
        self.log.error( "Error during BDII request", result['Message'] )
    else:
      self.voBdiiSEDict[vo] = result['Value']
    return result

  def __updateCEs( self ):
    """ Update the Site/CE/queue settings in the CS if they were changed in the BDII
    """

    bdiiChangeSet = set()

    for vo in self.voName:
      result = self.__getBdiiCEInfo( vo )
      if not result['OK']:
        continue
      ceBdiiDict = result['Value']
      result = getSiteUpdates( vo, bdiiInfo = ceBdiiDict, log = self.log )
      if not result['OK']:
        continue
      bdiiChangeSet = bdiiChangeSet.union( result['Value'] )
      
    # We have collected all the changes, consolidate VO settings
    result = self.__updateCS( bdiiChangeSet )
    return result

  def __updateCS( self, bdiiChangeSet ):
    
    queueVODict = {}
    changeSet = set()
    for entry in bdiiChangeSet:
      section, option , _value, new_value = entry
      if option == "VO":
        queueVODict.setdefault( section, set() )
        queueVODict[section] = queueVODict[section].union( set( new_value.split( ',' ) ) )  
      else:
        changeSet.add( entry )  
    for section, VOs in queueVODict.items():
      changeSet.add( ( section, 'VO', '', ','.join( VOs ) ) )    

    if changeSet:
      changeList = list( changeSet )
      changeList.sort()
      body = '\n'.join( [ "%s/%s %s -> %s" % entry for entry in changeList ] )
      if body and self.addressTo and self.addressFrom:
        notification = NotificationClient()
        result = notification.sendMail( self.addressTo, self.subject, body, self.addressFrom, localAttempt = False )
        
      if body:  
        self.log.info( 'The following configuration changes were detected:' )  
        self.log.info( body )

      for section, option, value, new_value in changeSet:
        if value == 'Unknown' or not value:
          self.csAPI.setOption( cfgPath( section, option ), new_value )
        else:
          self.csAPI.modifyValue( cfgPath( section, option ), new_value )

      result = self.csAPI.commit()
      if not result['OK']:
        self.log.error( "Error while committing to CS", result['Message'] )
      else:
        self.log.info( "Successfully committed %d changes to CS" % len( changeList ) )
      return result
    else:
      self.log.info( "No changes found" )
      return S_OK()

  def __lookForNewSEs( self ):
    """ Look up BDII for SEs not yet present in the DIRAC CS
    """
    
    bannedSEs = self.am_getOption( 'BannedSEs', [] )
    result = getSEsFromCS()
    if not result['OK']:
      return result
    knownSEs = set( result['Value'] )
    knownSEs = knownSEs.union( set( bannedSEs ) )

    for vo in self.voName:
      result = self.__getBdiiSEInfo( vo )
      if not result['OK']:
        continue
      bdiiInfo = result['Value']
      result = getGridSRMs( vo, bdiiInfo = bdiiInfo, srmBlackList = knownSEs )
      if not result['OK']:
        continue
      siteDict = result['Value']  
      body = ''
      for site in siteDict:
        newSEs = set( siteDict[site].keys() )
        if not newSEs:
          continue
        for se in newSEs:
          body += '\n New SE %s available at site %s:\n' % ( se, site )
          backend = siteDict[site][se]['SE'].get( 'GlueSEImplementationName', 'Unknown' )
          size = siteDict[site][se]['SE'].get( 'GlueSESizeTotal', 'Unknown' )
          body += '  Backend %s, Size %s' % ( backend, size )
          
      if body:
        body = "\nWe are glad to inform You about new SE(s) possibly suitable for %s:\n" % vo + body
        body += "\n\nTo suppress information about an SE add its name to BannedSEs list.\n"
        body += "Add new SEs for vo %s with the command:\n" % vo
        body += "dirac-admin-add-resources --vo %s --se\n" % vo
        self.log.info( body )
        if self.addressTo and self.addressFrom:
          notification = NotificationClient()
          result = notification.sendMail( self.addressTo, self.subject, body, self.addressFrom, localAttempt = False )
          if not result['OK']:
            self.log.error( 'Can not send new site notification mail', result['Message'] )

    return S_OK()   
  
  def __updateSEs( self ):
    """ Update the Storage Element settings in the CS if they were changed in the BDII
    """
    
    bdiiChangeSet = set()

    for vo in self.voName:
      result = self.__getBdiiSEInfo( vo )
      if not result['OK']:
        continue
      seBdiiDict = result['Value']
      result = getSRMUpdates( vo, bdiiInfo = seBdiiDict )
      if not result['OK']:
        continue
      bdiiChangeSet = bdiiChangeSet.union( result['Value'] )
      
    # We have collected all the changes, consolidate VO settings
    result = self.__updateCS( bdiiChangeSet )
    return result
예제 #30
0
class GOCDB2CSAgent(AgentModule):
    """ Class to retrieve information about service endpoints
      from GOCDB and update configuration stored by CS
  """
    def __init__(self, *args, **kwargs):
        """ c'tor
    """
        super(GOCDB2CSAgent, self).__init__(*args, **kwargs)
        self.GOCDBClient = None
        self.csAPI = None
        self.dryRun = False

    def initialize(self):
        """ Run at the agent initialization (normally every 500 cycles)
    """
        # client to connect to GOCDB
        self.GOCDBClient = GOCDBClient()
        self.dryRun = self.am_getOption('DryRun', self.dryRun)

        # API needed to update configuration stored by CS
        self.csAPI = CSAPI()
        return self.csAPI.initialize()

    def execute(self):
        """
    Execute GOCDB queries according to the function map
    and user request (options in configuration).
    """

        # __functionMap is at the end of the class definition
        for option, functionCall in GOCDB2CSAgent.__functionMap.iteritems():
            optionValue = self.am_getOption(option, True)
            if optionValue:
                result = functionCall(self)
                if not result['OK']:
                    self.log.error("%s() failed with message: %s" %
                                   (functionCall.__name__, result['Message']))
                else:
                    self.log.info("Successfully executed %s" %
                                  functionCall.__name__)

        return S_OK()

    def updatePerfSONARConfiguration(self):
        """
    Get current status of perfSONAR endpoints from GOCDB
    and update CS configuration accordingly.
    """
        log = self.log.getSubLogger('updatePerfSONAREndpoints')
        log.debug('Begin function ...')

        # get endpoints
        result = self.__getPerfSONAREndpoints()
        if not result['OK']:
            log.error("__getPerfSONAREndpoints() failed with message: %s" %
                      result['Message'])
            return S_ERROR('Unable to fetch perfSONAR endpoints from GOCDB.')
        endpointList = result['Value']

        # add DIRAC site name
        result = self.__addDIRACSiteName(endpointList)
        if not result['OK']:
            log.error("__addDIRACSiteName() failed with message: %s" %
                      result['Message'])
            return S_ERROR('Unable to extend the list with DIRAC site names.')
        extendedEndpointList = result['Value']

        # prepare dictionary with new configuration
        result = self.__preparePerfSONARConfiguration(extendedEndpointList)
        if not result['OK']:
            log.error(
                "__preparePerfSONARConfiguration() failed with message: %s" %
                result['Message'])
            return S_ERROR('Unable to prepare a new perfSONAR configuration.')
        finalConfiguration = result['Value']

        # update configuration according to the final status of endpoints
        self.__updateConfiguration(finalConfiguration)
        log.debug("Configuration updated succesfully")

        log.debug('End function.')
        return S_OK()

    def __getPerfSONAREndpoints(self):
        """
    Retrieve perfSONAR endpoint information directly from GOCDB.

    :return: List of perfSONAR endpoints (dictionaries) as stored by GOCDB.
    """

        log = self.log.getSubLogger('__getPerfSONAREndpoints')
        log.debug('Begin function ...')

        # get perfSONAR endpoints (latency and bandwidth) form GOCDB
        endpointList = []
        for endpointType in ['Latency', 'Bandwidth']:
            result = self.GOCDBClient.getServiceEndpointInfo(
                'service_type', 'net.perfSONAR.%s' % endpointType)

            if not result['OK']:
                log.error("getServiceEndpointInfo() failed with message: %s" %
                          result['Message'])
                return S_ERROR('Could not fetch %s endpoints from GOCDB' %
                               endpointType.lower())

            log.debug('Number of %s endpoints: %s' %
                      (endpointType.lower(), len(result['Value'])))
            endpointList.extend(result['Value'])

        log.debug('Number of perfSONAR endpoints: %s' % len(endpointList))
        log.debug('End function.')
        return S_OK(endpointList)

    def __preparePerfSONARConfiguration(self, endpointList):
        """
    Prepare a dictionary with a new CS configuration of perfSONAR endpoints.

    :return: Dictionary where keys are configuration paths (options and sections)
             and values are values of corresponding options
             or None in case of a path pointing to a section.
    """

        log = self.log.getSubLogger('__preparePerfSONARConfiguration')
        log.debug('Begin function ...')

        # static elements of a path
        rootPath = '/Resources/Sites'
        extPath = 'Network'
        baseOptionName = 'Enabled'
        options = {baseOptionName: 'True', 'ServiceType': 'perfSONAR'}

        # enable GOCDB endpoints in configuration
        newConfiguration = {}
        for endpoint in endpointList:
            if endpoint['DIRACSITENAME'] is None:
                continue

            split = endpoint['DIRACSITENAME'].split('.')
            path = cfgPath(rootPath, split[0], endpoint['DIRACSITENAME'],
                           extPath, endpoint['HOSTNAME'])
            for name, defaultValue in options.iteritems():
                newConfiguration[cfgPath(path, name)] = defaultValue

        # get current configuration
        currentConfiguration = {}
        for option in options.iterkeys():
            result = gConfig.getConfigurationTree(rootPath, extPath + '/',
                                                  '/' + option)
            if not result['OK']:
                log.error("getConfigurationTree() failed with message: %s" %
                          result['Message'])
                return S_ERROR('Unable to fetch perfSONAR endpoints from CS.')
            currentConfiguration.update(result['Value'])

        # disable endpoints that disappeared in GOCDB
        removedElements = set(currentConfiguration) - set(newConfiguration)
        newElements = set(newConfiguration) - set(currentConfiguration)

        addedEndpoints = len(newElements) / len(options)
        disabledEndpoints = 0
        for path in removedElements:
            if baseOptionName in path:
                newConfiguration[path] = 'False'
                if currentConfiguration[path] != 'False':
                    disabledEndpoints = disabledEndpoints + 1

        # inform what will be changed
        if addedEndpoints > 0:
            self.log.info(
                "%s new perfSONAR endpoints will be added to the configuration"
                % addedEndpoints)

        if disabledEndpoints > 0:
            self.log.info(
                "%s old perfSONAR endpoints will be disable in the configuration"
                % disabledEndpoints)

        if addedEndpoints == 0 and disabledEndpoints == 0:
            self.log.info("perfSONAR configuration is up-to-date")

        log.debug('End function.')
        return S_OK(newConfiguration)

    def __addDIRACSiteName(self, inputList):
        """
    Extend given list of GOCDB endpoints with DIRAC site name, i.e.
    add an entry "DIRACSITENAME" in dictionaries that describe endpoints.
    If given site name could not be found "DIRACSITENAME" is set to 'None'.

    :return: List of perfSONAR endpoints (dictionaries).
    """

        log = self.log.getSubLogger('__addDIRACSiteName')
        log.debug('Begin function ...')

        # get site name dictionary
        result = getDIRACGOCDictionary()
        if not result['OK']:
            log.error("getDIRACGOCDictionary() failed with message: %s" %
                      result['Message'])
            return S_ERROR('Could not get site name dictionary')

        # reverse the dictionary (assume 1 to 1 relation)
        DIRACGOCDict = result['Value']
        GOCDIRACDict = dict(zip(DIRACGOCDict.values(), DIRACGOCDict.keys()))

        # add DIRAC site names
        outputList = []
        for entry in inputList:
            try:
                entry['DIRACSITENAME'] = GOCDIRACDict[entry['SITENAME']]
            except KeyError:
                self.log.warn("No dictionary entry for %s. " %
                              entry['SITENAME'])
                entry['DIRACSITENAME'] = None
            outputList.append(entry)

        log.debug('End function.')
        return S_OK(outputList)

    def __updateConfiguration(self, setElements=None, delElements=None):
        """
    Update configuration stored by CS.
    """
        if setElements is None:
            setElements = {}
        if delElements is None:
            delElements = []

        log = self.log.getSubLogger('__updateConfiguration')
        log.debug('Begin function ...')

        # assure existence and proper value of a section or an option
        for path, value in setElements.iteritems():

            if value is None:
                section = path
            else:
                split = path.rsplit('/', 1)
                section = split[0]

            try:
                result = self.csAPI.createSection(section)
                if not result['OK']:
                    log.error("createSection() failed with message: %s" %
                              result['Message'])
            except Exception as e:
                log.error("Exception in createSection(): %s" %
                          repr(e).replace(',)', ')'))

            if value is not None:
                try:
                    result = self.csAPI.setOption(path, value)
                    if not result['OK']:
                        log.error("setOption() failed with message: %s" %
                                  result['Message'])
                except Exception as e:
                    log.error("Exception in setOption(): %s" %
                              repr(e).replace(',)', ')'))

        # delete elements in the configuration
        for path in delElements:
            result = self.csAPI.delOption(path)
            if not result['OK']:
                log.warn("delOption() failed with message: %s" %
                         result['Message'])

                result = self.csAPI.delSection(path)
                if not result['OK']:
                    log.warn("delSection() failed with message: %s" %
                             result['Message'])

        if self.dryRun:
            log.info("Dry Run: CS won't be updated")
            self.csAPI.showDiff()
        else:
            # update configuration stored by CS
            result = self.csAPI.commit()
            if not result['OK']:
                log.error("commit() failed with message: %s" %
                          result['Message'])
                return S_ERROR("Could not commit changes to CS.")
            else:
                log.info("Committed changes to CS")

        log.debug('End function.')
        return S_OK()

    # define mapping between an agent option in the configuration and a function call
    __functionMap = {
        'UpdatePerfSONARS': updatePerfSONARConfiguration,
    }
예제 #31
0
#       }
#     }
#   }
# }

for sct in [
        'Systems/Bookkeeping', 'Systems/Bookkeeping/Production',
        'Systems/Bookkeeping/Production/Databases',
        'Systems/Bookkeeping/Production/Databases/BookkeepingDB'
]:
    res = csAPI.createSection(sct)
    if not res['OK']:
        print(res['Message'])
        exit(1)

csAPI.setOption(
    'Systems/Bookkeeping/Production/Databases/BookkeepingDB/LHCbDIRACBookkeepingTNS',
    'FILL_ME')
csAPI.setOption(
    'Systems/Bookkeeping/Production/Databases/BookkeepingDB/LHCbDIRACBookkeepingUser',
    'FILL_ME')
csAPI.setOption(
    'Systems/Bookkeeping/Production/Databases/BookkeepingDB/LHCbDIRACBookkeepingPassword',
    'FILL_ME')
csAPI.setOption(
    'Systems/Bookkeeping/Production/Databases/BookkeepingDB/LHCbDIRACBookkeepingServer',
    'FILL_ME')

# Commit
csAPI.commit()
예제 #32
0
def checkUnusedSEs():

  global vo, dry

  result = getGridSRMs(vo, unUsed=True)
  if not result['OK']:
    gLogger.error('Failed to look up SRMs in BDII', result['Message'])
  siteSRMDict = result['Value']

  # Evaluate VOs
  result = getVOs()
  if result['OK']:
    csVOs = set(result['Value'])
  else:
    csVOs = {vo}

  changeSetFull = set()

  for site in siteSRMDict:
    for gridSE in siteSRMDict[site]:
      changeSet = set()
      seDict = siteSRMDict[site][gridSE]['SE']
      srmDict = siteSRMDict[site][gridSE]['SRM']
      # Check the SRM version
      version = srmDict.get('GlueServiceVersion', '')
      if not (version and version.startswith('2')):
        gLogger.debug('Skipping SRM service with version %s' % version)
        continue
      result = getDIRACSiteName(site)
      if not result['OK']:
        gLogger.notice('Unused se %s is detected at unused site %s' % (gridSE, site))
        gLogger.notice('Consider adding site %s to the DIRAC CS' % site)
        continue
      diracSites = result['Value']
      yn = raw_input(
          '\nDo you want to add new SRM SE %s at site(s) %s ? default yes [yes|no]: ' %
          (gridSE, str(diracSites)))
      if not yn or yn.lower().startswith('y'):
        if len(diracSites) > 1:
          prompt = 'Which DIRAC site the new SE should be attached to ?'
          for i, s in enumerate(diracSites):
            prompt += '\n[%d] %s' % (i, s)
          prompt += '\nEnter your choice number: '
          inp = raw_input(prompt)
          try:
            ind = int(inp)
          except BaseException:
            gLogger.notice('Can not interpret your choice: %s, try again later' % inp)
            continue
          diracSite = diracSites[ind]
        else:
          diracSite = diracSites[0]

        domain, siteName, country = diracSite.split('.')
        recName = '%s-disk' % siteName
        inp = raw_input('Give a DIRAC name to the grid SE %s, default %s : ' % (gridSE, recName))
        diracSEName = inp
        if not inp:
          diracSEName = recName

        gLogger.notice('Adding new SE %s at site %s' % (diracSEName, diracSite))
        seSection = cfgPath('/Resources/StorageElements', diracSEName)
        changeSet.add((seSection, 'BackendType', seDict.get('GlueSEImplementationName', 'Unknown')))
        changeSet.add((seSection, 'Description', seDict.get('GlueSEName', 'Unknown')))
        bdiiVOs = set([re.sub('^VO:', '', rule) for rule in srmDict.get('GlueServiceAccessControlBaseRule', [])])
        seVOs = csVOs.intersection(bdiiVOs)
        changeSet.add((seSection, 'VO', ','.join(seVOs)))
        accessSection = cfgPath(seSection, 'AccessProtocol.1')
        changeSet.add((accessSection, 'Protocol', 'srm'))
        changeSet.add((accessSection, 'PluginName', 'SRM2'))
        endPoint = srmDict.get('GlueServiceEndpoint', '')
        host = urlparse(endPoint).hostname
        port = urlparse(endPoint).port
        changeSet.add((accessSection, 'Host', host))
        changeSet.add((accessSection, 'Port', port))
        changeSet.add((accessSection, 'Access', 'remote'))
        voPathSection = cfgPath(accessSection, 'VOPath')
        if 'VOPath' in seDict:
          path = seDict['VOPath']
          voFromPath = os.path.basename(path)
          if voFromPath != diracVO:
            gLogger.notice('\n!!! Warning: non-conventional VO path: %s\n' % path)
            changeSet.add((voPathSection, diracVO, path))
          path = os.path.dirname(path)
        else:
          # Try to guess the Path
          domain = '.'.join(host.split('.')[-2:])
          path = '/dpm/%s/home' % domain
        changeSet.add((accessSection, 'Path', path))
        changeSet.add((accessSection, 'SpaceToken', ''))
        changeSet.add((accessSection, 'WSUrl', '/srm/managerv2?SFN='))

        gLogger.notice('SE %s will be added with the following parameters' % diracSEName)
        changeList = sorted(changeSet)
        for entry in changeList:
          gLogger.notice(entry)
        yn = raw_input('Do you want to add new SE %s ? default yes [yes|no]: ' % diracSEName)
        if not yn or yn.lower().startswith('y'):
          changeSetFull = changeSetFull.union(changeSet)

  if dry:
    if changeSetFull:
      gLogger.notice('Skipping commit of the new SE data in a dry run')
    else:
      gLogger.notice("No new SE to be added")
    return S_OK()

  if changeSetFull:
    csAPI = CSAPI()
    csAPI.initialize()
    result = csAPI.downloadCSData()
    if not result['OK']:
      gLogger.error('Failed to initialize CSAPI object', result['Message'])
      DIRACExit(-1)
    changeList = sorted(changeSetFull)
    for section, option, value in changeList:
      csAPI.setOption(cfgPath(section, option), value)

    yn = raw_input('New SE data is accumulated\n Do you want to commit changes to CS ? default yes [yes|no]: ')
    if not yn or yn.lower().startswith('y'):
      result = csAPI.commit()
      if not result['OK']:
        gLogger.error("Error while commit to CS", result['Message'])
      else:
        gLogger.notice("Successfully committed %d changes to CS" % len(changeSetFull))
  else:
    gLogger.notice("No new SE to be added")

  return S_OK()
예제 #33
0
class Bdii2CSAgent(AgentModule):

  def __init__(self, *args, **kwargs):
    """ Defines default parameters
    """

    super(Bdii2CSAgent, self).__init__(*args, **kwargs)

    self.addressTo = ''
    self.addressFrom = ''
    self.voName = []
    self.subject = self.am_getModuleParam('fullName')
    self.alternativeBDIIs = []
    self.voBdiiCEDict = {}
    self.voBdiiSEDict = {}
    self.host = 'lcg-bdii.cern.ch:2170'
    self.glue2URLs = []
    self.glue2Only = True

    self.csAPI = None

    # What to get
    self.processCEs = True
    self.selectedSites = []

    # Update the CS or not?
    self.dryRun = False

  def initialize(self):
    """ Gets run paramaters from the configuration
    """

    self.addressTo = self.am_getOption('MailTo', self.addressTo)
    self.addressFrom = self.am_getOption('MailFrom', self.addressFrom)
    # Create a list of alternative bdii urls
    self.alternativeBDIIs = self.am_getOption('AlternativeBDIIs', self.alternativeBDIIs)
    self.host = self.am_getOption('Host', self.host)
    self.glue2URLs = self.am_getOption('GLUE2URLs', self.glue2URLs)
    self.glue2Only = self.am_getOption('GLUE2Only', self.glue2Only)

    # Check if the bdii url is appended by a port number, if not append the default 2170
    for index, url in enumerate(self.alternativeBDIIs):
      if not url.split(':')[-1].isdigit():
        self.alternativeBDIIs[index] += ':2170'
    if self.addressTo and self.addressFrom:
      self.log.info("MailTo", self.addressTo)
      self.log.info("MailFrom", self.addressFrom)
    if self.alternativeBDIIs:
      self.log.info("AlternativeBDII URLs:", self.alternativeBDIIs)

    self.processCEs = self.am_getOption('ProcessCEs', self.processCEs)
    self.selectedSites = self.am_getOption('SelectedSites', [])
    self.dryRun = self.am_getOption('DryRun', self.dryRun)

    self.voName = self.am_getOption('VirtualOrganization', self.voName)
    if not self.voName:
      self.voName = self.am_getOption('VO', [])
    if not self.voName or (len(self.voName) == 1 and self.voName[0].lower() == 'all'):
      # Get all VOs defined in the configuration
      self.voName = []
      result = getVOs()
      if result['OK']:
        vos = result['Value']
        for vo in vos:
          vomsVO = getVOOption(vo, "VOMSName")
          if vomsVO:
            self.voName.append(vomsVO)

    if self.voName:
      self.log.info("Agent will manage VO(s) %s" % self.voName)
    else:
      self.log.fatal("VirtualOrganization option not defined for agent")
      return S_ERROR()

    self.csAPI = CSAPI()
    return self.csAPI.initialize()

  def execute(self):
    """ General agent execution method
    """
    self.voBdiiCEDict = {}

    # Get a "fresh" copy of the CS data
    result = self.csAPI.downloadCSData()
    if not result['OK']:
      self.log.warn("Could not download a fresh copy of the CS data", result['Message'])

    # Refresh the configuration from the master server
    gConfig.forceRefresh(fromMaster=True)

    if self.processCEs:
      self.__lookForNewCEs()
      self.__updateCEs()
    return S_OK()

  def __lookForNewCEs(self):
    """ Look up BDII for CEs not yet present in the DIRAC CS
    """

    bannedCEs = self.am_getOption('BannedCEs', [])

    for vo in self.voName:
      # get the known CEs for a given VO, so we can know the unknowns, or no longer supported,
      # for a VO
      res = getQueues(community=vo)
      if not res['OK']:
        return res

      knownCEs = set()
      for _site, ces in res['Value'].items():
        knownCEs.update(ces)
      knownCEs.update(bannedCEs)

      result = self.__getBdiiCEInfo(vo)
      if not result['OK']:
        continue
      bdiiInfo = result['Value']
      result = getGridCEs(vo, bdiiInfo=bdiiInfo, ceBlackList=knownCEs)
      if not result['OK']:
        self.log.error('Failed to get unused CEs', result['Message'])
        continue  # next VO
      siteDict = result['Value']
      unknownCEs = set(result['UnknownCEs']) - set(bannedCEs)

      body = ''
      for site in siteDict:
        newCEs = set(siteDict[site])  # pylint: disable=no-member
        if not newCEs:
          continue

        ceString = ''
        for ce in newCEs:
          queueString = ''
          ceInfo = bdiiInfo[site]['CEs'][ce]
          newCEString = "CE: %s, GOCDB Site Name: %s" % (ce, site)
          systemTuple = siteDict[site][ce]['System']
          osString = "%s_%s_%s" % (systemTuple)
          newCEString = "\n%s\n%s\n" % (newCEString, osString)
          for queue in ceInfo['Queues']:
            queueStatus = ceInfo['Queues'][queue].get('GlueCEStateStatus', 'UnknownStatus')
            if 'production' in queueStatus.lower():
              ceType = ceInfo['Queues'][queue].get('GlueCEImplementationName', '')
              queueString += "   %s %s %s\n" % (queue, queueStatus, ceType)
          if queueString:
            ceString += newCEString
            ceString += "Queues:\n"
            ceString += queueString

        if ceString:
          body += ceString

      if siteDict:
        body = "\nWe are glad to inform You about new CE(s) possibly suitable for %s:\n" % vo + body
        body += "\n\nTo suppress information about CE add its name to BannedCEs list.\n"
        body += "Add new Sites/CEs for vo %s with the command:\n" % vo
        body += "dirac-admin-add-resources --vo %s --ce\n" % vo

      if unknownCEs:
        body += '\n\n'
        body += 'There is no (longer) information about the following CEs for the %s VO.\n' % vo
        body += '\n'.join(sorted(unknownCEs))
        body += '\n\n'

      if body:
        self.log.info(body)
        if self.addressTo and self.addressFrom:
          notification = NotificationClient()
          result = notification.sendMail(self.addressTo, self.subject, body, self.addressFrom,
                                         localAttempt=False, avoidSpam=True)
          if not result['OK']:
            self.log.error('Can not send new site notification mail', result['Message'])

    return S_OK()

  def __getBdiiCEInfo(self, vo):

    if vo in self.voBdiiCEDict:
      return S_OK(self.voBdiiCEDict[vo])
    self.log.info("Check for available CEs for VO", vo)
    totalResult = S_OK({})
    message = ''

    mainResult = getBdiiCEInfo(vo, host=self.host, glue2=self.glue2Only)
    if not mainResult['OK']:
      self.log.error("Failed getting information from default bdii", mainResult['Message'])
      message = mainResult['Message']

    for bdii in reversed(self.alternativeBDIIs):
      resultAlt = getBdiiCEInfo(vo, host=bdii, glue2=self.glue2Only)
      if resultAlt['OK']:
        totalResult['Value'].update(resultAlt['Value'])
      else:
        self.log.error("Failed getting information from %s " % bdii, resultAlt['Message'])
        message = (message + "\n" + resultAlt['Message']).strip()

    for glue2URL in self.glue2URLs:
      if self.glue2Only:
        break
      resultGlue2 = getBdiiCEInfo(vo, host=glue2URL, glue2=True)
      if resultGlue2['OK']:
        totalResult['Value'].update(resultGlue2['Value'])
      else:
        self.log.error("Failed getting GLUE2 information for", "%s, %s: %s" %
                       (glue2URL, vo, resultGlue2['Message']))
        message = (message + "\n" + resultGlue2['Message']).strip()

    if mainResult['OK']:
      totalResult['Value'].update(mainResult['Value'])

    if not totalResult['Value'] and message:  # Dict is empty and we have an error message
      self.log.error("Error during BDII request", message)
      totalResult = S_ERROR(message)
    else:
      self.voBdiiCEDict[vo] = totalResult['Value']
      self.__purgeSites(totalResult['Value'])

    return totalResult

  def __updateCEs(self):
    """ Update the Site/CE/queue settings in the CS if they were changed in the BDII
    """

    bdiiChangeSet = set()

    for vo in self.voName:
      result = self.__getBdiiCEInfo(vo)
      if not result['OK']:
        continue
      ceBdiiDict = result['Value']
      result = getSiteUpdates(vo, bdiiInfo=ceBdiiDict, log=self.log)
      if not result['OK']:
        continue
      bdiiChangeSet = bdiiChangeSet.union(result['Value'])

    # We have collected all the changes, consolidate VO settings
    result = self.__updateCS(bdiiChangeSet)
    return result

  def __purgeSites(self, ceBdiiDict):
    """Remove all sites that are not in self.selectedSites.

    Modifies the ceBdiiDict!
    """
    if not self.selectedSites:
      return
    for site in list(ceBdiiDict):
      ces = list(ceBdiiDict[site]['CEs'])
      if not ces:
        self.log.error("No CE information for site:", site)
        continue
      siteInCS = 'Not_In_CS'
      for ce in ces:
        res = getCESiteMapping(ce)
        if not res['OK']:
          self.log.error("Failed to get DIRAC site name for ce", "%s: %s" % (ce, res['Message']))
          continue
        # if the ce is not in the CS the returned value will be empty
        if ce in res['Value']:
          siteInCS = res['Value'][ce]
          break
      self.log.debug("Checking site %s (%s), aka %s" % (site, ces, siteInCS))
      if siteInCS in self.selectedSites:
        continue
      self.log.info("Dropping site %s, aka %s" % (site, siteInCS))
      ceBdiiDict.pop(site)
    return

  def __updateCS(self, bdiiChangeSet):

    queueVODict = {}
    changeSet = set()
    for entry in bdiiChangeSet:
      section, option, _value, new_value = entry
      if option == "VO":
        queueVODict.setdefault(section, set())
        queueVODict[section] = queueVODict[section].union(set(new_value.split(',')))
      else:
        changeSet.add(entry)
    for section, VOs in queueVODict.items():  # can be an iterator
      changeSet.add((section, 'VO', '', ','.join(VOs)))

    if changeSet:
      changeList = sorted(changeSet)
      body = '\n'.join(["%s/%s %s -> %s" % entry for entry in changeList])
      if body and self.addressTo and self.addressFrom:
        notification = NotificationClient()
        result = notification.sendMail(self.addressTo, self.subject, body, self.addressFrom, localAttempt=False,
                                       avoidSpam=True)

      if body:
        self.log.info('The following configuration changes were detected:')
        self.log.info(body)

      for section, option, value, new_value in changeSet:
        if value == 'Unknown' or not value:
          self.csAPI.setOption(cfgPath(section, option), new_value)
        else:
          self.csAPI.modifyValue(cfgPath(section, option), new_value)

      if self.dryRun:
        self.log.info("Dry Run: CS won't be updated")
        self.csAPI.showDiff()
      else:
        result = self.csAPI.commit()
        if not result['OK']:
          self.log.error("Error while committing to CS", result['Message'])
        else:
          self.log.info("Successfully committed %d changes to CS" % len(changeList))
        return result
    else:
      self.log.info("No changes found")
      return S_OK()
예제 #34
0
def checkUnusedSEs():

    global vo, dry

    result = getGridSRMs(vo, unUsed=True)
    if not result["OK"]:
        gLogger.error("Failed to look up SRMs in BDII", result["Message"])
    siteSRMDict = result["Value"]

    # Evaluate VOs
    result = getVOs()
    if result["OK"]:
        csVOs = set(result["Value"])
    else:
        csVOs = {vo}

    changeSetFull = set()

    for site in siteSRMDict:
        for gridSE in siteSRMDict[site]:
            changeSet = set()
            seDict = siteSRMDict[site][gridSE]["SE"]
            srmDict = siteSRMDict[site][gridSE]["SRM"]
            # Check the SRM version
            version = srmDict.get("GlueServiceVersion", "")
            if not (version and version.startswith("2")):
                gLogger.debug("Skipping SRM service with version %s" % version)
                continue
            result = getDIRACSiteName(site)
            if not result["OK"]:
                gLogger.notice("Unused se %s is detected at unused site %s" % (gridSE, site))
                gLogger.notice("Consider adding site %s to the DIRAC CS" % site)
                continue
            diracSites = result["Value"]
            yn = raw_input(
                "\nDo you want to add new SRM SE %s at site(s) %s ? default yes [yes|no]: " % (gridSE, str(diracSites))
            )
            if not yn or yn.lower().startswith("y"):
                if len(diracSites) > 1:
                    prompt = "Which DIRAC site the new SE should be attached to ?"
                    for i, s in enumerate(diracSites):
                        prompt += "\n[%d] %s" % (i, s)
                    prompt += "\nEnter your choice number: "
                    inp = raw_input(prompt)
                    try:
                        ind = int(inp)
                    except:
                        gLogger.notice("Can not interpret your choice: %s, try again later" % inp)
                        continue
                    diracSite = diracSites[ind]
                else:
                    diracSite = diracSites[0]

                domain, siteName, country = diracSite.split(".")
                recName = "%s-disk" % siteName
                inp = raw_input("Give a DIRAC name to the grid SE %s, default %s : " % (gridSE, recName))
                diracSEName = inp
                if not inp:
                    diracSEName = recName

                gLogger.notice("Adding new SE %s at site %s" % (diracSEName, diracSite))
                seSection = cfgPath("/Resources/StorageElements", diracSEName)
                changeSet.add((seSection, "BackendType", seDict.get("GlueSEImplementationName", "Unknown")))
                changeSet.add((seSection, "Description", seDict.get("GlueSEName", "Unknown")))
                bdiiVOs = set(
                    [re.sub("^VO:", "", rule) for rule in srmDict.get("GlueServiceAccessControlBaseRule", [])]
                )
                seVOs = csVOs.intersection(bdiiVOs)
                changeSet.add((seSection, "VO", ",".join(seVOs)))
                accessSection = cfgPath(seSection, "AccessProtocol.1")
                changeSet.add((accessSection, "Protocol", "srm"))
                changeSet.add((accessSection, "PluginName", "SRM2"))
                endPoint = srmDict.get("GlueServiceEndpoint", "")
                host = urlparse(endPoint).hostname
                port = urlparse(endPoint).port
                changeSet.add((accessSection, "Host", host))
                changeSet.add((accessSection, "Port", port))
                changeSet.add((accessSection, "Access", "remote"))
                voPathSection = cfgPath(accessSection, "VOPath")
                if "VOPath" in seDict:
                    path = seDict["VOPath"]
                    voFromPath = os.path.basename(path)
                    if voFromPath != diracVO:
                        gLogger.notice("\n!!! Warning: non-conventional VO path: %s\n" % path)
                        changeSet.add((voPathSection, diracVO, path))
                    path = os.path.dirname(path)
                else:
                    # Try to guess the Path
                    domain = ".".join(host.split(".")[-2:])
                    path = "/dpm/%s/home" % domain
                changeSet.add((accessSection, "Path", path))
                changeSet.add((accessSection, "SpaceToken", ""))
                changeSet.add((accessSection, "WSUrl", "/srm/managerv2?SFN="))

                gLogger.notice("SE %s will be added with the following parameters" % diracSEName)
                changeList = list(changeSet)
                changeList.sort()
                for entry in changeList:
                    gLogger.notice(entry)
                yn = raw_input("Do you want to add new SE %s ? default yes [yes|no]: " % diracSEName)
                if not yn or yn.lower().startswith("y"):
                    changeSetFull = changeSetFull.union(changeSet)

    if dry:
        if changeSetFull:
            gLogger.notice("Skipping commit of the new SE data in a dry run")
        else:
            gLogger.notice("No new SE to be added")
        return S_OK()

    if changeSetFull:
        csAPI = CSAPI()
        csAPI.initialize()
        result = csAPI.downloadCSData()
        if not result["OK"]:
            gLogger.error("Failed to initialize CSAPI object", result["Message"])
            DIRACExit(-1)
        changeList = list(changeSetFull)
        changeList.sort()
        for section, option, value in changeList:
            csAPI.setOption(cfgPath(section, option), value)

        yn = raw_input("New SE data is accumulated\n Do you want to commit changes to CS ? default yes [yes|no]: ")
        if not yn or yn.lower().startswith("y"):
            result = csAPI.commit()
            if not result["OK"]:
                gLogger.error("Error while commit to CS", result["Message"])
            else:
                gLogger.notice("Successfully committed %d changes to CS" % len(changeSetFull))
    else:
        gLogger.notice("No new SE to be added")

    return S_OK()