Name

sn_itom_license.ITOMLegacyLicensingAccountingHelper

Description

Helper to account usage of each license bundle/product against the legacy ITOM SKU s assigned to an instance

Script

var ITOMLegacyLicensingAccountingHelper = Class.create();

ITOMLegacyLicensingAccountingHelper.prototype = {
  initialize: function() {
      this.productCounts = {}; // hold counts per individual products
      this.aLaCarteSKUs = {};
      this.instanceSkuList = JSON.parse(sn_lef.GlideEntitlement.getItomLegacyLicenseInfo());

      // Products
      this.DISCOVERY = "Discovery";
      this.EVENT_MGMT = "Event Management";
      this.SERVICE_MAPPING = "Service Mapping";
      this.OP_INT = "Operational Intelligence";
      this.CLOUD_MGMT = "Cloud Management";

      // Bundles
      this.ITOM_STD = "ITOM Standard";
      this.ITOM_PRO = "ITOM Professional";
      this.ITOM_ENT = "ITOM Enterprise";
      this.SW_MAPPING = "ServiceWatch Mapping";
      this.SW_INSIGHT = "ServiceWatch Insight";
      this.SW_SUITE = "ServiceWatch Suite";
      this.SW_SUITE_V2 = "ServiceWatch Suite v2";
      this.SERVICE_OPS_SUITE = "Service Operations Suite";
      this.IT_OPS_MGMT_SUITE = "IT Operations Management Suite";

      // Tables
      this.PRODUCT_COUNT_TABLE = 'itom_lu_product_count';
      this.USAGE_ACCOUNT_TABLE = 'itom_lu_usage_account';

      // Mapping between bundle and the products contained within
      this.bundle2Products = {};
      this.bundle2Products[this.ITOM_STD] = [this.DISCOVERY, this.EVENT_MGMT];
      this.bundle2Products[this.ITOM_PRO] = [this.DISCOVERY, this.EVENT_MGMT, this.OP_INT];
      this.bundle2Products[this.ITOM_ENT] = [this.CLOUD_MGMT, this.DISCOVERY, this.EVENT_MGMT, this.SERVICE_MAPPING, this.OP_INT];
      this.bundle2Products[this.SW_MAPPING] = [this.DISCOVERY, this.SERVICE_MAPPING];
      this.bundle2Products[this.SW_INSIGHT] = [this.DISCOVERY, this.EVENT_MGMT, this.SERVICE_MAPPING];
      this.bundle2Products[this.SW_SUITE] = [this.CLOUD_MGMT, this.DISCOVERY, this.EVENT_MGMT, this.SERVICE_MAPPING];
      this.bundle2Products[this.SW_SUITE_V2] = [this.CLOUD_MGMT, this.DISCOVERY, this.EVENT_MGMT, this.SERVICE_MAPPING];
      this.bundle2Products[this.SERVICE_OPS_SUITE] = [this.DISCOVERY, this.EVENT_MGMT];
      this.bundle2Products[this.IT_OPS_MGMT_SUITE] = [this.EVENT_MGMT];

      // Mapping between license_id and SKU name/bundle
      this.licenseId2Sku = {
          "0f8743e36ff0ba40d1c9c3154b3ee4dd": { name: this.IT_OPS_MGMT_SUITE, bundle: true },
          "4b8787a36ff0ba40d1c9c3154b3ee493": { name: this.IT_OPS_MGMT_SUITE, bundle: true },
          "cf8743e36ff0ba40d1c9c3154b3ee4db": { name: this.IT_OPS_MGMT_SUITE, bundle: true },
          "28363246db2a9b04848c581adc9619e4": { name: this.ITOM_STD, bundle: true },
          "44e651cadbdfe3c012c0f5771d96192d": { name: this.ITOM_STD, bundle: true },
          "3d79bdebdb3b3604d49ff5eeaf961982": { name: this.ITOM_PRO, bundle: true },
          "4b8743e36ff0ba40d1c9c3154b3ee4dc": { name: this.SERVICE_OPS_SUITE, bundle: true },
          "4b8787a36ff0ba40d1c9c3154b3ee48f": { name: this.SERVICE_OPS_SUITE, bundle: true },
          "895cf65adba20380d49ff5eeaf96194a": { name: this.SW_SUITE, bundle: true },
          "9d583580db3d2240653cf4331f9619b4": { name: this.SW_SUITE, bundle: true },
          "41b455806f0fae000ba092ae5d3ee455": { name: this.SW_SUITE_V2, bundle: true },
          "bb283940db3d2240653cf4331f96190f": { name: this.SW_INSIGHT, bundle: true },
          "da79bdebdb3b3604d49ff5eeaf9619b6": { name: this.ITOM_ENT, bundle: true },
          "e33140addbd07b00a3bdff661d9619cd": { name: this.OP_INT, bundle: false },
          "18562e8adba2d380f6dff1141d9619be": { name: this.SERVICE_MAPPING, bundle: false },
          "421613c9dba2134089f2f3231d961940": { name: this.CLOUD_MGMT, bundle: false },
          "5a63966e6f5eb5006de0a4f44b3ee4e9": { name: this.DISCOVERY, bundle: false },
          "81dfba0edbec3a00a71c7a9e0f961959": { name: this.EVENT_MGMT, bundle: false }
      };

      // remove all existing accounting records
      this._cleanup();
  },

  accountUsage: function() {
      var productCountsGr = new GlideRecord(this.PRODUCT_COUNT_TABLE);
      productCountsGr.query();

      while (productCountsGr.next())
          this.productCounts[productCountsGr.getValue('product')] = parseInt(productCountsGr.getValue('count'), 10);

      gs.debug("initialized productCounts=" + global.JSON.stringify(this.productCounts));
      gs.debug("starting accounting with license list=" + global.JSON.stringify(this.instanceSkuList));

      var bundleInfo = this._processLicenses();
      if (bundleInfo)
          this._processBundle(bundleInfo);

      this._processALaCarteSKUs();
  },

  _cleanup: function() {
      var cleanupGr = new GlideRecord(this.USAGE_ACCOUNT_TABLE);
      cleanupGr.deleteMultiple();
  },

  _createAccountRecord: function(skuName, skuUsage) {
      var skuAccounting = new GlideRecord(this.USAGE_ACCOUNT_TABLE);
      skuAccounting.addQuery('sku_name', skuName);
      skuAccounting.query();

      gs.debug("creating accounting record for sku_name=" + skuName + ", skuUsage=" + skuUsage);

      if (skuAccounting.next()) {
          skuAccounting.setValue('accounted_usage', skuUsage);
          skuAccounting.update();
      } else {
          skuAccounting.setValue('sku_name', skuName);
          skuAccounting.setValue('accounted_usage', skuUsage);
          skuAccounting.insert();
      }
  },

  _getHighWaterMark: function(bundleName) {
      var highWaterMark = {};
      highWaterMark.total = 0; // High watermark among all products
      highWaterMark.nonALaCarte = 0; // High watermark among products without purchased a la carte
      var products = this.bundle2Products[bundleName];
      for (var i = 0; i < products.length; i++) {
          var product = products[i];
          var productCount = this.productCounts[product];
          if (!productCount) {
              gs.debug('no product count found for: ' + product);
              continue;
          }

          // special case with ITOM Enteprise bundle where Service Mapping has x10 usage modifier
          if (bundleName == this.ITOM_ENT && product == this.SERVICE_MAPPING)
              productCount *= 10;

          if (productCount > highWaterMark.total) {
              highWaterMark.total = productCount;
              highWaterMark.product = product;
          }

          if (!this._findValidSku(product))
              highWaterMark.nonALaCarte = Math.max(productCount, highWaterMark.nonALaCarte);
      }

      return highWaterMark;
  },

  _findValidSku: function(productName) {
      var sku = this.aLaCarteSKUs[productName];
      if (sku)
          return sku;

      return null;
  },

  /*
   * we should NEVER encounter a scenario where there are multiple bundles in the SKU list
   * a customer can have ONE bundle with as many a la carte SKU's as they desire
   * if multiple bundles are found, we'll consider only the first bundle
   */
  _processLicenses: function() {
      var bundleInfo;
      var license;
      var sku;
      var i;
      var firstBundle = true;

      for (i = 0; i < this.instanceSkuList.length; i++) {
          sku = this.instanceSkuList[i];
          license = sku.license_id && this.licenseId2Sku[sku.license_id];
          if (!license) {
              gs.warn('Skipping invalid ITOM license assigned to the instance : ' + global.JSON.stringify(sku));
              continue;
          }

          if (!license.bundle) {
              this.aLaCarteSKUs[license.name] = sku;
              continue;
          }

          // we found the first bundle SKU.. we account the usage against this bundle first
          if (this.bundle2Products[license.name]) {
              // check if multiple bundles are in the SKU's assigned to this instance... skip it, log a warning
              if (!firstBundle) {
                  gs.warn('Skipping additional ITOM bundle SKU assigned to the instance : ' + skuId);
              } else {
                  firstBundle = false;
                  bundleInfo = {'sku': sku, 'name': license.name};
              }

              continue;
          }
      }

      gs.debug("bundleInfo=" + global.JSON.stringify(bundleInfo));
      return bundleInfo;
  },

  _processBundle: function(bundleInfo) {
      var highWaterMark = this._getHighWaterMark(bundleInfo.name);
      gs.debug("highWaterMark=" + global.JSON.stringify(highWaterMark));

      // only bundle SKU assigned to this instance.. fill the bundle up and exit
      if (Object.keys(this.aLaCarteSKUs).length == 0) {
          // use the high watermark as the bundle count and return
          this._createAccountRecord(bundleInfo.name, highWaterMark.total); // create the bundle usage record
          gs.debug('done accounting against bundle.. within bundle usage limits..');
          return;
      }

      // more than 1 SKU assigned.. start with checking the usage against the bundle
      var purchased_count = parseInt(bundleInfo.sku.purchased_count, 10);
      var products = this.bundle2Products[bundleInfo.name];

      var bundleUsageCount = (highWaterMark.total <= purchased_count) ? highWaterMark.total :
      Math.max(highWaterMark.nonALaCarte, purchased_count);

      for (i = 0; i < products.length; i++) {
          var product = products[i];
          var productCount = this.productCounts[product];
          if (!productCount)
              continue;

          // special case with ITOM Enteprise bundle where Service Mapping has x10 usage modifier
          if (bundleInfo.name == this.ITOM_ENT && product == this.SERVICE_MAPPING && product == highWaterMark.product) {
              var modifierApplied = true;
              productCount *= 10;
          }

          // adjust stream usage if there are valid SKU's available
          if (productCount > bundleUsageCount) {
              if (this._findValidSku(product)) {
                  // special case w/ x10 modifier on Service Mapping with ITOM Enterprise bundle,
                  // if its the highwater mark then we need to divide by 10 when accounting to a la carte SKUs ...
                  if (modifierApplied)
                      this.productCounts[product] -= (bundleUsageCount / 10);
                  else
                      this.productCounts[product] -= bundleUsageCount;
              }
          } else {
              this.productCounts[product] = 0; // reduce the usage to 0 since we are within limits here
          }
      }

      // create the bundle usage record
      this._createAccountRecord(bundleInfo.name, bundleUsageCount);
      gs.debug('done accounting against bundle.. accounting against a la carte sku next...');
  },

  _processALaCarteSKUs: function() {
      gs.debug("pending allocation=" + global.JSON.stringify(this.productCounts));

      // account for the remaining usage from the products against the available SKU's
      for (var j = 0; j < Object.keys(this.aLaCarteSKUs).length; j++) {
          var skuId = Object.keys(this.aLaCarteSKUs)[j];
          if (this.productCounts[skuId] > 0) {
              this._createAccountRecord(skuId, this.productCounts[skuId]);
          }
      }
  },

  type: 'ITOMLegacyLicensingAccountingHelper'
};

Sys ID

856a38ea53f70010996addeeff7b122c

Offical Documentation

Official Docs: