示例#1
0
文件: PDP.py 项目: remenska/DIRAC
class PDP:
  """
  PDP = Policy Decision Point.

  Used to invoke policies and to take decision based on the polict results combination.
  """

#############################################################################

  def __init__(self, VOExtension, granularity = None, name = None, status = None, formerStatus = None,
               reason = None, siteType = None, serviceType = None, resourceType = None,
               useNewRes = False):
    """
    PDP (Policy Decision Point) initialization

    :params:
      :attr:`VOExtension`: string - VO extension (e.g. 'LHCb')

      :attr:`granularity`: string - a ValidRes

      :attr:`name`: string - name (e.g. of a site)

      :attr:`status`: string - status

      :attr:`formerStatus`: string - former status

      :attr:`reason`: string - optional reason for last status change

      :attr:`siteType`: string - optional site type

      :attr:`serviceType`: string - optional service type

      :attr:`resourceType`: string - optional resource type
    """

    self.VOExtension = VOExtension

    self.__granularity  = assignOrRaise(granularity, ValidRes, InvalidRes, self, self.__init__)
    self.__name         = name
    self.__status       = assignOrRaise(status, ValidStatus, InvalidStatus, self, self.__init__)
    self.__formerStatus = assignOrRaise(formerStatus, ValidStatus, InvalidStatus, self, self.__init__)
    self.__reason       = reason
    self.__siteType     = assignOrRaise(siteType, ValidSiteType, InvalidSiteType, self, self.__init__)
    self.__serviceType  = assignOrRaise(serviceType, ValidServiceType, InvalidServiceType, self, self.__init__)
    self.__resourceType = assignOrRaise(resourceType, ValidResourceType, InvalidResourceType, self, self.__init__)

    cc      = CommandCaller()
    self.pc = PolicyCaller(cc)

    self.useNewRes = useNewRes

    self.args      = None
    self.policy    = None
    self.knownInfo = None
    self.ig        = None


#############################################################################

  def takeDecision(self, policyIn=None, argsIn=None, knownInfo=None):
    """ PDP MAIN FUNCTION

        decides policies that have to be applied, based on

        __granularity,

        __name,

        __status,

        __formerStatus

        __reason

        If more than one policy is evaluated, results are combined.

        Logic for combination: a conservative approach is followed
        (i.e. if a site should be banned for at least one policy, that's what is returned)

        returns:

          { 'PolicyType': a policyType (in a string),
            'Action': True|False,
            'Status': 'Active'|'Probing'|'Banned',
            'Reason': a reason
            'EndDate: datetime.datetime (in a string)}
    """

    self.args = argsIn
    self.policy = policyIn
    self.knownInfo = knownInfo


    self.ig = InfoGetter(self.VOExtension)

    EVAL = self.ig.getInfoToApply(('policy', 'policyType'),
                                  granularity = self.__granularity,
                                  status = self.__status,
                                  formerStatus = self.__formerStatus,
                                  siteType = self.__siteType,
                                  serviceType = self.__serviceType,
                                  resourceType = self.__resourceType,
                                  useNewRes = self.useNewRes)

    policyCombinedResultsList = []

    for policyGroup in EVAL:

      policyType = policyGroup['PolicyType']

      if self.policy is not None:
        # Only the policy provided will be evaluated
        # FIXME: Check that the policies are valid.
        singlePolicyResults = self.policy.evaluate()
      else:
        if policyGroup['Policies'] is None:
          return {'SinglePolicyResults' : [],
                  'PolicyCombinedResult' : [{'PolicyType': policyType,
                                             'Action': False,
                                             'Reason':'No policy results'}]}
        else:
          singlePolicyResults = self._invocation(self.VOExtension, self.__granularity,
                                                 self.__name, self.__status, self.policy,
                                                 self.args, policyGroup['Policies'])

      policyCombinedResults = self._policyCombination(singlePolicyResults)

      if not policyCombinedResults:
        return { 'SinglePolicyResults': singlePolicyResults,
                 'PolicyCombinedResult': [] }


      #
      # policy results communication
      #

      newstatus = policyCombinedResults['Status']

      if newstatus != self.__status: # Policies satisfy
        reason = policyCombinedResults['Reason']
        newPolicyType = self.ig.getNewPolicyType(self.__granularity, newstatus)
        for npt in newPolicyType:
          if npt not in policyType:
            policyType.append(npt)
        decision = { 'PolicyType': policyType, 'Action': True, 'Status': newstatus, 'Reason': reason }
        if policyCombinedResults.has_key('EndDate'):
          decision['EndDate'] = policyCombinedResults['EndDate']
        policyCombinedResultsList.append(decision)

      else: # Policies does not satisfy
        reason = policyCombinedResults['Reason']
        decision = { 'PolicyType': policyType, 'Action': False, 'Reason': reason }
        if policyCombinedResults.has_key('EndDate'):
          decision['EndDate'] = policyCombinedResults['EndDate']
        policyCombinedResultsList.append(decision)

    res = { 'SinglePolicyResults' : singlePolicyResults,
           'PolicyCombinedResult' : policyCombinedResultsList }

    return res

#############################################################################

  def _invocation(self, VOExtension, granularity, name, status, policy, args, policies):
    """ One by one, use the PolicyCaller to invoke the policies, and putting
        their results in `policyResults`. When the status is `Unknown`, invokes
        `self.__useOldPolicyRes`.
    """

    policyResults = []

    for p in policies:
      pName = p['Name']
      pModule = p['Module']
      extraArgs = p['args']
      commandIn = p['commandIn']
      res = self.pc.policyInvocation(VOExtension, granularity = granularity, name = name,
                                     status = status, policy = policy, args = args, pName = pName,
                                     pModule = pModule, extraArgs = extraArgs, commandIn = commandIn)

      # If res is empty, return immediately
      if not res: return policyResults

      if not res.has_key('Status'):
        print("\n\n Policy result " + str(res) + " does not return 'Status'\n\n")
        raise TypeError

      # Else
      if res['Status'] == 'Unknown':
        res = self.__useOldPolicyRes(name = name, policyName = pName)

      if res['Status'] == 'NeedConfirmation':
        pName = p['ConfirmationPolicy']
        triggeredPolicy = self.ig.C_Policies[pName]
        pModule = triggeredPolicy['module']
        extraArgs = triggeredPolicy['args']
        commandIn = triggeredPolicy['commandIn']
        res = self.pc.policyInvocation(VOExtension, granularity = granularity, name = name,
                                       status = status, policy = policy, args = args, pName = pName,
                                       pModule = pModule, extraArgs = extraArgs, commandIn = commandIn)

      if res['Status'] not in ('Error', 'Unknown', 'NeedConfirmation'):
        policyResults.append(res)

    return policyResults

#############################################################################

  def _policyCombination(self, policies):
    """
    * Compute a new status, and store it in variable newStatus, of type integer.
    * Make a list of policies that have the worst result.
    * Concatenate the Reason fields
    * Take the first EndDate field that exists (FIXME: Do something more clever)
    * Finally, return the result
    """
    if policies == []: return {}

    policies.sort(key=value_of_policy)
    newStatus = -1 # First, set an always invalid status

    try:
      # We are in a special status, maybe forbidden transitions
      prio, access_list, gofun = statesInfo[self.__status]
      if access_list != set():
        # Restrictions on transitions, checking if one is suitable:
        for p in policies:
          if value_of_policy(p) in access_list:
            newStatus = value_of_policy(p)
            break

        # No status from policies suitable, applying stategy and
        # returning result.
        if newStatus == -1:
          newStatus = gofun(access_list)
          return { 'Status': status_of_value(newStatus), 'Reason': 'Status forced by PDP' }

      else:
        # Special Status, but no restriction on transitions
        newStatus = value_of_policy(policies[0])

    except KeyError:
      # We are in a "normal" status: All transitions are possible.
      newStatus = value_of_policy(policies[0])

    # At this point, a new status has been chosen. newStatus is an
    # integer.

    worstPolicies = [p for p in policies if value_of_policy(p) == newStatus]

    # Concatenate reasons
    def getReason(p):
      try:
        res = p['Reason']
      except KeyError:
        res = ''
      return res

    worstPoliciesReasons = [getReason(p) for p in worstPolicies]

    def catRes(x, y):
      if x and y : return x + ' |###| ' + y
      elif x or y:
        if x: return x
        else: return y
      else       : return ''

    concatenatedRes = reduce(catRes, worstPoliciesReasons, '')

    # Handle EndDate
    endDatePolicies = [p for p in worstPolicies if p.has_key('EndDate')]

    # Building and returning result
    res = {}
    res['Status'] = status_of_value(newStatus)
    if concatenatedRes != '': res['Reason']  = concatenatedRes
    if endDatePolicies != []: res['EndDate'] = endDatePolicies[0]['EndDate']
    return res

#############################################################################

  def __useOldPolicyRes(self, name, policyName):
    """ Use the RSS Service to get an old policy result.
        If such result is older than 2 hours, it returns {'Status':'Unknown'}
    """

    from DIRAC.Core.DISET.RPCClient import RPCClient
    rsS = RPCClient("ResourceStatus/ResourceManagement")

    res = rsS.getPolicyRes(name, policyName, True)
    if not res['OK']:
      raise RSSException, where(self, self.__useOldPolicyRes) + ' Could not get a policy result'

    res = res['Value']

    if res == []:
      return {'Status':'Unknown'}

    oldStatus = res[0]
    oldReason = res[1]
    lastCheckTime = res[2]

    if ( lastCheckTime + datetime.timedelta(hours = 2) ) < datetime.datetime.utcnow():
      return {'Status':'Unknown'}

    result = {}

    result['Status'] = oldStatus
    result['Reason'] = oldReason
    result['OLD'] = True
    result['PolicyName'] = policyName

    return result
示例#2
0
文件: PDP.py 项目: bmb/DIRAC
class PDP:
  """
    The PDP (Policy Decision Point) module is used to:
    1. Decides which policies have to be applied.
    2. Invokes an evaluation of the policies, and returns the result (to a PEP)
  """

  def __init__( self, **clients ):
    '''
      Constructor. Defines members that will be used later on.
    '''
    
    cc                  = CommandCaller()
    self.clients        = clients
    self.pCaller        = PolicyCaller( cc, **clients )
    self.iGetter        = InfoGetter()

    self.__granularity  = None
    self.__name         = None
    self.__statusType   = None
    self.__status       = None
    self.__formerStatus = None
    self.__reason       = None
    self.__siteType     = None
    self.__serviceType  = None
    self.__resourceType = None
    self.__useNewRes    = None
        

  def setup( self, granularity = None, name = None, statusType = None,
             status = None, formerStatus = None, reason = None, siteType = None,
             serviceType = None, resourceType = None, useNewRes = False ):
    """
    PDP (Policy Decision Point) initialization

    :params:
      :attr:`granularity`: string - a ValidElement
      :attr:`name`: string - name (e.g. of a site)
      :attr:`status`: string - status
      :attr:`formerStatus`: string - former status
      :attr:`reason`: string - optional reason for last status change
      :attr:`siteType`: string - optional site type
      :attr:`serviceType`: string - optional service type
      :attr:`resourceType`: string - optional resource type
    """

    self.__granularity  = granularity
    self.__name         = name
    self.__statusType   = statusType
    self.__status       = status
    self.__formerStatus = formerStatus
    self.__reason       = reason
    self.__siteType     = siteType
    self.__serviceType  = serviceType
    self.__resourceType = resourceType
    self.__useNewRes    = useNewRes



################################################################################

  def takeDecision( self, policyIn = None, argsIn = None, knownInfo = None ):
    """ PDP MAIN FUNCTION

        decides policies that have to be applied, based on

        __granularity,

        __name,

        __status,

        __formerStatus

        __reason

        If more than one policy is evaluated, results are combined.

        Logic for combination: a conservative approach is followed
        (i.e. if a site should be banned for at least one policy, that's what is returned)

        returns:

          { 'PolicyType': a policyType (in a string),
            'Action': True|False,
            'Status': 'Active'|'Probing'|'Banned',
            'Reason': a reason
            'EndDate: datetime.datetime (in a string)}
    """

    polToEval = self.iGetter.getInfoToApply( ( 'policy', 'policyType' ),
                                        granularity  = self.__granularity,
                                        statusType   = self.__statusType,
                                        status       = self.__status,
                                        formerStatus = self.__formerStatus,
                                        siteType     = self.__siteType,
                                        serviceType  = self.__serviceType,
                                        resourceType = self.__resourceType,
                                        useNewRes    = self.__useNewRes )

    policyType = polToEval[ 'PolicyType' ] # type: generator

    if policyIn:
      # Only the policy provided will be evaluated
      # FIXME: Check that the policies are valid.
      singlePolicyResults = policyIn.evaluate()

    else:
      singlePolicyResults = self._invocation( self.__granularity,
                                              self.__name, self.__status, policyIn,
                                              argsIn, polToEval['Policies'] )

    policyCombinedResults = self._policyCombination( singlePolicyResults )

    if policyCombinedResults == {}:
      policyCombinedResults[ 'Action' ]     = False
      policyCombinedResults[ 'Reason' ]     = 'No policy results'
      policyCombinedResults[ 'PolicyType' ] = policyType

    if policyCombinedResults.has_key( 'Status' ):
      newstatus = policyCombinedResults[ 'Status' ]

      if newstatus != self.__status: # Policies satisfy
        newPolicyType = self.iGetter.getNewPolicyType( self.__granularity, newstatus )
        policyType    = set( policyType ) & set( newPolicyType )
        
        policyCombinedResults[ 'Action' ] = True

      else:                          # Policies does not satisfy
        policyCombinedResults[ 'Action' ] = False

      policyCombinedResults[ 'PolicyType' ] = policyType

    return { 'SinglePolicyResults'  : singlePolicyResults,
             'PolicyCombinedResult' : policyCombinedResults }

################################################################################

  def _invocation( self, granularity, name, status, policy, args, policies ):
    '''
      One by one, use the PolicyCaller to invoke the policies, and putting
      their results in `policyResults`. When the status is `Unknown`, invokes
      `self.__useOldPolicyRes`. Always returns a list, possibly empty.
    '''

    policyResults = []

    for pol in policies:
      
      pName     = pol[ 'Name' ]
      pModule   = pol[ 'Module' ]
      extraArgs = pol[ 'args' ]
      commandIn = pol[ 'commandIn' ]
      
      res = self.pCaller.policyInvocation( granularity = granularity, name = name,
                                           status = status, policy = policy, 
                                           args = args, pName = pName,
                                           pModule = pModule, extraArgs = extraArgs, 
                                           commandIn = commandIn )

      # If res is empty, return immediately
      if not res: 
        return policyResults

      if not res.has_key( 'Status' ):
        print('\n\n Policy result ' + str(res) + ' does not return "Status"\n\n')
        raise TypeError

      # Else
      if res[ 'Status' ] == 'Unknown':
        res = self.__useOldPolicyRes( name = name, policyName = pName )

      if res[ 'Status' ] not in ( 'Error', 'Unknown' ):
        policyResults.append( res )
      else:
        gLogger.warn( res )      
      
    return policyResults

################################################################################

  def _policyCombination( self, pol_results ):
    '''
    INPUT: list type
    OUTPUT: dict type
    * Compute a new status, and store it in variable newStatus, of type integer.
    * Make a list of policies that have the worst result.
    * Concatenate the Reason fields
    * Take the first EndDate field that exists (FIXME: Do something more clever)
    * Finally, return the result
    '''
    if pol_results == []: 
      return {}

    pol_results.sort( key=Status.value_of_policy )
    newStatus = -1 # First, set an always invalid status

    try:
      # We are in a special status, maybe forbidden transitions
      _prio, access_list, gofun = Status.statesInfo[ self.__status ]
      if access_list != set():
        # Restrictions on transitions, checking if one is suitable:
        for polRes in pol_results:
          if Status.value_of_policy( polRes ) in access_list:
            newStatus = Status.value_of_policy( polRes )
            break

        # No status from policies suitable, applying stategy and
        # returning result.
        if newStatus == -1:
          newStatus = gofun( access_list )
          return { 'Status': Status.status_of_value( newStatus ),
                   'Reason': 'Status forced by PDP' }

      else:
        # Special Status, but no restriction on transitions
        newStatus = Status.value_of_policy( pol_results[ 0 ] )

    except KeyError:
      # We are in a "normal" status: All transitions are possible.
      newStatus = Status.value_of_policy( pol_results[ 0 ] )

    # At this point, a new status has been chosen. newStatus is an
    # integer.

    worstResults = [ p for p in pol_results
                     if Status.value_of_policy( p ) == newStatus ]

    # Concatenate reasons
    def getReason( pol ):
      try:
        res = pol[ 'Reason' ]
      except KeyError:
        res = ''
      return res

    worstResultsReasons = [ getReason( p ) for p in worstResults ]

    def catRes( xVal, yVal ):
      '''
        Concatenate xVal and yVal.
      '''
      if xVal and yVal : 
        return xVal + ' |###| ' + yVal
      elif xVal or yVal:
        if xVal: 
          return xVal
        else: 
          return yVal
      else: 
        return ''

    concatenatedRes = reduce( catRes, worstResultsReasons, '' )

    # Handle EndDate
    endDatePolicies = [ p for p in worstResults if p.has_key( 'EndDate' ) ]

    # Building and returning result
    res = {}
    res[ 'Status' ] = Status.status_of_value( newStatus )
    if concatenatedRes != '': 
      res[ 'Reason' ]  = concatenatedRes
    if endDatePolicies != []: 
      res[ 'EndDate' ] = endDatePolicies[ 0 ][ 'EndDate' ]
    return res

################################################################################

  def __useOldPolicyRes( self, name, policyName ):
    '''
     Use the RSS Service to get an old policy result.
     If such result is older than 2 hours, it returns {'Status':'Unknown'}
    '''
    res = self.clients[ 'ResourceManagementClient' ].getPolicyResult( name = name, policyName = policyName )
    
    if not res[ 'OK' ]:
      return { 'Status' : 'Unknown' }
    
    res = res[ 'Value' ]

    if res == []:
      return { 'Status' : 'Unknown' }

    res = res[ 0 ]

    oldStatus     = res[ 5 ]
    oldReason     = res[ 6 ]
    lastCheckTime = res[ 8 ]

    if ( lastCheckTime + datetime.timedelta(hours = 2) ) < datetime.datetime.utcnow():
      return { 'Status' : 'Unknown' }

    result = {}

    result[ 'Status' ]     = oldStatus
    result[ 'Reason' ]     = oldReason
    result[ 'OLD' ]        = True
    result[ 'PolicyName' ] = policyName

    return result

################################################################################
#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF
示例#3
0
class PDP:
    """
  PDP = Policy Decision Point.

  Used to invoke policies and to take decision based on the polict results combination.
  """

    #############################################################################

    def __init__(self,
                 VOExtension,
                 granularity=None,
                 name=None,
                 status=None,
                 formerStatus=None,
                 reason=None,
                 siteType=None,
                 serviceType=None,
                 resourceType=None,
                 useNewRes=False):
        """
    PDP (Policy Decision Point) initialization

    :params:
      :attr:`VOExtension`: string - VO extension (e.g. 'LHCb')

      :attr:`granularity`: string - a ValidRes

      :attr:`name`: string - name (e.g. of a site)

      :attr:`status`: string - status

      :attr:`formerStatus`: string - former status

      :attr:`reason`: string - optional reason for last status change

      :attr:`siteType`: string - optional site type

      :attr:`serviceType`: string - optional service type

      :attr:`resourceType`: string - optional resource type
    """

        self.VOExtension = VOExtension

        self.__granularity = assignOrRaise(granularity, ValidRes, InvalidRes,
                                           self, self.__init__)
        self.__name = name
        self.__status = assignOrRaise(status, ValidStatus, InvalidStatus, self,
                                      self.__init__)
        self.__formerStatus = assignOrRaise(formerStatus, ValidStatus,
                                            InvalidStatus, self, self.__init__)
        self.__reason = reason
        self.__siteType = assignOrRaise(siteType, ValidSiteType,
                                        InvalidSiteType, self, self.__init__)
        self.__serviceType = assignOrRaise(serviceType, ValidServiceType,
                                           InvalidServiceType, self,
                                           self.__init__)
        self.__resourceType = assignOrRaise(resourceType, ValidResourceType,
                                            InvalidResourceType, self,
                                            self.__init__)

        cc = CommandCaller()
        self.pc = PolicyCaller(cc)

        self.useNewRes = useNewRes

        self.args = None
        self.policy = None
        self.knownInfo = None
        self.ig = None

#############################################################################

    def takeDecision(self, policyIn=None, argsIn=None, knownInfo=None):
        """ PDP MAIN FUNCTION

        decides policies that have to be applied, based on

        __granularity,

        __name,

        __status,

        __formerStatus

        __reason

        If more than one policy is evaluated, results are combined.

        Logic for combination: a conservative approach is followed
        (i.e. if a site should be banned for at least one policy, that's what is returned)

        returns:

          { 'PolicyType': a policyType (in a string),
            'Action': True|False,
            'Status': 'Active'|'Probing'|'Banned',
            'Reason': a reason
            'EndDate: datetime.datetime (in a string)}
    """

        self.args = argsIn
        self.policy = policyIn
        self.knownInfo = knownInfo

        self.ig = InfoGetter(self.VOExtension)

        EVAL = self.ig.getInfoToApply(('policy', 'policyType'),
                                      granularity=self.__granularity,
                                      status=self.__status,
                                      formerStatus=self.__formerStatus,
                                      siteType=self.__siteType,
                                      serviceType=self.__serviceType,
                                      resourceType=self.__resourceType,
                                      useNewRes=self.useNewRes)

        for policyGroup in EVAL:

            policyType = policyGroup['PolicyType']

            if self.policy is not None:
                # Only the policy provided will be evaluated
                # FIXME: Check that the policies are valid.
                singlePolicyResults = self.policy.evaluate()
            else:
                if policyGroup['Policies'] is None:
                    return {
                        'SinglePolicyResults': [],
                        'PolicyCombinedResult': {
                            'PolicyType': policyType,
                            'Action': False,
                            'Reason': 'No policy results'
                        }
                    }
                else:
                    singlePolicyResults = self._invocation(
                        self.VOExtension, self.__granularity, self.__name,
                        self.__status, self.policy, self.args,
                        policyGroup['Policies'])

            policyCombinedResults = self._policyCombination(
                singlePolicyResults)
            assert (type(policyCombinedResults) == dict)

            if not policyCombinedResults:
                return {
                    'SinglePolicyResults': singlePolicyResults,
                    'PolicyCombinedResult': {}
                }

            #
            # policy results communication
            #

            newstatus = policyCombinedResults['Status']

            if newstatus != self.__status:  # Policies satisfy
                reason = policyCombinedResults['Reason']
                newPolicyType = self.ig.getNewPolicyType(
                    self.__granularity, newstatus)
                for npt in newPolicyType:
                    if npt not in policyType:
                        policyType.append(npt)
                decision = {
                    'PolicyType': policyType,
                    'Action': True,
                    'Status': newstatus,
                    'Reason': reason
                }
                if policyCombinedResults.has_key('EndDate'):
                    decision['EndDate'] = policyCombinedResults['EndDate']

            else:  # Policies does not satisfy
                reason = policyCombinedResults['Reason']
                decision = {
                    'PolicyType': policyType,
                    'Action': False,
                    'Reason': reason
                }
                if policyCombinedResults.has_key('EndDate'):
                    decision['EndDate'] = policyCombinedResults['EndDate']

        res = {
            'SinglePolicyResults': singlePolicyResults,
            'PolicyCombinedResult': decision
        }

        assert (type(res) == dict)
        return res

#############################################################################

    def _invocation(self, VOExtension, granularity, name, status, policy, args,
                    policies):
        """ One by one, use the PolicyCaller to invoke the policies, and putting
        their results in `policyResults`. When the status is `Unknown`, invokes
        `self.__useOldPolicyRes`.
    """

        policyResults = []

        for p in policies:
            pName = p['Name']
            pModule = p['Module']
            extraArgs = p['args']
            commandIn = p['commandIn']
            res = self.pc.policyInvocation(VOExtension,
                                           granularity=granularity,
                                           name=name,
                                           status=status,
                                           policy=policy,
                                           args=args,
                                           pName=pName,
                                           pModule=pModule,
                                           extraArgs=extraArgs,
                                           commandIn=commandIn)

            # If res is empty, return immediately
            if not res: return policyResults

            if not res.has_key('Status'):
                print("\n\n Policy result " + str(res) +
                      " does not return 'Status'\n\n")
                raise TypeError

            # Else
            if res['Status'] == 'Unknown':
                res = self.__useOldPolicyRes(name=name, policyName=pName)

            if res['Status'] == 'NeedConfirmation':
                pName = p['ConfirmationPolicy']
                triggeredPolicy = self.ig.C_Policies[pName]
                pModule = triggeredPolicy['module']
                extraArgs = triggeredPolicy['args']
                commandIn = triggeredPolicy['commandIn']
                res = self.pc.policyInvocation(VOExtension,
                                               granularity=granularity,
                                               name=name,
                                               status=status,
                                               policy=policy,
                                               args=args,
                                               pName=pName,
                                               pModule=pModule,
                                               extraArgs=extraArgs,
                                               commandIn=commandIn)

            if res['Status'] not in ('Error', 'Unknown', 'NeedConfirmation'):
                policyResults.append(res)

        return policyResults

#############################################################################

    def _policyCombination(self, pol_results):
        """
    INPUT: list type
    OUTPUT: dict type
    * Compute a new status, and store it in variable newStatus, of type integer.
    * Make a list of policies that have the worst result.
    * Concatenate the Reason fields
    * Take the first EndDate field that exists (FIXME: Do something more clever)
    * Finally, return the result
    """
        if pol_results == []: return {}

        pol_results.sort(key=Status.value_of_policy)
        newStatus = -1  # First, set an always invalid status

        try:
            # We are in a special status, maybe forbidden transitions
            _prio, access_list, gofun = Status.statesInfo[self.__status]
            if access_list != set():
                # Restrictions on transitions, checking if one is suitable:
                for p in pol_results:
                    if Status.value_of_policy(p) in access_list:
                        newStatus = Status.value_of_policy(p)
                        break

                # No status from policies suitable, applying stategy and
                # returning result.
                if newStatus == -1:
                    newStatus = gofun(access_list)
                    return {
                        'Status': Status.status_of_value(newStatus),
                        'Reason': 'Status forced by PDP'
                    }

            else:
                # Special Status, but no restriction on transitions
                newStatus = Status.value_of_policy(pol_results[0])

        except KeyError:
            # We are in a "normal" status: All transitions are possible.
            newStatus = Status.value_of_policy(pol_results[0])

        # At this point, a new status has been chosen. newStatus is an
        # integer.

        worstResults = [
            p for p in pol_results if Status.value_of_policy(p) == newStatus
        ]

        # Concatenate reasons
        def getReason(p):
            try:
                res = p['Reason']
            except KeyError:
                res = ''
            return res

        worstResultsReasons = [getReason(p) for p in worstResults]

        def catRes(x, y):
            if x and y: return x + ' |###| ' + y
            elif x or y:
                if x: return x
                else: return y
            else: return ''

        concatenatedRes = reduce(catRes, worstResultsReasons, '')

        # Handle EndDate
        endDatePolicies = [p for p in worstResults if p.has_key('EndDate')]

        # Building and returning result
        res = {}
        res['Status'] = Status.status_of_value(newStatus)
        if concatenatedRes != '': res['Reason'] = concatenatedRes
        if endDatePolicies != []:
            res['EndDate'] = endDatePolicies[0]['EndDate']
        return res

#############################################################################

    def __useOldPolicyRes(self, name, policyName):
        """ Use the RSS Service to get an old policy result.
        If such result is older than 2 hours, it returns {'Status':'Unknown'}
    """

        from DIRAC.Core.DISET.RPCClient import RPCClient
        rsS = RPCClient("ResourceStatus/ResourceManagement")

        res = rsS.getPolicyRes(name, policyName, True)
        if not res['OK']:
            raise RSSException, where(
                self,
                self.__useOldPolicyRes) + ' Could not get a policy result'

        res = res['Value']

        if res == []:
            return {'Status': 'Unknown'}

        oldStatus = res[0]
        oldReason = res[1]
        lastCheckTime = res[2]

        if (lastCheckTime +
                datetime.timedelta(hours=2)) < datetime.datetime.utcnow():
            return {'Status': 'Unknown'}

        result = {}

        result['Status'] = oldStatus
        result['Reason'] = oldReason
        result['OLD'] = True
        result['PolicyName'] = policyName

        return result
示例#4
0
class PDP:
    """
    The PDP (Policy Decision Point) module is used to:
    1. Decides which policies have to be applied.
    2. Invokes an evaluation of the policies, and returns the result (to a PEP)
  """
    def __init__(self, **clients):
        '''
      Constructor. Defines members that will be used later on.
    '''

        cc = CommandCaller()
        self.clients = clients
        self.pCaller = PolicyCaller(cc, **clients)
        self.iGetter = InfoGetter()

        self.__granularity = None
        self.__name = None
        self.__statusType = None
        self.__status = None
        self.__formerStatus = None
        self.__reason = None
        self.__siteType = None
        self.__serviceType = None
        self.__resourceType = None
        self.__useNewRes = None

    def setup(self,
              granularity=None,
              name=None,
              statusType=None,
              status=None,
              formerStatus=None,
              reason=None,
              siteType=None,
              serviceType=None,
              resourceType=None,
              useNewRes=False):
        """
    PDP (Policy Decision Point) initialization

    :params:
      :attr:`granularity`: string - a ValidElement
      :attr:`name`: string - name (e.g. of a site)
      :attr:`status`: string - status
      :attr:`formerStatus`: string - former status
      :attr:`reason`: string - optional reason for last status change
      :attr:`siteType`: string - optional site type
      :attr:`serviceType`: string - optional service type
      :attr:`resourceType`: string - optional resource type
    """

        self.__granularity = granularity
        self.__name = name
        self.__statusType = statusType
        self.__status = status
        self.__formerStatus = formerStatus
        self.__reason = reason
        self.__siteType = siteType
        self.__serviceType = serviceType
        self.__resourceType = resourceType
        self.__useNewRes = useNewRes

################################################################################

    def takeDecision(self, policyIn=None, argsIn=None, knownInfo=None):
        """ PDP MAIN FUNCTION

        decides policies that have to be applied, based on

        __granularity,

        __name,

        __status,

        __formerStatus

        __reason

        If more than one policy is evaluated, results are combined.

        Logic for combination: a conservative approach is followed
        (i.e. if a site should be banned for at least one policy, that's what is returned)

        returns:

          { 'PolicyType': a policyType (in a string),
            'Action': True|False,
            'Status': 'Active'|'Probing'|'Banned',
            'Reason': a reason
            'EndDate: datetime.datetime (in a string)}
    """

        polToEval = self.iGetter.getInfoToApply(
            ('policy', 'policyType'),
            granularity=self.__granularity,
            statusType=self.__statusType,
            status=self.__status,
            formerStatus=self.__formerStatus,
            siteType=self.__siteType,
            serviceType=self.__serviceType,
            resourceType=self.__resourceType,
            useNewRes=self.__useNewRes)

        policyType = polToEval['PolicyType']  # type: generator

        if policyIn:
            # Only the policy provided will be evaluated
            # FIXME: Check that the policies are valid.
            singlePolicyResults = policyIn.evaluate()

        else:
            singlePolicyResults = self._invocation(self.__granularity,
                                                   self.__name, self.__status,
                                                   policyIn, argsIn,
                                                   polToEval['Policies'])

        policyCombinedResults = self._policyCombination(singlePolicyResults)

        if policyCombinedResults == {}:
            policyCombinedResults['Action'] = False
            policyCombinedResults['Reason'] = 'No policy results'
            policyCombinedResults['PolicyType'] = policyType

        if policyCombinedResults.has_key('Status'):
            newstatus = policyCombinedResults['Status']

            if newstatus != self.__status:  # Policies satisfy
                newPolicyType = self.iGetter.getNewPolicyType(
                    self.__granularity, newstatus)
                policyType = set(policyType) & set(newPolicyType)

                policyCombinedResults['Action'] = True

            else:  # Policies does not satisfy
                policyCombinedResults['Action'] = False

            policyCombinedResults['PolicyType'] = policyType

        return {
            'SinglePolicyResults': singlePolicyResults,
            'PolicyCombinedResult': policyCombinedResults
        }

################################################################################

    def _invocation(self, granularity, name, status, policy, args, policies):
        '''
      One by one, use the PolicyCaller to invoke the policies, and putting
      their results in `policyResults`. When the status is `Unknown`, invokes
      `self.__useOldPolicyRes`. Always returns a list, possibly empty.
    '''

        policyResults = []

        for pol in policies:

            pName = pol['Name']
            pModule = pol['Module']
            extraArgs = pol['args']
            commandIn = pol['commandIn']

            res = self.pCaller.policyInvocation(granularity=granularity,
                                                name=name,
                                                status=status,
                                                policy=policy,
                                                args=args,
                                                pName=pName,
                                                pModule=pModule,
                                                extraArgs=extraArgs,
                                                commandIn=commandIn)

            # If res is empty, return immediately
            if not res:
                return policyResults

            if not res.has_key('Status'):
                print('\n\n Policy result ' + str(res) +
                      ' does not return "Status"\n\n')
                raise TypeError

            # Else
            if res['Status'] == 'Unknown':
                res = self.__useOldPolicyRes(name=name, policyName=pName)

            if res['Status'] not in ('Error', 'Unknown'):
                policyResults.append(res)
            else:
                gLogger.warn(res)

        return policyResults

################################################################################

    def _policyCombination(self, pol_results):
        '''
    INPUT: list type
    OUTPUT: dict type
    * Compute a new status, and store it in variable newStatus, of type integer.
    * Make a list of policies that have the worst result.
    * Concatenate the Reason fields
    * Take the first EndDate field that exists (FIXME: Do something more clever)
    * Finally, return the result
    '''
        if pol_results == []:
            return {}

        pol_results.sort(key=Status.value_of_policy)
        newStatus = -1  # First, set an always invalid status

        try:
            # We are in a special status, maybe forbidden transitions
            _prio, access_list, gofun = Status.statesInfo[self.__status]
            if access_list != set():
                # Restrictions on transitions, checking if one is suitable:
                for polRes in pol_results:
                    if Status.value_of_policy(polRes) in access_list:
                        newStatus = Status.value_of_policy(polRes)
                        break

                # No status from policies suitable, applying stategy and
                # returning result.
                if newStatus == -1:
                    newStatus = gofun(access_list)
                    return {
                        'Status': Status.status_of_value(newStatus),
                        'Reason': 'Status forced by PDP'
                    }

            else:
                # Special Status, but no restriction on transitions
                newStatus = Status.value_of_policy(pol_results[0])

        except KeyError:
            # We are in a "normal" status: All transitions are possible.
            newStatus = Status.value_of_policy(pol_results[0])

        # At this point, a new status has been chosen. newStatus is an
        # integer.

        worstResults = [
            p for p in pol_results if Status.value_of_policy(p) == newStatus
        ]

        # Concatenate reasons
        def getReason(pol):
            try:
                res = pol['Reason']
            except KeyError:
                res = ''
            return res

        worstResultsReasons = [getReason(p) for p in worstResults]

        def catRes(xVal, yVal):
            '''
        Concatenate xVal and yVal.
      '''
            if xVal and yVal:
                return xVal + ' |###| ' + yVal
            elif xVal or yVal:
                if xVal:
                    return xVal
                else:
                    return yVal
            else:
                return ''

        concatenatedRes = reduce(catRes, worstResultsReasons, '')

        # Handle EndDate
        endDatePolicies = [p for p in worstResults if p.has_key('EndDate')]

        # Building and returning result
        res = {}
        res['Status'] = Status.status_of_value(newStatus)
        if concatenatedRes != '':
            res['Reason'] = concatenatedRes
        if endDatePolicies != []:
            res['EndDate'] = endDatePolicies[0]['EndDate']
        return res

################################################################################

    def __useOldPolicyRes(self, name, policyName):
        '''
     Use the RSS Service to get an old policy result.
     If such result is older than 2 hours, it returns {'Status':'Unknown'}
    '''
        res = self.clients['ResourceManagementClient'].getPolicyResult(
            name=name, policyName=policyName)

        if not res['OK']:
            return {'Status': 'Unknown'}

        res = res['Value']

        if res == []:
            return {'Status': 'Unknown'}

        res = res[0]

        oldStatus = res[5]
        oldReason = res[6]
        lastCheckTime = res[8]

        if (lastCheckTime +
                datetime.timedelta(hours=2)) < datetime.datetime.utcnow():
            return {'Status': 'Unknown'}

        result = {}

        result['Status'] = oldStatus
        result['Reason'] = oldReason
        result['OLD'] = True
        result['PolicyName'] = policyName

        return result


################################################################################
#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF#EOF