Home

Fabasoft app.telemetry Infrastructure Scripting

Starting with Fabasoft app.telemetry 2015 Update Rollup 3 (Version 15.3) a powerful scripting engine is available to script infrastructure modifications.

Package

A package is a collection of scripts and a package.xml file to describe the package and packed together into a zip archive. The scripts must be put into the execute directory within the archive and the package.xml must be put in the root of the archive. All scripts with the .js extension are executed in alphabetical order. Any script can define multiple Modules using the config_module function. Check out the example package below.

Modules

Modules represent the set of infrastructure objects the scripts can modify. A Module has a reference and a version. The reference is the value you pass to the config_module function. The reference must not collide with the reference somebody else uses. It is therefore recommended to use the Java package naming convention. The version of a module is the highest version any successful call to the upgrade function used.

References

References are used to identify objects inside a Module. References may only contain characters like: A-Za-z0-9_.+: -. All the references are local to the Module and Class. The selector functions of the Config class always only operate on objects of the same Class, thus a reference cpu used with services will never find agents or any other Class.

Exceptions

During the development of infrastructure scripts you will likely encounter JavaScript exceptions. Exceptions are used to abort upgrade transactions whenever they create invalid objects, and serve to protect your infrastructure configuration. Do not try to use try and catch to handle them as doing so will render your upgrades very unreliable. Keep in mind that any upgrade transaction that finished will never be executed again and modifications that were not performed because of ignoring an exception will be lost. To have multiple independent Modules in a single package separate the code into multiple JavaScript files inside the execute directory.

Example Package

Example package.xml:

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Package
  name="Pretty name of your package"
  description=""
  packageid="internal-name-of-your-package"
  version="1.0.0.0"
  author="Author of the package"
  >
</Package>

Example infrastructure script execute/00-applicance.js:

// minimal Fabasoft app.telemetry version required to execute the script
API(15, 3);

// starting the installation/upgrade of the module with the given name
config_module("com.apptelemetry.example.appliance");

// upgrade the module to version 1
// this upgrade will be skipped in case version 1 or any later version is already installed
upgrade(1, function(cfg) {
  cfg.agentGroup('backend-root', function(ag) {
    ag.name = 'Backend';
    cfg.agent(ag, 'backend-agent', function(agent) {
      agent.name = 'Backend Agent';
      agent.agentPort = 10001;
      agent.networkAddress = 'localhost';
    });
  });
});

Lets assume that your backend agent needs to run on another port. To change the configuration of existing objects you simply add a new version to your module:

upgrade(2, function(cfg) {
  // searching for existing objects works almost the same as creating new objects
  cfg.agent('backend-agent', function(agent) {
    // only change the properties that need to be changed
    agent.agentPort = 6000;
  });
});

Now consider to add services and service checks:

upgrade(3, function(cfg) {
  // a group without context is a root group
  cfg.serviceGroup('backend-root', function(rootGroup) {
    rootGroup.name = "Backend";
    // create a subgroup for every agent with reference 'backend-agent'
    cfg.serviceGroups(rootGroup, 'backend-sub', cfg.agent('backend-agent'), function(serviceGroup, agent) {
      serviceGroup.name = "Subgroup for " + agent.name;
      // services with an array of instance names manages multiple objects
      cfg.services(serviceGroup, 'backend-node', [1, 2], function(service, instance) {
        service.name = "Node " + instance;
        service.agent = agent.id;
        var counterInstances = cfg.enumInstances(agent, ServiceCheckProtocol.winperf, 'Processor');
        cfg.serviceChecks(service, 'cpu', counterInstances, function(check, cpu) {
          check.name = "CPU " + cpu;
          check.interval = 1;
          check.properties[ServiceCheckProperty.counter] = '\\Processor(' + cpu + ')\\% User Time';
          check.properties[ServiceCheckProperty.protocol] = ServiceCheckProtocol.winperf;
        });
      });
    });
  });
});

Lets add a nice Dashboard showing the CPU use of our services:

upgrade(4, function(cfg) {
  cfg.dashboard('backend-cpu', function(dashboard) {
    dashboard.name = "Backend CPU";
    // every service gets it's own chart
    cfg.charts(dashboard, 'node-cpu-load', cfg.service('backend-node'), function(chart, service) {
      chart.name = "CPU Load on " + service.name;
      chart.updateInterval = 10;
      extend(chart.control, {
        format: "%",
        maxValue: 100,
        minHeight: 200,
        minValue: 0,
        minWidth: 200,
        type: "line"
      });
      extend(chart.datasource, {
        checks: cfg.serviceCheck(service, 'cpu').map(function(check) { return check.id; }),
        timeRange: 'minute-30',
        type: 'CounterCheck'
      });
    });
  });
});

Details about the available functions and the meaning of the parameters can be found in the description of the Config class. The extend utility function makes it easy to set multiple properties while not accidentally deleting existing properties. The easiest way to find out how to achieve the desired result is to use the Export JSON Action in the Fabasoft app.telemetry Client on the Configuration view.