Page tree
Skip to end of metadata
Go to start of metadata

The following is a partial list of the default billing methods supplied with Flexiant Cloud Orchestrator. This should serve as a useful starting point for writing your own billing methods.

-- This will be the default billing supplied by the Extility code
--[[
Please read the following instructions carefully.
The Lua FDL code below is meant to run within Jade. Executing this script separately will not work.
If you want to log data please use the "logger(String)" function; this will write your
log data into the standard Jade log. If you use the default Lua "print", this will write
the log data into the Jade .sysout log.
The entry point to the Lua script will be the register() function. This returns a list of
exported entry points, each of which is associated with an API. All the examples in this
file are for the billing API.
Each entry point is first called with no parameters, which gives the entry point the chance
to describe itself. This works as follows:
local confvalues = {
  -- List of configured values
  {
    key = "key_for_configured_value",
    name = "name for configured value"
    description = "long description of configured value",
    measureType = "UNIT",
    validator = {validatorType = "NUMERIC_DOUBLE"}
  }
}
return {
  -- Unique reference
  ref = "_serverAllocatedCPUBilling",
  -- billingFuncName: name of an alternate method to call or nil (or omit) to use this function
  billingFuncName = nil
  -- Description of billing method
  description = "Long description of billing method",
  -- List of permitted PCTs
  permittedPCT = {"PCT-SERVER-CPU"},
  -- Configured Values
  configuredValues = confvalues,
  -- API type
  api="BILLING",
  -- API version
  version = 1
}
]]
-- ///////////////////////////////// Global Helper Functions ///////////////////////////////////////d
function getRepeatUnits(billingcomp)
  local val = billingcomp:getBillingValue("repeat_units")
  if (val) then
    local units_str = val:getValue()
    if (units_str) then
      return tonumber(units_str)
    end
  end
  return nil
end
function getInitialUnits(billingcomp)
  local val = billingcomp:getBillingValue("initial_units")
  if (val) then
    local units_str = val:getValue()
    if (units_str) then
      return tonumber(units_str)
    end
  end
  return nil
end
-- This will return the allocated size and the type it is measured with
function getConfiguredMeasureTypeValue(billingComp, key)
  local val = billingComp:getProductValue(key)
  if (val) then
    local size = val:getValue()
    local mtype = (val:getMeasureType()):toString()
    return mtype, size
  end
  return nil, nil
end
function getRepeatUnitsDefinitionTable()
  return {
    key = "repeat_units",
    name = "Repeat Units",
    description = "Number of units that will be charged or allocate on each subsequent billing cycle after the purchase",
    measureType = "UNIT",
    validator = {validatorType = "NUMERIC_DOUBLE"}
  }
end
function getInitialUnitsDefinitionTable()
  return {
    key = "initial_units",
    name = "Initial Units",
    measureType = "UNIT",
    validator = {validatorType = "NUMERIC_DOUBLE"},
    description = "Number of units that will be charged or allocated on the first billing cycle after the purchase"
  }
end
function getChargeMeasureTypeDefinition_table(addInitialUnits, addRepeatUnits)
  local confValues = {
    {
      key = "charge_measurement_type",
      name = "Measurement Type",
      description = "Measurement type corresponding to the charge; the charge will be applied per byte, kilobyte, megabyte, gigabyte or terabyte according to this setting",
      validator = {validatorType = "ENUM", validateString = "B,KB,MB,GB,TB"},
      measureType = "NUMERIC"
    }
  }
  if (addInitialUnits) then
    table.insert(confValues, getInitialUnitsDefinitionTable())
  end
  if (addRepeatUnits) then
    table.insert(confValues, getRepeatUnitsDefinitionTable())
  end
  return confValues
end
function getChargeMeasureTypeRepeatUnits(billingComp)
  local measureType = (billingComp:getBillingValue("charge_measurement_type")):getValue()
  local repeatUnits = getRepeatUnits(billingComp);
  return measureType, repeatUnits
end
function getChargeMeasureTypeInitialUnits(billingComp)
  local measureType = (billingComp:getBillingValue("charge_measurement_type")):getValue()
  local initalUnits = getInitialUnits(billingComp);
  return measureType, initalUnits
end
function getMeasureTypeMeasuredValue(measuredComp, key)
  local val = measuredComp:getMeasuredValue(key)
  if (val) then
    local mtype = (val:getMeasureType()):toString()
    local mval = val:getValue()
    return mtype, mval
  end
  return nil, nil
end
-- //////////////////////////////// End of global Helper Function ////////////////////////////////////////
-- ************************************ FIXED MODE BILLING *****************************
-- This is the most simplistic billing function this charge a fixed amount each time
-- invoked, this can be used with any PCT
function fixedResourceBilling(p)
  if (p == nil) then
    -- define the which is needs to be configurable params
    local confvalues = {getInitialUnitsDefinitionTable(), getRepeatUnitsDefinitionTable()}
    return {
      ref = "_fixedResourceBilling",
      name = "Fixed billing for resources",
      description = "Charge the same number of units in each billing period",
      permittedPCT = nil,
      configuredValues = confvalues,
      api = "BILLING",
      version = 1
    }
  end
  --print ("Last Billing Time is : " .. p.lastBillingTime .. " Current Billing Time ".. p.currentBillingTime .." Billing Factor is "..p.billingFactor)
  if (p.lastBillingTime == p.currentBillingTime) then
    cunits = getInitialUnits(p.billingComp)
    if (cunits > 0.0) then
      --print("Initial charge -"..cunits);
      return {{units = cunits * -1, description="Initial charge"}}
    end
  else
    cunits = getRepeatUnits(p.billingComp)
    if (cunits > 0.0) then
      return {{units = cunits * -1 * p.billingFactor , description = "Charge for period" }}
    end
  end
  return nil
end
-- ************************************* END OF FIXED MODE BILLING ***************************************
-- ************************************ ALLOCATION BASED BILLING *****************************************
function serverBilling(p)
  local measureType, chargeUnits = getChargeMeasureTypeRepeatUnits(p.billingComp)
  local only_when_running = (p.billingComp:getBillingValue("only_when_running")):getValue()
  local chargeUnits = getRepeatUnits(p.billingComp)
  local doCharge = true
  -- If the only_when_running billing parameter is set we will only be charging if the server was running at the give time
  if (only_when_running == "TRUE") then
    -- print ("Looking for maximum running value between ".. p.lastBillingTime .. " and " .. p.currentBillingTime)
    local measuredHash = p.measureComp:getMAXMeasureBetween(p.lastBillingTime, p.currentBillingTime, "running")
    local uptime = measuredHash:get("running")
    if ((uptime == nil) or (uptime:getMeasurement() <= 0)) then
      doCharge = false
    end
  end
  if (doCharge) then
    if ((chargeUnits ~= nil) and (chargeUnits > 0.0)) then
      if (measureType == "CPU_CORE") then
        local val = p.billingComp:getProductValue("cpu")
        if (val) then
          local cores = tonumber(val:getValue())
          return {{
              units = (cores * chargeUnits * -1 * p.billingFactor),
              description = "Charge for " .. cores .." CPU cores"
          }}
        end
      else
        local configMtype, configSize = getConfiguredMeasureTypeValue(p.billingComp, "ram")
        if (configSize) then
          return {{
              units = p.billingFactor * -1 * chargeUnits * tonumber(configSize) * (convert_mtype(configMtype, measureType)),
              description = "Charge for " .. configSize .. configMtype
          }}
        end
      end
    end
  end
  return nil
end
-- This function will be billing, based on the allocated size of CPU
function serverAllocatedCPUBilling(p)
  local confvalues = { {
      key = "charge_measurement_type",
      name = "Per CPU billing",
      description = "Per CPU billing",
      value = "CPU_CORE",
      validator = {validatorType = "ENUM", validateString = "CPU_CORE"},
      measureType = "NUMERIC"
    },
    getRepeatUnitsDefinitionTable (),
    {
      key = "only_when_running",
      name = "Bill only when running",
      description = "If this value is set to TRUE, charging will only be done when server is running",
      validator = {validatorType = "ENUM", validateString = "TRUE,FALSE"},
      measureType = "STRING"
    }
  }
  return {
    ref = "_serverAllocatedCPUBilling",
    executionFunction = "serverBilling",
    name = "Server billing for allocated CPUs",
    description = "Bill a fixed amount per allocated CPU",
    permittedPCT = {"PCT_SERVER_CPU"},
    configuredValues = confvalues,
    api = "BILLING",
    version = 1
  }
end
function serverAllocatedRAMBilling(p)
  local confvalues = getChargeMeasureTypeDefinition_table (false, true)
  table.insert (confvalues,
    {
      key = "only_when_running",
      name = "Bill only when running",
      description = "If this value is set to TRUE, charging will only be done when server is running",
      validator = {validatorType = "ENUM", validateString = "TRUE,FALSE"},
      measureType = "STRING"
    }
  )
  return {
    ref = "_serverAllocatedRAMBilling",
    executionFunction = "serverBilling",
    name = "Server billing for allocated RAM",
    description = "Bill a fixed amount depending on allocated RAM",
    permittedPCT = {"PCT_SERVER_RAM"},
    configuredValues = confvalues,
    api = "BILLING",
    version = 1
  }
end
-- This billing will be done depending on the allocated size of disk snapshot
-- This can only be used with PCT-DISK-ALLOC
function sizeAllocatedBilling(p)
  if (p == nil) then
    local confvalues = getChargeMeasureTypeDefinition_table(false, true)
    return {
      ref = "_sizeAllocatedBilling",
      name = "Billing based on storage size allocated",
      description = "Bill a fixed amount depending on the allocated amount of storage",
      permittedPCT = {"PCT_DISK_ALLOC","PCT_SNAPSHOT","PCT_IMAGE"},
      configuredValues = confvalues,
      api = "BILLING",
      version = 1
    }
  end
  local measureType, chargeUnits = getChargeMeasureTypeRepeatUnits (p.billingComp)
  local configMType, configSize = getConfiguredMeasureTypeValue (p.billingComp, "size")
  if ((chargeUnits ~= nil) and (chargeUnits > 0.0) and
    (configSize ~= nil) and (tonumber(configSize) > 0 )) then
    return {{
        units = (chargeUnits * tonumber(configSize) * (convert_mtype(configMType,measureType)) * -1 * p.billingFactor),
        description = "Charge for " .. configSize .. configMType .. " allocated space"
    }}
  end
  return nil
end
-- ************************************ END OF ALLOCATION BASED BILLING *****************************************
-- ************************************ USAGE BASED BILLING *****************************************************
function diskIOUsageBilling(p)
  if (p == nil) then
    local confvalues = getChargeMeasureTypeDefinition_table(false, true)
    return {
      ref = "_diskIOUsageBilling",
      name = "Disk I/O charging",
      description = "Bill a variable amount depending on disk I/O in the period",
      permittedPCT = {"PCT_DISK_IO"},
      configuredValues = confvalues,
      api = "BILLING",
      version = 1
    }
  end
  if (p.currentBillingTime == 0 ) then
    return nil
  end
  -- print ("Current bill time is "..p.currentBillingTime)
  -- No previous measure value: charge the initial units
  if (p.lastBillingTime == 0) then
    local iniMtype, chargeUnits = getChargeMeasureTypeInitialUnits(p.billingComp)
    if ((chargeUnits == nil) or (chargeUnits <= 0.0)) then
      return nil
    end
    return {{
        units = (chargeUnits * -1),
        description="Initial charge"
    }}
  end
  -- Lets bill for the usage
  local recMtype, chargeUnits = getChargeMeasureTypeRepeatUnits(p.billingComp)
  if ((recMtype == nil) or (chargeUnits == nil) or (chargeUnits <= 0.0)) then
    -- print ("Current Condition failing here while getting billing config")
    return nil
  end
  local previousHash = p.measureComp:getMeasureAt(p.lastBillingTime, "disk_read,disk_write")
  if ((not previousHash) or (previousHash:size() == 0 )) then
    previousHash = p.measureComp:getMINMeasureBetween(p.lastBillingTime, p.currentBillingTime, "disk_read,disk_write")
  end
  local currHash = p.measureComp:getMeasureAt(p.currentBillingTime, "disk_read,disk_write")
  local currentRead = currHash:get("disk_read")
  local currentWrite = currHash:get("disk_write")
  local curReadM = 0
  local curWriteM = 0
  local currM = "MB"
  if (currentRead) then
    curReadM = currentRead:getMeasurement()
    currM = currentRead:getMeasurementType()
  end
  if (currentWrite) then
    curWriteM = currentWrite:getMeasurement()
  end
  local preReadM = 0
  local preWriteM = 0
  local preM = "MB"
  local preRead = previousHash:get("disk_read")
  local preWrite = previousHash:get("disk_write")
  if (preRead) then
    preReadM = preRead:getMeasurement()
    preM = preRead:getMeasurementType()
  end
  if (preWrite) then
    preWriteM = preWrite:getMeasurement()
  end
  local usage = ((curReadM * convert_mtype(currM,recMtype)) - (preReadM * convert_mtype(preM,recMtype))) + ((curWriteM * convert_mtype(currM,recMtype)) - (preWriteM * convert_mtype(preM,recMtype)))
  if (usage > 0) then
    return {{
        units = (usage * chargeUnits * -1),
        description = "Charge for " .. usage .. recMtype .. " disk I/O"
    }}
  end
  return nil
end
function networkIOUsageBilling(p)
  if (p == nil) then
    local confValues = getChargeMeasureTypeDefinition_table(false, false)
    table.insert(confValues,{
        key = "repeat_units_tx",
        name = "Repeat Units (TX data)",
        description = "Number of units that will be charged on given billing cycle for transmitted data",
        measureType = "UNIT",
        validator = {validatorType = "NUMERIC_DOUBLE"}
      } )
    table.insert(confValues,{
        key = "repeat_units_rx",
        name = "Repeat Units (RX data)",
        description = "Number of units that will be charged on given billing cycle for received data",
        measureType = "UNIT",
        validator = {validatorType = "NUMERIC_DOUBLE"}
      } )
    return {
      ref = "_networkIOUsageBilling",
      name = "Network usage charging",
      description = "Bill a variable amount depending on network usage during the period",
      permittedPCT = {"PCT_NETWORK_TRANSFER"},
      configuredValues = confValues,
      api = "BILLING",
      version = 1
    }
  end
  local cRxVal = 0
  local cTxVal = 0
  local cM = "MB"
  local pRxVal = 0
  local pTxVal = 0
  local pM = "MB"
  local preHash = p.measureComp:getMeasureAt(p.lastBillingTime, "traffic_in, traffic_out")
  -- As we are truncating the measured values tables in slow system we might not get the value for the last billing
  -- in such cases lets default to the minimum value
  if ((not preHash) or (preHash:size() == 0 )) then
    preHash = p.measureComp:getMINMeasureBetween(p.lastBillingTime, p.currentBillingTime, "traffic_in, traffic_out")
  end
  local curHash = p.measureComp:getMeasureAt(p.currentBillingTime, "traffic_in, traffic_out")
  local pRx = preHash:get("traffic_in")
  local pTx = preHash:get("traffic_out")
  local cRx = curHash:get("traffic_in")
  local cTx = curHash:get("traffic_out")
  if (pRx) then
    pRxVal = pRx:getMeasurement()
    pM = pRx:getMeasurementType()
  end
  if (pTx) then
    pTxVal = pTx:getMeasurement()
  end
  if (cRx) then
    cRxVal = cRx:getMeasurement()
    cM = cRx:getMeasurementType()
  end
  if (cTx) then
    cTxVal = cTx:getMeasurement()
  end
  local recMtype = (p.billingComp:getBillingValue("charge_measurement_type")):getValue()
  -- print ("Measurement Type set " .. recMtype)
  local txCharge = (p.billingComp:getBillingValue("repeat_units_tx")):getValue()
  -- print ("Tx charge set " .. txCharge)
  local rxCharge = (p.billingComp:getBillingValue("repeat_units_rx")):getValue()
  -- print ("Rx charge set " .. rxCharge)
  if (recMtype == nil) then
    return nil
  end
  local out = {}
  if (txCharge ~= nil) then
    local usage = (cTxVal * (convert_mtype(cM, recMtype))) -
    (pTxVal * (convert_mtype(pM, recMtype)))
    -- print ("Charging for TX usage "..usage)
    if (usage > 0 ) then
      table.insert(out,{
          units = (usage * tonumber(txCharge) * -1 ),
          description = "Charge for " .. usage .. recMtype .. " network transmitted data"
        })
    end
  end
  if (rxCharge ~= nil) then
    local usage = (cRxVal * (convert_mtype(cM, recMtype))) -
    (pRxVal * (convert_mtype(pM, recMtype)))
    -- print ("Charging for RX usage "..usage)
    if (usage > 0) then
      table.insert(out,{
          units = (usage * tonumber(rxCharge) * -1),
          description = "Charge for " .. usage .. recMtype .. " network received data"
        })
    end
  end
  return out
end
function fetchResourceBilling(p)
  if (p == nil ) then
    local confVals = getChargeMeasureTypeDefinition_table(true, false)
    return {
      ref = "_fetchResourceBilling",
      name = "Resource fetch charging",
      description = "Bill when a resource is fetched",
      permittedPCT = {"PCT_FETCH"},
      configuredValues = confVals,
      api = "BILLING",
      version = 1
    }
  end
  if (p.currentBillingTime > 0 ) then
    local mType, mVal = getConfiguredMeasureTypeValue(p.billingComp, "fetchSize")
    if ((mType == nil) or (mVal == nil)) then
      return nil
    end
    local cType, cVal = getChargeMeasureTypeInitialUnits(p.billingComp)
    if ((cType == nil) or (cVal == nil)) then
      return nil
    end
    return {{
        units = (-1 * cVal * (convert_mtype(mType, cType)) * mVal),
        description = "Charge for fetching ".. mVal .. " " .. mType .. " of data"
    }}
  end
  return nil
end
-- ************************************ END OF USAGE BASED BILLING **********************************************
-- ************************************ AUTO TOP UP BILLING *****************************************************
function unitTopUpBilling(p)
  if (p == nil) then
    local confValues = {}
    -- The unit balance threshold before the top up will be started
    table.insert(confValues, {
        key = "balance_threshold",
        name = "Balance threshold",
        measureType = "UNIT",
        validator = {validatorType = "NUMERIC_DOUBLE"},
        description = "An auto topup will run each time the customer goes below the balance threshold"
      })
    return {
      ref = "_unitTopUpBilling",
      name = "Auto top up billing",
      description = "Billing for purchasing units",
      permittedPCT = {"PCT_UNIT_TRANSACTION"},
      configuredValues = confValues,
      api = "BILLING",
      version = 1
    }
  end
  local curr_bal = p.customer:getCarryOverBalance()
  -- logger ("Current customer carryover balance for customer [".. p.customer:getResourceUUID() .. "] is " .. curr_bal)
  local thresholdVal = p.billingComp:getBillingValue("balance_threshold")
  local threshold = 0
  if ( thresholdVal and thresholdVal:getValue()) then
    threshold = tonumber(thresholdVal:getValue());
  end
  -- logger ("The top up triggering billing threshold is set to "..threshold)
  if (curr_bal <= threshold or p.billingFactor == 0) then
    -- Issue the units and ask it to do the charge
    local charge = tonumber((p.billingComp:getProductValue("transactionCharge")):getValue())
    local unitsToAdd = tonumber((p.billingComp:getProductValue("transactionAmount")):getValue())
    -- logger("A charge of ["..charge.."] need to be taken and ["..unitsToAdd.."] will be added to the customer account [")
    return {{
        units = unitsToAdd,
        description = unitsToAdd .. " units",
        charge = charge
    }}
  end
  return nil;
end
function arbitraryUnitBilling(p)
  if (p == nil) then 
    return {
      ref = "_arbitraryUnitBilling",
      name = "Arbitrary Unit Billing",
      description = "This permits unit transactions of arbitrary size and will bill the Transaction Charge for each unit that is purchased.",
      permittedPCT = {"PCT_UNIT_TRANSACTION"},
      configuredValues = confValues,
      api = "BILLING",
      version = 1
    }
  end
  
  local charge = tonumber((p.billingComp:getProductValue("transactionCharge")):getValue())
  local unitsToAdd = tonumber((p.billingComp:getProductValue("transactionAmount")):getValue())
  
  if (charge >= 0 and unitsToAdd > 0) then 
    return {{
        units = unitsToAdd,
        description = unitsToAdd .. " units",
        charge = charge * unitsToAdd
    }}
  end 
  
  return nil
end
function fixedUnitBilling(p)
  if (p == nil) then 
    return {
      ref = "_fixedUnitBilling",
      name = "Fixed Unit Billing",
      description = "This will charge the customer amount specified by the Transaction charge.",
      permittedPCT = {"PCT_UNIT_TRANSACTION"},
      configuredValues = confValues,
      api = "BILLING",
      version = 1
    }
  end
  
  local charge = tonumber((p.billingComp:getProductValue("transactionCharge")):getValue())
  local unitsToAdd = tonumber((p.billingComp:getProductValue("transactionAmount")):getValue())
  
  if (charge >= 0 and unitsToAdd > 0) then 
    return {{
        units = unitsToAdd,
        description = unitsToAdd .. " units",
        charge = charge
    }}
  end 
  
  return nil
end
-- ************************************ END OF AUTO TOP UP BILLING **********************************************
-- ************************************ BILLING FOR SUBNET *****************************************************
function subnetBillingIpV4(p)
  if (p == nil) then
    local confValues = {}
    table.insert(confValues,{
        key = "units_per_address",
        name = "Units per IPv4 Address",
        description = "The number of units to be charged per IPv4 address in subnet",
        measureType = "UNIT",
        validator = {validatorType = "NUMERIC_DOUBLE"}
      })
    return {
      ref = "_subnetBillingIpV4",
      name = "Bill per IPv4 Address",
      description = "Billing for each Ip address in a IPv4 subnet",
      permittedPCT = {
        "PCT_SUBNET"
      },
      configuredValues = confValues,
      api = "BILLING",
      version = 1
    }
  end
  local chargeVal = p.billingComp:getBillingValue("units_per_address")
  local unit_charge = 0
  if (chargeVal) then
    unit_charge = tonumber(chargeVal:getValue())
  end
  if (unit_charge > 0 and p.billingResource) then
    -- Resource is subnet here
    if (p.billingResource:getSubnetType():getDBValue() == 1 ) then
      local numIps = math.pow(2,(32 - p.billingResource:getMask()))
      return {{
          units = (p.billingFactor * unit_charge * -1 * numIps) ,
          description = "Charge for " .. numIps .. " of IPv4 addresses"
      }}
    end
  end
  return nil
end
-- ************************************* END OF BILLING FOR SUBNETS *********************************************
-- ************************************ BILLING FOR CUSTOMER ASSETS ********************************************
function customerResourceAssetBilling(p)
  if (p == nil) then
    local confValues = {}
    table.insert(confValues,{
        key = "standard_units",
        name = "Base unit charge",
        description = "The number of units to be charged as standard, regardless of the assets used",
        measureType = "UNIT",
        validator = {validatorType = "NUMERIC_DOUBLE"}
      })
    table.insert(confValues,{
        key = "free_assets",
        name = "Asset allowance",
        description = "The number assets given to the customer as an allowance, without an extra charge ",
        measureType = "NUMERIC",
        validator = {validatorType = "NUMERIC_DOUBLE"}
      })
    table.insert(confValues,{
        key = "per_extra_asset_units",
        name = "Units per excess asset",
        description = "The number of units to be charged for each asset that exceeds the allowance",
        measureType = "UNIT",
        validator = {validatorType = "NUMERIC_DOUBLE"}
      })
    return {
      ref = "_customerResourceAssetBilling",
      name = "Customer Asset Billing",
      description = "Bill for customer assets",
      permittedPCT = {
        "PCT_CUST_ASSET_USER",
        "PCT_CUST_ASSET_IMAGE",
        "PCT_CUST_ASSET_SNAPSHOT",
        "PCT_CUST_ASSET_STORAGEGB",
        "PCT_CUST_ASSET_DISK",
        "PCT_CUST_ASSET_SERVER",
        "PCT_CUST_ASSET_CPU",
        "PCT_CUST_ASSET_RAM",
        "PCT_CUST_ASSET_SUBNET",
        "PCT_CUST_ASSET_NETWORK",
        "PCT_CUST_ASSET_VDC",
        "PCT_CUST_ASSET_IPv4",
        "PCT_CUST_ASSET_IPv6"
      },
      configuredValues = confValues,
      api = "BILLING",
      version = 1
    }
  end
  local val = p.billingComp:getProductValue("asset_type")
  -- If the asset type is not defined we do not know what to bill for
  if (val ~= nil) then
    local asset = val:getValue()
    if (asset ~= nil) then
      local freeAsset = 0
      local standardUnits = 0
      local extraUnits = 0
      local sval = p.billingComp:getBillingValue("standard_units")
      if (sval ~= nil) then
        local val = sval:getValue()
        if (val ~= nil) then
          standardUnits = tonumber(val)
        end
      end
      local fval = p.billingComp:getBillingValue("free_assets")
      if (fval ~= nil) then
        local val = fval:getValue()
        if (val ~= nil) then
          freeAsset = tonumber(val)
        end
      end
      local eval = p.billingComp:getBillingValue("per_extra_asset_units")
      if (eval ~= nil) then
        local val = eval:getValue()
        if (val ~= nil) then
          extraUnits = tonumber(val)
        end
      end
      local usedFql = "used." .. asset
      local currAsset = 0
      local currentUsed = p.measureComp:getMAXMeasureBetween(p.lastBillingTime, p.currentBillingTime, usedFql)
      if ((currentUsed ~= nil) and (currentUsed:size() > 0)) then
        currAsset = (currentUsed:get(usedFql)):getMeasurement()
      end
      local ret = {}
      if (standardUnits > 0) then
        table.insert(ret, {
            units = standardUnits * -1 * p.billingFactor,
            description = "Standard Charge for Asset Type " .. asset
          })
      end
      local billAsset = currAsset - freeAsset
      if (billAsset > 0 and extraUnits > 0) then
        table.insert(ret, {
            units = billAsset * extraUnits * -1 * p.billingFactor,
            description = "Charge for Using " .. billAsset .. " " .. asset .. "(s) over allowance"
          })
      end
      return ret;
    end
  end
  return nil
end
-- ************************************ END OF CUSTOMER ASSET BILLING ******************************************
-- ************************************ CUSTOMER ASSET NETWORK/IO BILLING ****************************************
function customerNetIOBilling (p)
  if (p == nil) then
    local confValues = getChargeMeasureTypeDefinition_table(false, false)
    table.insert(confValues,1,{ 
        key = "standard_units",
        name = "Base unit charge",
        description = "The number of units to be charged as standard, regardless of the usage",
        measureType = "UNIT",
        validator = {validatorType = "NUMERIC_DOUBLE"}
      })
    table.insert(confValues,{
        key = "free_assets_tx",
        name = "TX data allowance",
        description = "The allowance for transmitted data, without an extra charge ",
        measureType = "NUMERIC",
        validator = {validatorType = "NUMERIC_DOUBLE"}
      })
    table.insert(confValues,{
        key = "repeat_units_tx",
        name = "Per excess TX data",
        description = "Number of units that will be charged on given billing cycle for transmitted data which exceed the transfer allowance",
        measureType = "UNIT",
        validator = {validatorType = "NUMERIC_DOUBLE"}
      } )
    table.insert(confValues,{
        key = "free_assets_rx",
        name = "RX data allowance",
        description = "The allowance for recived data, without an extra charge ",
        measureType = "NUMERIC",
        validator = {validatorType = "NUMERIC_DOUBLE"}
      })
    table.insert(confValues,{
        key = "repeat_units_rx",
        name = "Per excess RX data",
        description = "Number of units that will be charged on given billing cycle for received data which exceed the transfer allowance",
        measureType = "UNIT",
        validator = {validatorType = "NUMERIC_DOUBLE"}
      } )
    return {
      ref = "_customerNetIOBilling",
      name = "Network IO Billing on customer ",
      description = "Bill network IO usage on a customer account",
      permittedPCT = {"PCT_CUST_ASSET_INTERWORK_NET",
        "PCT_CUST_ASSET_EXTERNAL_NET",
        "PCT_CUST_ASSET_NETWORK_IP_NET",
        "PCT_CUST_ASSET_NETWORK_PRIVATE_NET",
        "PCT_CUST_ASSET_NETWORK_PUBLIC_NET",
      "PCT_CUST_ASSET_NETWORK_ALL_NET"},
      configuredValues = confValues,
      api = "BILLING",
      version = 1
    }
  end
  if (p.currentBillingTime > 0) then
    -- print ("Current time is "..p.currentBillingTime)
    local val = p.billingComp:getProductValue("asset_type")
    if (val ~= nil) then
      -- print ("Asset Type found")
      local asset = val:getValue()
      if (asset ~= nil) then
        -- print ("Asset is "..asset)
        local in_measure = "used."..asset.."_IN"
        local out_measure = "used."..asset.."_OUT"
        local req = in_measure..", "..out_measure
        local recMtype = (p.billingComp:getBillingValue("charge_measurement_type")):getValue()
        -- print ("Measurement Type set " .. recMtype)
        local txCharge = (p.billingComp:getBillingValue("repeat_units_tx")):getValue()
        -- print ("Tx charge set " .. txCharge)
        local rxCharge = (p.billingComp:getBillingValue("repeat_units_rx")):getValue()
        -- print ("Rx charge set " .. rxCharge)
        local standardUnits = 0
        local freeAssetTX = 0
        local freeAssetRX = 0
        local sval = p.billingComp:getBillingValue("standard_units")
        if (sval ~= nil) then
          local v = sval:getValue()
          if (v ~= nil) then
            standardUnits = tonumber(v)
          end
        end
        local ftxval = p.billingComp:getBillingValue("free_assets_tx")
        if (ftxval ~= nil) then
          local v = ftxval:getValue()
          if (v ~= nil) then
            freeAssetTX = tonumber(v)
          end
        end
        local frxval = p.billingComp:getBillingValue("free_assets_rx")
        if (frxval ~= nil) then
          local v = frxval:getValue()
          if (v ~= nil) then
            freeAssetRX = tonumber(v)
          end
        end
        local cRxVal = 0
        local cTxVal = 0
        local cM = "MB"
        local pRxVal = 0
        local pTxVal = 0
        local pM = "MB"
        local preHash = p.measureComp:getMeasureAt(p.lastBillingTime, req)
        -- As we are truncating the measured values tables in slow system we might not get the value for the last billing
        -- in such cases lets default to the minimum value
        if ((not preHash) or (preHash:size() == 0 )) then
          preHash = p.measureComp:getMINMeasureBetween(p.lastBillingTime, p.currentBillingTime, req)
        end
        local curHash = p.measureComp:getMeasureAt(p.currentBillingTime, req)
        local pRx = preHash:get(in_measure)
        local pTx = preHash:get(out_measure)
        local cRx = curHash:get(in_measure)
        local cTx = curHash:get(out_measure)
        if (pRx) then
          pRxVal = pRx:getMeasurement()
          pM = pRx:getMeasurementType()
        end
        if (pTx) then
          pTxVal = pTx:getMeasurement()
        end
        if (cRx) then
          cRxVal = cRx:getMeasurement()
          cM = cRx:getMeasurementType()
        end
        if (cTx) then
          cTxVal = cTx:getMeasurement()
        end
        if (recMtype == nil) then
          return nil
        end
        local out = {}
        if (standardUnits > 0) then
          table.insert(out, {
              units = standardUnits * -1 * p.billingFactor,
              description = "Standard Charge for " .. asset
            })
        end
        if (txCharge ~= nil) then
          local usage = (cTxVal * (convert_mtype(cM, recMtype))) -
          (pTxVal * (convert_mtype(pM, recMtype)))
          -- print ("Charging for TX usage "..usage)
          usage = (usage - (freeAssetTX * p.billingFactor))
          if (usage > 0 ) then
            table.insert(out,{
                units = (usage * tonumber(txCharge) * -1 ),
                description = "Charge for " .. usage .. recMtype .. " network transmitted data over allowance"
              })
          end
        end
        if (rxCharge ~= nil) then
          local usage = (cRxVal * (convert_mtype(cM, recMtype))) -
          (pRxVal * (convert_mtype(pM, recMtype)))
          -- print ("Charging for RX usage "..usage)
          usage = (usage - (freeAssetRX * p.billingFactor))
          if (usage > 0) then
            table.insert(out,{
                units = (usage * tonumber(rxCharge) * -1),
                description = "Charge for " .. usage .. recMtype .. " network received data over allowance"
              })
          end
        end
        return out
      end
    end
  end
  return nil
end
function customerDiskIOUsageBilling (p)
  if (p == nil ) then
    local confValues = getChargeMeasureTypeDefinition_table(false, false)
    table.insert(confValues,1,{
        key = "standard_units",
        name = "Base unit charge",
        description = "The number of units to be charged as standard, regardless of the usage",
        measureType = "UNIT",
        validator = {validatorType = "NUMERIC_DOUBLE"}
      })
    table.insert(confValues,{
        key = "free_assets_io",
        name = "Disk IO allowance",
        description = "The allowance for disk IO, without an extra charge ",
        measureType = "NUMERIC",
        validator = {validatorType = "NUMERIC_DOUBLE"}
      })
    table.insert(confValues,{
        key = "repeat_units",
        name = "Units per excess",
        description = "Number of units that will be charged on given billing cycle for disk io which exceed the allowance",
        measureType = "UNIT",
        validator = {validatorType = "NUMERIC_DOUBLE"}
      } )
    return {
      ref = "_customerDiskIOUsageBilling",
      name = "Disk I/O charging for customer",
      description = "Bill a variable amount depending on disk I/O in the period",
      permittedPCT = {"PCT_CUST_ASSET_DISK_IO"},
      configuredValues = confValues,
      api = "BILLING",
      version = 1
    }
  end
  if (p.currentBillingTime == 0 ) then
    return nil
  end
  local val = p.billingComp:getProductValue("asset_type")
  if (val == nil) then
    return nil
  end
  local asset = val:getValue()
  if (asset == nil) then
    return nil
  end
  -- Lets bill for the usage
  local recMtype, chargeUnits = getChargeMeasureTypeRepeatUnits(p.billingComp)
  local standardUnits = 0
  local freeAssetIO = 0
  local sval = p.billingComp:getBillingValue("standard_units")
  if (sval ~= nil) then
    local v = sval:getValue()
    if (v ~= nil) then
      standardUnits = tonumber(v)
    end
  end
  local ftxval = p.billingComp:getBillingValue("free_assets_io")
  if (ftxval ~= nil) then
    local v = ftxval:getValue()
    if (v ~= nil) then
      freeAssetIO = tonumber(v)
    end
  end
  local out = {}
  if (standardUnits > 0) then
    table.insert(out, {
        units = standardUnits * -1 * p.billingFactor,
        description = "Standard Charge for disk IO"
      })
  end
  if ((recMtype == nil) or (chargeUnits == nil) or (chargeUnits <= 0.0)) then
    -- print ("Current Condition failing here while getting billing config")
    return out
  end
  local read_measure = "used."..asset.."_READ"
  local write_measure = "used."..asset.."_WRITE"
  local req = read_measure..", "..write_measure
  local previousHash = p.measureComp:getMeasureAt(p.lastBillingTime, req)
  if ((not previousHash) or (previousHash:size() == 0 )) then
    previousHash = p.measureComp:getMINMeasureBetween(p.lastBillingTime, p.currentBillingTime, req)
  end
  local currHash = p.measureComp:getMeasureAt(p.currentBillingTime, req)
  local currentRead = currHash:get(read_measure)
  local currentWrite = currHash:get(write_measure)
  local curReadM = 0
  local curWriteM = 0
  local currM = "MB"
  if (currentRead) then
    curReadM = currentRead:getMeasurement()
    currM = currentRead:getMeasurementType()
  end
  if (currentWrite) then
    curWriteM = currentWrite:getMeasurement()
  end
  local preReadM = 0
  local preWriteM = 0
  local preM = "MB"
  local preRead = previousHash:get(read_measure)
  local preWrite = previousHash:get(write_measure)
  if (preRead) then
    preReadM = preRead:getMeasurement()
    preM = preRead:getMeasurementType()
  end
  if (preWrite) then
    preWriteM = preWrite:getMeasurement()
  end
  local usage = ((curReadM * convert_mtype(currM,recMtype)) - (preReadM * convert_mtype(preM,recMtype))) + ((curWriteM * convert_mtype(currM,recMtype)) - (preWriteM * convert_mtype(preM,recMtype)))
  usage = (usage - (freeAssetIO * p.billingFactor))
  if (usage > 0) then
    table.insert(out, {
        units = (usage * chargeUnits * -1),
        description = "Charge for " .. usage .. recMtype .. " disk I/O over allowance"
      })
  end
  return out
end
-- ************************************ CUSTOMER ASSET IO END BILLING *************************************
-- ************************************ IMAGE INSTANCE BILLING ***************************************************
function imageInstanceBillingRAMCPU (p)
  if (p == nil) then
    local confvalues = { {
        key = "per_cpu_core_units",
        name = "Per CPU Units",
        description = "Units to be charged per CPU allocated to the server using the image",
        measureType = "UNIT",
        validator = {validatorType = "NUMERIC_DOUBLE"}
        },{
        key = "per_ram_mb_units",
        name = "Per MB of RAM Units",
        description = "Units to be charged per MB of RAM allocated to the serve using the image",
        measureType = "UNIT",
        validator = {validatorType = "NUMERIC_DOUBLE"}
        },{
        key = "only_when_running",
        name = "Bill only when server running",
        description = "If this value is set to TRUE, charging will only take place when the server is running",
        validator = {validatorType = "ENUM", validateString = "TRUE,FALSE"},
        measureType = "STRING"
    }}
    return {
      ref = "_imageInstanceBillingRAMCPU",
      name = "Image usage billing on server RAM/CPU",
      description = "Bill for usage of an image based on the configured server RAM and CPU",
      permittedPCT = {"PCT_IMAGE_INSTANCE"},
      configuredValues = confvalues,
      api = "BILLING",
      version = 1
    }
  end
  local only_when_running = (p.billingComp:getBillingValue("only_when_running")):getValue()
  local doCharge = true
  -- If the only_when_running billing parameter is set we will only be charging if the server was running at the give time
  if (only_when_running == "TRUE") then
    -- print ("Looking for maximum running value between ".. p.lastBillingTime .. " and " .. p.currentBillingTime)
    local measuredHash = p.measureComp:getMAXMeasureBetween(p.lastBillingTime, p.currentBillingTime, "running")
    local uptime = measuredHash:get("running")
    if ((uptime == nil) or (uptime:getMeasurement() <= 0)) then
      doCharge = false
    end
  end
  if (doCharge) then
    local ret = {}
    local per_cpu_charge_val = p.billingComp:getBillingValue("per_cpu_core_units")
    local per_ram_charge_val = p.billingComp:getBillingValue("per_ram_mb_units")
    local usageHash = p.measureComp:getMAXMeasureBetween(p.lastBillingTime, p.currentBillingTime, "cpu, ram")
    if (usageHash and usageHash:size() > 0) then
      if (per_cpu_charge_val) then
        local cpuM = usageHash:get("cpu")
        if (cpuM) then
          local cval = tonumber(per_cpu_charge_val:getValue())
          if (cval > 0 ) then
            local u = cpuM:getMeasurement() * cval * -1
            table.insert(ret, {units = u , description = "Charge for Image CPU "..cpuM:getMeasurement() })
          end
        end
      end
      if (per_ram_charge_val) then
        local ramM = usageHash:get("ram")
        if (ramM) then
          local cval = tonumber(per_ram_charge_val:getValue())
          if (cval > 0 ) then
            local u = ramM:getMeasurement() * cval * -1
            table.insert(ret, {units = u , description = "Charge for Image RAM "..ramM:getMeasurement().." MB" })
          end
        end
      end
    end
    return ret
  end
  return nil
end
function imageInstanceBillingStandard (p)
  if (p == nil) then
    local confvalues = {
      getRepeatUnitsDefinitionTable(),{
        key = "only_when_running",
        name = "Bill only when server running",
        description = "If this value is set to TRUE, charging will only take place when the server is running",
        validator = {validatorType = "ENUM", validateString = "TRUE,FALSE"},
        measureType = "STRING"
    }}
    return {
      ref = "_imageInstanceBillingStandard",
      name = "Image usage billing on a fixed rate",
      description = "Bill a fixed amount for the image usage ",
      permittedPCT = {"PCT_IMAGE_INSTANCE"},
      configuredValues = confvalues,
      api = "BILLING",
      version = 1
    }
  end
  local only_when_running = (p.billingComp:getBillingValue("only_when_running")):getValue()
  local doCharge = true
  local chargeUnits = getRepeatUnits(p.billingComp)
  -- If the only_when_running billing parameter is set we will only be charging if the server was running at the give time
  if (only_when_running == "TRUE") then
    -- print ("Looking for maximum running value between ".. p.lastBillingTime .. " and " .. p.currentBillingTime)
    local measuredHash = p.measureComp:getMAXMeasureBetween(p.lastBillingTime, p.currentBillingTime, "running")
    local uptime = measuredHash:get("running")
    if ((uptime == nil) or (uptime:getMeasurement() <= 0)) then
      doCharge = false
    end
  end
  if (doCharge and (chargeUnits~=nil) and (chargeUnits > 0 )) then
    return {{units=(chargeUnits * -1), description = "Charge for Image usage on server "}}
  end
end
-- ************************************ END OF IMAGE INSTANCE BILLING ********************************************
-- ************************************ SUBSCRIPTION BILLING ********************************************
function getChargeAmount(p)
  local chargeAmount = p.billingComp:getBillingValue("charge_amount")
  if(chargeAmount ~= nil) then
    local chargeValue = chargeAmount:getValue()
    if(chargeValue ~= nil) then
      return tonumber(chargeValue)
    end
  end
  return nil
end
function advanceSubscriptionBilling(p)
  if (p == nil) then
    return {
      ref = "_advance_subscription_billing",
      name = "Advance Subscription Billing",
      description = "Subscription billing which will charge a set amount at the start of each billing period. If the subscription is cancelled you will be refunded the outstanding amount.",
      permittedPCT = {"PCT_CUST_CREDIT"},
      configuredValues = {
        {
          key = "charge_amount",
          name = "Charge amount",
          description = "The amount that will be charged at the start of each billing period",
          validator = {validatorType = "NUMERIC_DOUBLE"},
          measureType = "NUMERIC"
        },
      },
      api = "BILLING",
      version = 1
    }
  end
  if(p.lastBillingRun) then
    local amount = getChargeAmount(p)
    local refundAmount = amount - (amount * p.billingFactor)
    if(refundAmount >= 0.01) then
      return {{charge = refundAmount * -1 , description = "Subscription Refund" }}
    end
  else
    local amount = getChargeAmount(p)
    if (amount >= 0.01) then
      return {{charge = amount , description = "Subscription Charge" }}
    end
  end
  return nil
end
-- ************************************ END OF SUBSCRIPTION BILLING ********************************************
-- Registers all the billing functions
function register ()
  -- print ("Loading default billing methods")
  return {
    "fixedResourceBilling",
    "serverAllocatedCPUBilling",
    "serverAllocatedRAMBilling",
    "sizeAllocatedBilling",
    "diskIOUsageBilling",
    "networkIOUsageBilling",
    "unitTopUpBilling",
    "fetchResourceBilling",
    "customerResourceAssetBilling",
    "subnetBillingIpV4",
    "imageInstanceBillingStandard",
    "imageInstanceBillingRAMCPU",
    "customerNetIOBilling",
    "customerDiskIOUsageBilling",
    "arbitraryUnitBilling",
    "fixedUnitBilling",
    "advanceSubscriptionBilling"
    
  }
end

  • No labels