4.2 Computed Facts

This section includes the following information:

4.2.1 What is a Computed Fact?

There are situations when facts with static or even simple dynamic values don’t provide adequate data to enable implementation of more complex policy constraints. In such cases, Cloud Manager Orchestration can use “computed facts” whose values are dynamically and programmatically calculated using more sophisticated scripted l

A computed fact is meant to be used in a policy’s constraint. When applied through the execution of a Jython thread, the computed fact calculates values on demand and returns them to the Orchestration logic (Job Definition Language or “JDL”) which evaluates the job context and extends the built-in factsets for a Grid Object. This methodology lets you create facts that represent other metrics on the system that are not necessarily available in the default factset.

A computed fact implementation uses the ComputedFactContext class to access the evaluation context. This context contains the Grid Objects that the constraint engine uses to evaluate constraints, so this class could access the current job instance, deployed job, User, Resource, vBridge, and Repository Grid Objects if they are available within the context of the policy constraint consuming the computed fact's value.

Some computed facts are bundled with the product and used to handle more complex scenarios relating to provisioning adapter actions. However, computed facts can also be used by jobs which are not related to provisioning adapters or virtual machines in any way. More specifically, the VM host, vBridge and Repository grid objects are in the context only to evaluate provisioning constraints, such as vmHost, whereasthe Job and Job Instance objects are in the context only to evaluate resource or allocation constraints.

The following computed fact example illustrates how you can use a computed fact to calculate how many instances of the job named “foo” that the user in the current job context is running. The value of this computed fact can be used in a constraint to limit the number of instances of a particular job that a user can run:

  class ActiveFooJobs(ComputedFact):

      def compute(self):
          count = 0
          ctx = self.getContext()
          if ctx == None:
              print "No context"
          else:
              jobInstance = ctx.getJobInstance()
              if jobInstance == None:
                  print "No job instance in context"
              else:
                  activejobs = getMatrix().getActiveJobs()
                  for j in activejobs:
                      state = j.getFact("jobinstance.state.string")
                      if j.getFact("job.id") == "foo" and \
                          j.getFact("user.id") == ctx.getFact("user.id") and \
                          (state == "Running" or state == "Starting"):
                          count+=1
          return count

Example 4-1 Example of a Computed Fact

The following is an actual computed fact used by the xen and vsphere provisioning adapters:

"""
Computed Fact to check whether vmHost in given context has access to all shared repositories.

This fact is to be used in a vmhost constraint.
The vmhostDefault policy can be augmented to evaluate on this fact.

First you must define the usage of the computed fact on the grid object
by creating a linked fact.  Create a policy with the following text and
apply the new policy to any repository objects you want to constrain
(or for simplification apply this new policy to All repositories)

<policy>
    <resource>
        <fact name="candidateHost" type="Boolean" cfactvalue="cfact.resource"/>
    </resource>
</policy>

This creates a new fact in the repository namespace that links to the computed fact.

Second, create a constraint that uses the new repository fact.
Example constraint to add to the vmhostDefault policy:

      <eq fact="resource.candidateHost" value="True" reason="vmHost doesn't have access to all shared repositories" />

To test:
  Select a VM and choose "Check Host Assignment". This brings up a dialog of what host/repository
  plans match.  In this case, all the plans will not match unless they have access to shared repositories.

"""

class resource(ComputedFact):

    def compute(self):

        context = self.getContext()
        if context is not None:

            #Get vm resource and vmhost Info object
            resource = context.getResource()
            vmhost = context.getVmHost()

            if resource == None:
                print "no resource"
                return False

            elif vmhost == None:
                print "no vmhost"
                return False

            else:
                #get the vm disks list
                vmdisks = resource.getFact("resource.vm.vdisks")
                #print 'disk info is ',vmdisks
                for diskId in vmdisks:
                    disk = getMatrix().getGridObject(TYPE_VDISK,diskId)
                    if disk != None and disk.factExists("vdisk.moveable) and not disk.getFact(vdisk.moveable"):

                        # If the disk is not moveable, get repository where it is stored
                        if disk.factExists("vdisk.repository"):
                            repo = disk.getFact("vdisk.repository")

                            #Get list of repositories to which vmhost has access to
                            repos = vmhost.getFact("vmhost.repositories")

                            #If vmhost doesn't have access to disk repo, return False
                            if not repo in repos:
                                return False
            #vmhost in context has access to all shared repositories
            return True

        # no context (console fact table)    
        return False

4.2.2 Creating a New Computed Fact

ComputedFact is the base class for creating custom computed Facts. To create a new computed fact, you need to subclass the ComputedFact class with the .cfact extension.

You can create a standard fact to include the cfact just as you would in a policy structure. For example:

    <fact name="network.score" type="Integer" cfactvalue="cfact.networkScore"/>

4.2.3 Using a Computed Fact

To use a computed fact you must deploy to the server the file with the .cfact extension referred to above. The ComputedFact subclass name is not required to match the computed fact file name. The file name is the computed fact name that you define. To use the Computed Fact, create a linked fact that references the deployed ComputedFact class. Cloud Manager Orchestration uses the linked fact in constraints.

The following is an example you would use to retrieve the current job instance where a computed fact is being executed in a resource constraint:

      class myComputedFact(ComputedFact):
          def compute(self):
              ctx = self.getContext()
              if ctx == None:
                  print "No context"
                  ...
              else:
                  jobInstance = ctx.getJobInstance()
                  if jobInstance == None:
                      print "No job instance in context"
                      ...
                  else:
                      print "jobInstance.id=%s" % (jobInstance.getFact("jobinstance.id"))
                      ...

4.2.4 Caching and Performance Considerations

Due to spawning of Jython threads, cfacts place a considerably higher load on the server than normal dynamic fact computations. You should enable caching when an excess load occurs, such as when a fact is associated with a large number of resources. An example of this is using the vmbuilderPXEBoot cfact when the grid is large, to avoid performance degradation.

To enable caching of cfacts,

  1. In the Explorer treee of the Orchestration Console, expand the Computed Facts group object and select vmbuilderPXEBoot to open the admin view.

  2. In the admin view, select the Attributes tab to open the Attributes page.

  3. On the Attributes page, select the Cache Result For check box and enter an amount of time greater than 30 seconds (for example, enter 10 minutes).

  4. Click the Save icon in the main toolbar to save the new settings.

Use this procedure only on Orchestration resources that are also VMs – for example, on Xen VMs where the agent is installed.

NOTE:The caching setting is unselected by default because Cloud Manager Orchestration requires some facts to be evaluated frequently for VM host plans. As an administrator, you should also be aware of and select the amount of time for cache refresh. The vmbuilderPXEBoot fact does not change, so setting the cache for this fact has no undesirable effects.