Skip to content

Commit

Permalink
Merge pull request #1133 from mountaindude/805
Browse files Browse the repository at this point in the history
805
  • Loading branch information
mountaindude authored May 6, 2024
2 parents c653d69 + 97e965a commit e7e171a
Show file tree
Hide file tree
Showing 7 changed files with 399 additions and 52 deletions.
18 changes: 14 additions & 4 deletions src/butler.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ const start = async () => {
globals.logger.verbose(`START: Globals init done: ${globals.initialised}`);

const setupServiceMonitorTimer = (await import('./lib/service_monitor.js')).default;
const { setupQlikSenseLicenseMonitor, setupQlikSenseLicenseRelease } = await import('./lib/qliksense_license.js');
const { setupQlikSenseAccessLicenseMonitor, setupQlikSenseLicenseRelease, setupQlikSenseServerLicenseMonitor } = await import(
'./lib/qliksense_license.js'
);
const { setupQlikSenseVersionMonitor } = await import('./lib/qliksense_version.js');

// The build function creates a new instance of the App class and returns it.
Expand Down Expand Up @@ -207,15 +209,23 @@ const start = async () => {
setupQlikSenseVersionMonitor(globals.config, globals.logger);
}

// Set up Qlik Sense license monitoring, if enabled in the config file
// Set up Qlik Sense server license monitoring, if enabled in the config file
if (
globals.config.has('Butler.qlikSenseLicense.serverLicenseMonitor.enable') &&
globals.config.get('Butler.qlikSenseLicense.serverLicenseMonitor.enable') === true
) {
setupQlikSenseServerLicenseMonitor(globals.config, globals.logger);
}

// Set up Qlik Sense access license monitoring, if enabled in the config file
if (
globals.config.has('Butler.qlikSenseLicense.licenseMonitor.enable') &&
globals.config.get('Butler.qlikSenseLicense.licenseMonitor.enable') === true
) {
setupQlikSenseLicenseMonitor(globals.config, globals.logger);
setupQlikSenseAccessLicenseMonitor(globals.config, globals.logger);
}

// Set up Qlik Sense license release, if enabled in the config file
// Set up Qlik Sense access license release, if enabled in the config file
// Enable only if at least one license type is enabled for automatic release
if (
globals.config.has('Butler.qlikSenseLicense.licenseRelease.enable') &&
Expand Down
14 changes: 14 additions & 0 deletions src/config/config-gen-api-docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,20 @@ Butler:

# Settings for monitoring Qlik Sense licenses
qlikSenseLicense:
serverLicenseMonitor:
enable: true
frequency: every 24 hours # https://bunkat.github.io/later/parsers.html#text
alert: # Alert if the number of days left on the license is below the threshold
# License expiry alerts on a global level are enabled here, then configured on
# a per-destination basis elsewhere in this config file.
thresholdDays: 60
destination:
influxDb: # Store license data in InfluxDB
enable: true
tag:
static: # Static attributes/tags to attach to the data sent to InflixDB
# - name: foo
# value: bar
licenseMonitor:
enable: false
frequency: every 6 hours # https://bunkat.github.io/later/parsers.html#text
Expand Down
24 changes: 19 additions & 5 deletions src/config/production_template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -155,18 +155,32 @@ Butler:

# Settings for monitoring Qlik Sense licenses
qlikSenseLicense:
licenseMonitor:
serverLicenseMonitor:
enable: false
frequency: every 24 hours # https://bunkat.github.io/later/parsers.html#text
alert: # Alert if the number of days left on the license is below the threshold
# License expiry alerts on a global level are enabled here, then configured on
# a per-destination basis elsewhere in this config file.
thresholdDays: 60
destination:
influxDb: # Store license data in InfluxDB
enable: false
tag:
static: # Static attributes/tags to attach to the data sent to InflixDB
- name: foo
value: bar
licenseMonitor: # Monitor Qlik Sense accesds license usage
enable: false
frequency: every 6 hours # https://bunkat.github.io/later/parsers.html#text
destination:
influxDb: # Store license data in InfluxDB
enable: true
enable: false
tag:
static: # Static attributes/tags to attach to the data sent to InflixDB
- name: foo
value: bar
licenseRelease:
enable: false # true/false. If true, Butler will release unused licenses according to settings below
licenseRelease: # Release unused Qlik Sense access licenses
enable: false # true/false. If true, Butler will release unused licenses according to settings below
dryRun: true # true/false. If true, Butler will not actually release any licenses, just log what it would have done.
frequency: every 6 hours # https://bunkat.github.io/later/parsers.html#text
neverRelease: # Various ways of defining which users should never have their licenses released
Expand Down Expand Up @@ -201,7 +215,7 @@ Butler:
releaseThresholdDays: 30 # Number of days a license can be unused before it is released
destination:
influxDb: # Store info about released licenses in InfluxDB
enable: true
enable: false
tag:
static: # Static attributes/tags to attach to the data sent to InflixDB
- name: foo
Expand Down
81 changes: 80 additions & 1 deletion src/lib/assert/assert_config_file.js
Original file line number Diff line number Diff line change
Expand Up @@ -1033,7 +1033,86 @@ export const configFileStructureAssert = async (config, logger) => {
configFileCorrect = false;
}

// Qlik Sense license monitoring
// Qlik Sense server license monitoring
if (!config.has('Butler.qlikSenseLicense.serverLicenseMonitor.enable')) {
logger.error('ASSERT CONFIG: Missing config file entry "Butler.qlikSenseLicense.serverLicenseMonitor.enable"');
configFileCorrect = false;
}

if (!config.has('Butler.qlikSenseLicense.serverLicenseMonitor.frequency')) {
logger.error('ASSERT CONFIG: Missing config file entry "Butler.qlikSenseLicense.serverLicenseMonitor.frequency"');
configFileCorrect = false;
}

if (!config.has('Butler.qlikSenseLicense.serverLicenseMonitor.alert.thresholdDays')) {
logger.error('ASSERT CONFIG: Missing config file entry "Butler.qlikSenseLicense.serverLicenseMonitor.alert.thresholdDays"');
configFileCorrect = false;
}

if (!config.has('Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.enable')) {
logger.error(
'ASSERT CONFIG: Missing config file entry "Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.enabled"'
);
configFileCorrect = false;
}

// Make sure all entries in Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static are objects with the following properties:
// {
// name: 'string',
// value: 'string'
// }
if (config.has('Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static')) {
const tagsStatic = config.get('Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static');

if (tagsStatic) {
if (!Array.isArray(tagsStatic)) {
logger.error(
'ASSERT CONFIG: "Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static" is not an array'
);
configFileCorrect = false;
} else {
tagsStatic.forEach((tag, index) => {
if (typeof tag !== 'object') {
logger.error(
`ASSERT CONFIG: "Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static[${index}]" is not an object`
);
configFileCorrect = false;
} else {
if (!Object.prototype.hasOwnProperty.call(tag, 'name')) {
logger.error(
`ASSERT CONFIG: Missing "name" property in "Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static[${index}]"`
);
configFileCorrect = false;
} else if (typeof tag.name !== 'string') {
logger.error(
`ASSERT CONFIG: "name" property in "Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static[${index}]" is not a string`
);
configFileCorrect = false;
}

if (!Object.prototype.hasOwnProperty.call(tag, 'value')) {
logger.error(
`ASSERT CONFIG: Missing "value" property in "Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static[${index}]"`
);
configFileCorrect = false;
} else if (typeof tag.value !== 'string') {
logger.error(
`ASSERT CONFIG: "value" property in "Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static[${index}]" is not a string`
);
configFileCorrect = false;
}
}
});
}
}
} else {
logger.error(
'ASSERT CONFIG: Missing config file entry "Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.enabled"'
);
configFileCorrect = false;
}

// Qlik Sense access license monitoring
if (!config.has('Butler.qlikSenseLicense.licenseMonitor.enable')) {
logger.error('ASSERT CONFIG: Missing config file entry "Butler.qlikSenseLicense.licenseMonitor.enable"');
configFileCorrect = false;
Expand Down
63 changes: 62 additions & 1 deletion src/lib/post_to_influxdb.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,68 @@ export async function postQlikSenseVersionToInfluxDB(qlikSenseVersion) {
globals.logger.verbose('INFLUXDB QLIK SENSE VERSION: Sent Qlik Sense version to InfluxDB');
}

// Function to store Qlik Sense license status to InfluxDB
// Function to store Qlik Sense server license status to InfluxDB
// 1st parameter is an object with the following structure:
// {
// "licenseExpired": <boolean>,
// "expiryDate": <date>,
// "expiryDateStr": "<string>
// "daysUntilExpiry": <number>,
// }
export async function postQlikSenseServerLicenseStatusToInfluxDB(qlikSenseServerLicenseStatus) {
globals.logger.verbose('INFLUXDB QLIK SENSE SERVER LICENSE STATUS: Sending Qlik Sense server license status to InfluxDB');

const instanceTag = globals.config.has('Butler.influxDb.instanceTag') ? globals.config.get('Butler.influxDb.instanceTag') : '';

// Get tags from config file
// Stored in array Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag
const configTags = globals.config.get('Butler.qlikSenseLicense.serverLicenseMonitor.destination.influxDb.tag.static');

const tags = {
butler_instance: instanceTag,
};

// Add tags from config file
if (configTags) {
// eslint-disable-next-line no-restricted-syntax
for (const item of configTags) {
tags[item.name] = item.value;
}
}

// Do a deep clone of the tags object
const tagsCloned = _.cloneDeep(tags);

// Build InfluxDB datapoint
let datapoint = [
{
measurement: 'qlik_sense_server_license',
tags: tagsCloned,
fields: {
license_expired: qlikSenseServerLicenseStatus.licenseExpired,
expiry_date: qlikSenseServerLicenseStatus.expiryDateStr,
days_until_expiry: qlikSenseServerLicenseStatus.daysUntilExpiry,
},
},
];

// Write to InfluxDB
const deepClonedDatapoint = _.cloneDeep(datapoint);
await globals.influx.writePoints(deepClonedDatapoint);

globals.logger.silly(
`INFLUXDB QLIK SENSE SERVER LICENSE STATUS: Influxdb datapoint for Qlik Sense server license status: ${JSON.stringify(
datapoint,
null,
2
)}`
);

datapoint = null;
globals.logger.verbose('INFLUXDB QLIK SENSE SERVER LICENSE STATUS: Sent Qlik Sense server license status to InfluxDB');
}

// Function to store Qlik Sense access license status to InfluxDB
// License JSON has the following structure:
// {
// "totalTokens": 0,
Expand Down
Loading

0 comments on commit e7e171a

Please sign in to comment.