Initial commit: Extract base Zabbix GraphQl - API functionality from VCR Project and add dynamic schema samples

This commit is contained in:
Andreas Hilbig 2026-01-05 21:05:35 +01:00
commit 92ffe71684
42 changed files with 4234 additions and 0 deletions

194
src/api/resolver_helpers.ts Normal file
View file

@ -0,0 +1,194 @@
import {isObjectType} from "graphql";
import {logger} from "../logging/logger.js";
/*
As a default all . - seperators within a key shall be replaced by a Capital letter of the following word
*/
function defaultKeyMappingFunction(key: string): string {
let words = key.split(".")
for (let i = 1; i < words.length; i++) {
if (words[i]) {
words[i] = words[i][0].toUpperCase() + words[i].substring(1);
}
}
return words.join("")
}
export function createHierarchicalValueFieldResolver(
schema: any, typename: string,
sourceFieldMapper: (fieldname: string, parent: any, objectTypeRequested: boolean) => { [p: string]: any } | null): {
[fieldname: string]: any
} {
let resolver: { [fieldname: string]: any } = {}
let type = schema.getType(typename)
if (isObjectType(type)) {
let fields = type.getFields();
for (let fieldsKey in fields) {
let field = fields[fieldsKey];
resolver[field.name] = (parent: any) => sourceFieldMapper(field.name, parent, isObjectType(field.type));
}
}
return resolver
}
export function zabbixItemValueSourceFieldMapper(
fieldname: string,
parent: {
items: [{ itemid: string, key_: string; name: string, lastvalue: any }],
[key: string]: any
},
objectTypeRequested: boolean
) {
let result: { [p: string]: any; } | any = parent[fieldname]
if (!parent.items) {
logger.debug(`No parent.items found: ${JSON.stringify(parent)}`)
return result
}
parent.items.forEach(
item => {
result = mapAttributeListToGraphQlType(result, objectTypeRequested, fieldname, {
key: item.key_,
value: item.lastvalue
}
)
}
)
logger.info(`Device data mapped: ${JSON.stringify(result)}`)
return result;
}
export function zabbixTagsValueSourceFieldMapper(
fieldname: string,
tags: [{ tag: string, value: any }],
objectTypeRequested: boolean
) {
let result: { [p: string]: any; } | any = {}
if (!tags) {
logger.debug(`No parent.tags or parent.inheritedTags found: ${JSON.stringify(tags)}`)
return result
}
tags.forEach(
tag => {
result = mapAttributeListToGraphQlType(result, objectTypeRequested, fieldname, {
key: tag.tag,
value: tag.value
}
)
}
)
logger.info(`Device tags mapped: ${JSON.stringify(result)}`)
return result;
}
function mapAttributeListToGraphQlType(result: {
[p: string]: any;
} | any, objectTypeRequested: boolean, fieldname: string, item: { value: any, key: string }) {
logger.debug(`Resolving ${objectTypeRequested ? "attributes of object" : "value of scalar"} field parent.${fieldname} (${result}), looking up key from item ${JSON.stringify(item)}`)
if (item.key) {
if (objectTypeRequested) {
function addRecursive(
result: { [x: string]: any; } | null,
fieldHierarchy: string[],
value: any
) {
if (!fieldHierarchy || fieldHierarchy.length == 0) {
return result
} else {
if (!result) {
result = {}
}
if (fieldHierarchy.length == 1) {
let fieldTokenName = fieldHierarchy[0];
const TOKEN_SEPERATOR = "_";
// As value is not typed we must parse the type in order to transform it to a strongly
// typed value which is expected by Graphql
// Example: Graphql does not accept a string "true" and empty string as false as boolean
// In order to facilitate this it is possible (but not mandatory) to provide typehints
// to item keys by prepending the fieldTokenName with a typehint, following by an underscore.
// I.e. if a key is prefixed with str_, bool_, float_ or json_ this will be stripped
// and the value will be cast to the appropriate type.
// If no typeHint is provided and the type is string it will be tried to create a float
// or a boolean out of it.
let typeHintToken = fieldTokenName.split(TOKEN_SEPERATOR);
let typeHint = undefined;
if (typeHintToken.length > 0) {
switch (typeHintToken[0]) {
case "str":
case "bool":
case "float":
case "json":
typeHint = typeHintToken[0];
// Remove typehint + token separator from field name - if the typehint
// is followed by another token. If not (e.g. fieldTokenName="str" only) the
// token is considered to be a valid typehint, but it is not stripped from the
// fieldTokenName (i.e. nothing happens to the fieldTokenName in that case)
if (typeHintToken.length > 1) {
fieldTokenName = fieldTokenName.substring(typeHint.length + 1);
}
}
}
let fieldValue = undefined;
if (typeof value === 'string' && (typeHint == "bool" || value.toLowerCase() === 'true' || value.toLowerCase() === 'false')) {
fieldValue = (value.toLowerCase() === 'true');
// logger.debug("Parsing attribute '" + fieldTokenName + "' as true/false string, type=" + typeof fieldValue + ", value=" + fieldValue);
} else if (typeof value === 'string' && value !== '' && !isNaN(Number(value))) {
fieldValue = Number(value);
// logger.debug("Parsing attribute '" + fieldTokenName + "' as number, type=" + typeof fieldValue + ", value=" + fieldValue);
} else if (typeof value === 'string' && typeHint == "json") {
logger.debug("Trying to parse attribute value as json, typeHint=" + typeHint + ", type=" + typeof value + ", value=" + value);
if (!value) {
// Empty string values will be considered to be an unset JSON-Object if there is a typeHin=="json"
fieldValue = undefined;
} else {
try {
fieldValue = JSON.parse(value);
// logger.debug("Parsing attribute '" + fieldTokenName + "' as json, type=" + typeof fieldValue + ", value=" + value);
} catch (e) {
logger.debug("Unable to parse attribute value as json, passing unmodified, type=" + typeof value + ", value=" + value);
fieldValue = value;
}
}
} else {
fieldValue = value;
// logger.debug("Passing attribute '" + fieldTokenName + "' unmodified, type=" + typeof fieldValue + ", value=" + fieldValue);
}
if (fieldValue !== undefined) {
// logger.debug(`length of parsed field hierarchy is 1: Setting fieldTokenName=${fieldTokenName} to ${value}`);
result[fieldTokenName] = fieldValue;
} /*else {
// logger.debug(`length of parsed field hierarchy is 1: Skipping to set fieldHierarchy=${fieldHierarchy} to ${value} (empty value)`);
}*/
} else {
result[fieldHierarchy[0]] = addRecursive(result[fieldHierarchy[0]], fieldHierarchy.slice(1), value)
}
}
logger.debug(`Adding attribute ${fieldHierarchy[0]}, result: ${JSON.stringify(result)}`)
return result
}
let fieldHierarchy = item.key.split(".");
if (fieldHierarchy[0] == fieldname) {
result = addRecursive(result, fieldHierarchy.slice(1), item.value);
logger.debug(`Detected matching item key ${fieldname} in item , result: ${JSON.stringify(result)}`)
} else {
logger.debug(`Item key ${fieldHierarchy[0]} not matched fieldname=${fieldname}, result: ${JSON.stringify(result)}`)
}
} else {
let keyInCamel = defaultKeyMappingFunction(item.key);
if (keyInCamel == fieldname) {
result = item.value
logger.debug(`Detected matching item key ${keyInCamel} in item , result: ${JSON.stringify(result)}`)
}
}
}
return result;
}

257
src/api/resolvers.ts Normal file
View file

@ -0,0 +1,257 @@
import {
DeviceCommunicationType,
DeviceStatus,
MutationCreateHostArgs,
MutationImportHostsArgs,
MutationImportHostGroupsArgs,
MutationImportUserRightsArgs,
Permission,
QueryAllHostsArgs,
QueryAllHostGroupsArgs,
QueryExportDeviceValueHistoryArgs,
QueryExportUserRightsArgs,
QueryHasPermissionsArgs,
QueryUserPermissionsArgs,
Resolvers,
StorageItemType, Host,
} from "../generated/graphql.js";
import {HostImporter} from "../execution/host_importer";
import {HostValueExporter} from "../execution/host_exporter";
import {logger} from "../logging/logger.js";
import {ParsedArgs, ZabbixPermissionsHelper, ZabbixRequest} from "../datasources/zabbix-request.js";
import {ZabbixCreateHostRequest, ZabbixQueryHostsRequestWithItemsAndInventory,} from "../datasources/zabbix-hosts.js";
import {ZabbixQueryHostgroupsParams, ZabbixQueryHostgroupsRequest} from "../datasources/zabbix-hostgroups.js";
import {
ZabbixExportUserGroupArgs,
ZabbixExportUserGroupsRequest,
ZabbixImportUserGroupsParams,
ZabbixImportUserGroupsRequest
} from "../datasources/zabbix-usergroups.js";
import {
ZabbixImportUserRolesParams,
ZabbixImportUserRolesRequest,
ZabbixQueryUserRolesRequest
} from "../datasources/zabbix-userroles.js";
import {ZABBIX_EDGE_DEVICE_BASE_GROUP, zabbixAPI} from "../datasources/zabbix-api";
import {GraphQLInterfaceType, GraphQLList} from "graphql/type";
export function createResolvers(): Resolvers {
// @ts-ignore
// @ts-ignore
return {
Query: {
userPermissions: async (_parent: any, objectNamesFilter: QueryUserPermissionsArgs, {
zabbixAuthToken,
cookie
}: any) => {
return ZabbixPermissionsHelper.getUserPermissions(zabbixAPI, zabbixAuthToken, cookie, objectNamesFilter.objectNames)
},
hasPermissions: async (_parent: any, args: QueryHasPermissionsArgs, {zabbixAuthToken, cookie}: any) => {
return ZabbixPermissionsHelper.hasUserPermissions(zabbixAPI, args, zabbixAuthToken, cookie)
},
locations: (_parent: any, args: Object, {dataSources, zabbixAuthToken}: any) => {
return dataSources.zabbixAPI.getLocations(zabbixAuthToken, new ParsedArgs(args));
},
apiVersion: () => {
return process.env.API_VERSION ?? "unknown"
},
zabbixVersion: async () => {
return await new ZabbixRequest<string>("apiinfo.version").executeRequestThrowError(
zabbixAPI)
},
login: async (_parent, args) => {
return await new ZabbixRequest<any>("user.login").executeRequestThrowError(
zabbixAPI, new ParsedArgs(args))
},
logout: async (_parent, _args, {zabbixAuthToken, cookie}: any) => {
return await new ZabbixRequest<any>("user.logout", undefined, cookie).executeRequestThrowError(zabbixAPI);
},
allHosts: async (_parent: any, args: QueryAllHostsArgs, {
zabbixAuthToken,
cookie, dataSources
}: any) => {
args.tag_hostType ??= [ZABBIX_EDGE_DEVICE_BASE_GROUP];
return await new ZabbixQueryHostsRequestWithItemsAndInventory(zabbixAuthToken, cookie)
.executeRequestThrowError(
dataSources.zabbixAPI, new ParsedArgs(args)
)
},
allHostGroups: async (_parent: any, args: QueryAllHostGroupsArgs, {
zabbixAuthToken,
cookie
}: any) => {
if (!args.search_name) {
args.search_name = ZABBIX_EDGE_DEVICE_BASE_GROUP + "/*"
}
return await new ZabbixQueryHostgroupsRequest(zabbixAuthToken, cookie).executeRequestThrowError(
zabbixAPI, new ZabbixQueryHostgroupsParams(args)
)
},
exportDeviceValueHistory: (_parent: any, args: QueryExportDeviceValueHistoryArgs, {
zabbixAuthToken,
cookie
}: any) => {
return HostValueExporter.exportDeviceData(args, zabbixAuthToken, cookie)
},
exportUserRights: async (_, args: QueryExportUserRightsArgs, {
zabbixAuthToken,
cookie
}: any) => {
let groups = await new ZabbixExportUserGroupsRequest(zabbixAuthToken, cookie)
.executeRequestThrowError(zabbixAPI, new ZabbixExportUserGroupArgs(args.name_pattern, args.exclude_hostgroups_pattern));
let roles = await new ZabbixQueryUserRolesRequest(zabbixAuthToken, cookie)
.executeRequestThrowError(zabbixAPI, new ParsedArgs(args.name_pattern ? {name_pattern: args.name_pattern} : undefined));
return {
userGroups: groups,
userRoles: roles
}
}
},
Mutation: {
createHost: async (_parent: any, args: MutationCreateHostArgs, {
zabbixAuthToken,
cookie
}: any) => {
return await new ZabbixCreateHostRequest(zabbixAuthToken, cookie).executeRequestThrowError(
zabbixAPI,
new ParsedArgs(args)
)
},
importHostGroups: async (_parent: any, args: MutationImportHostGroupsArgs, {
zabbixAuthToken,
cookie
}: any) => {
return HostImporter.importHostGroups(args.hostGroups, zabbixAuthToken, cookie)
},
importHosts: async (_parent: any, args: MutationImportHostsArgs, {
zabbixAuthToken,
cookie
}: any) => {
return HostImporter.importHosts(args.devices, zabbixAuthToken, cookie)
},
importUserRights: async (_, args: MutationImportUserRightsArgs, {
zabbixAuthToken,
cookie
}: any) => {
let userRoleImportArgs = structuredClone(args);
let userGroupImportArgs = structuredClone(args);
let userRolesImport = userRoleImportArgs.input.userRoles ?
await new ZabbixImportUserRolesRequest(zabbixAuthToken, cookie)
.executeRequestThrowError(zabbixAPI,
new ZabbixImportUserRolesParams(userRoleImportArgs.input.userRoles, userRoleImportArgs.dryRun)) : null;
let userGroupsImport = userGroupImportArgs.input.userGroups ?
await new ZabbixImportUserGroupsRequest(zabbixAuthToken, cookie)
.executeRequestThrowError(zabbixAPI,
new ZabbixImportUserGroupsParams(userGroupImportArgs.input.userGroups, userGroupImportArgs.dryRun)) : null;
return {
userRoles: userRolesImport,
userGroups: userGroupsImport
}
}
},
Host: {
// @ts-ignore
__resolveType: function (host: Host, _context, info ): string {
const deviceType = host.deviceType ?? "";
if (deviceType) {
logger.info(`checking host ${host.name} for deviceType - found ${deviceType}`);
let interfaceType: GraphQLInterfaceType = (info.returnType instanceof GraphQLList ?
info.returnType.ofType : info.returnType) as GraphQLInterfaceType
if (info.schema.getImplementations(interfaceType).objects.some((impl: { name: string; }) => impl.name === deviceType)) {
return deviceType;
}
return "GenericDevice"
}
logger.info(`checking host ${host.name} for deviceType - no device type found, returning as ZabbixHost`);
return "ZabbixHost"; // Return "generic" device host as a default if no templates are assigned
}
},
Inventory: {
/*
Always map inventory.location,... fields to location object
*/
// @ts-ignore
location: (parent: { location: string; location_lon: string; location_lat: string; }) => {
return {
name: parent.location,
longitude: parent.location_lon,
latitude: parent.location_lat,
}
}
},
UserRoleRules: {
ui_default_access: (parent: any) => {
return parent["ui.default_access"]
},
modules_default_access: (parent: any) => {
return parent["modules.default_access"]
},
actions_default_access: (parent: any) => {
return parent["actions.default_access"]
},
api_access: (parent: any) => {
return parent["api.access"]
},
api_mode: (parent: any) => {
return parent["api.mode"]
},
},
// Enum Value Mappings
Permission: {
READ: Permission.Read,
READ_WRITE: Permission.ReadWrite,
DENY: Permission.Deny
},
DeviceCommunicationType: {
ZABBIX_AGENT: DeviceCommunicationType.ZABBIX_AGENT,
ZABBIX_AGENT_ACTIVE: DeviceCommunicationType.ZABBIX_AGENT_ACTIVE,
ZABBIX_TRAP: DeviceCommunicationType.ZABBIX_TRAP,
SIMPLE_CHECK: DeviceCommunicationType.SIMPLE_CHECK,
ZABBIX_INTERNAL_ITEM: DeviceCommunicationType.ZABBIX_INTERNAL_ITEM,
DEPENDANT_ITEM: DeviceCommunicationType.DEPENDANT_ITEM,
HTTP_AGENT: DeviceCommunicationType.HTTP_AGENT,
SIMULATOR_CALCULATED: DeviceCommunicationType.SIMULATOR_CALCULATED,
SNMP_AGENT: DeviceCommunicationType.SNMP_AGENT,
SNMP_TRAP: DeviceCommunicationType.SNMP_TRAP,
IPMI_AGENT: DeviceCommunicationType.IPMI_AGENT,
JMX_AGENT: DeviceCommunicationType.JMX_AGENT,
SIMULATOR_JAVASCRIPT: DeviceCommunicationType.SIMULATOR_JAVASCRIPT,
DATABASE_MONITOR: DeviceCommunicationType.DATABASE_MONITOR,
},
DeviceStatus: {
ENABLED: DeviceStatus.ENABLED,
DISABLED: DeviceStatus.DISABLED
},
SensorValueType: {
NUMERIC: 0,
CHARACTER: 1,
LOG: 2,
NUMERIC_UNSIGNED: 3,
TEXT: 4
},
StorageItemType: {
TEXT: StorageItemType.Text,
FLOAT: StorageItemType.Float,
INT: StorageItemType.Int,
}
}
}

59
src/api/schema.ts Normal file
View file

@ -0,0 +1,59 @@
import {
createHierarchicalValueFieldResolver,
zabbixItemValueSourceFieldMapper,
zabbixTagsValueSourceFieldMapper
} from "./resolver_helpers.js";
import {makeExecutableSchema, mergeSchemas} from "@graphql-tools/schema";
import {readFileSync} from "fs";
import {GraphQLSchema} from "graphql/type";
import {createResolvers} from "./resolvers.js";
const createZabbixHierarchicalDeviceFieldResolver =
(typename: string, schema: any, additionalMappings: { [p: string]: any } = {}) => {
return {
...createHierarchicalValueFieldResolver(schema, typename, zabbixItemValueSourceFieldMapper),
...additionalMappings
}
}
const createZabbixHierarchicalDeviceTagsResolver =
(typename: string, schema: any, additionalMappings: { [p: string]: any } = {}) => {
return {
...createHierarchicalValueFieldResolver(schema, typename, zabbixTagsValueSourceFieldMapper),
...additionalMappings
}
}
export async function schema_loader(): Promise<GraphQLSchema> {
const resolvers = createResolvers();
let typeDefs: string = readFileSync('./schema.graphql', {encoding: 'utf-8'});
if (process.env.ADDITIONAL_SCHEMAS) {
for (const schema of process.env.ADDITIONAL_SCHEMAS.split(",")){
typeDefs += readFileSync(schema, {encoding: 'utf-8'});
}
}
let originalSchema =
makeExecutableSchema({
typeDefs,
resolvers,
});
let additionalMappings = {
tags: (parent: { tags: any; inheritedTags: any }) => {
return (parent.tags || []).concat(parent.inheritedTags || [])
}
}
let genericResolvers: Record<string, any> = {
Device: createZabbixHierarchicalDeviceFieldResolver("Device", originalSchema,additionalMappings ),
GenericDevice: createZabbixHierarchicalDeviceFieldResolver("GenericDevice", originalSchema, additionalMappings),
}
if (process.env.ADDITIONAL_RESOLVERS) {
for (const resolver of process.env.ADDITIONAL_RESOLVERS.split(",")){
genericResolvers[resolver] = createZabbixHierarchicalDeviceFieldResolver(resolver, originalSchema, additionalMappings)
}
}
return mergeSchemas({
schemas: [originalSchema],
// TODO Generate resolvers for all schema types with @generateZabbix directive automatically
resolvers: genericResolvers
});
}

98
src/api/start.ts Normal file
View file

@ -0,0 +1,98 @@
import http from "http";
import {schema_loader} from "./schema.js";
import {GraphQLSchema} from "graphql/type";
import {ApolloServer} from "@apollo/server";
import {expressMiddleware} from '@as-integrations/express4';
import express from 'express';
import cors from "cors";
import {ApolloServerPluginDrainHttpServer} from '@apollo/server/plugin/drainHttpServer';
import {logger} from "../logging/logger.js";
import {zabbixAPI, zabbixRequestAuthToken} from "../datasources/zabbix-api";
import {WebSocketServer} from "ws";
import {useServer} from "graphql-ws/lib/use/ws";
const GRAPHQL_PATH = "/"
const GRAPHQL_PORT = 4000
export function startAPi() {
startApolloServer().then(
r => {
logger.info(`🚀 API ready at http://localhost:` + GRAPHQL_PORT + GRAPHQL_PATH);
});
}
async function startApolloServer() {
return schema_loader().then(async (executableSchema: GraphQLSchema) => {
// Required logic for integrating with Express
const app = express();
// Our httpServer handles incoming requests to our Express app.
// Below, we tell Apollo Server to "drain" this httpServer,
// enabling our servers to shut down gracefully.
const httpServer = http.createServer(app);
const wsServer = new WebSocketServer({
// This is the `httpServer` we created in a previous step.
server: httpServer,
// Pass a different path here if app.use
// serves expressMiddleware at a different path
path: GRAPHQL_PATH,
});
// Hand in the schema we just created and have the
// WebSocketServer start listening.
const serverCleanup = useServer({schema: executableSchema}, wsServer);
const server: ApolloServer = new ApolloServer({
schema: executableSchema,
plugins: [
// Proper shutdown for the HTTP server.
ApolloServerPluginDrainHttpServer({httpServer}),
// Proper shutdown for the WebSocket server.
{
async serverWillStart() {
return {
async drainServer() {
await serverCleanup.dispose();
},
};
},
},
],
});
await server.start();
// Set up our Express middleware to handle CORS, body parsing,
// and our expressMiddleware function.
app.use(
GRAPHQL_PATH,
cors<cors.CorsRequest>(),
express.json(),
// expressMiddleware accepts the same arguments:
// an Apollo Server instance and optional configuration options
expressMiddleware(server, {
context: async ({req}) => {
const {cache} = server;
return {
cache,
dataSources: {
zabbixAPI: zabbixAPI,
},
zabbixAuthToken: req.headers["zabbix-auth-token"] ?? zabbixRequestAuthToken,
cookie: req.headers.cookie,
token: req.headers.token
};
},
}),
);
// Modified server startup
await new Promise<void>((resolve) => httpServer.listen({port: GRAPHQL_PORT}, resolve));
});
}

8
src/common_utils.ts Normal file
View file

@ -0,0 +1,8 @@
export function sleep(ms: number): { promise: Promise<void>, cancel: () => void } {
let timeoutId: NodeJS.Timeout;
const promise = new Promise<void>((resolve) => {
timeoutId = setTimeout(resolve, ms);
});
const cancel = () => clearTimeout(timeoutId);
return { promise, cancel };
}

View file

@ -0,0 +1,115 @@
import {
CacheOptions,
DataSourceConfig,
DataSourceFetchResult,
DataSourceRequest,
PostRequest,
RESTDataSource
} from "@apollo/datasource-rest";
import {logger} from "../logging/logger.js";
import {ParsedArgs, ZabbixErrorResult, ZabbixRequest, ZabbixResult} from "./zabbix-request.js";
export const zabbixRequestAuthToken = process.env.ZABBIX_AUTH_TOKEN_FOR_REQUESTS
export const zabbixSuperAuthToken = process.env.ZABBIX_AUTH_TOKEN
export const ZABBIX_EDGE_DEVICE_BASE_GROUP = process.env.ZABBIX_EDGE_DEVICE_BASE_GROUP || process.env.ZABBIX_ROADWORK_BASE_GROUP || "Baustellen-Devices"
export const FIND_ZABBIX_EDGE_DEVICE_BASE_GROUP_PREFIX = new RegExp(`^(${ZABBIX_EDGE_DEVICE_BASE_GROUP})\/`)
export class ZabbixAPI
extends RESTDataSource {
private static readonly MAX_LOG_REQUEST_BODY_LIMIT_LENGTH = 500
constructor(public baseURL: string, config?: DataSourceConfig) {
super(config);
logger.info("Connecting to Zabbix at url=" + this.baseURL)
}
override async fetch<Object>(path: string, incomingRequest: DataSourceRequest = {}): Promise<DataSourceFetchResult<Object>> {
logger.debug(`Zabbix request path=${path}, body=${JSON.stringify(incomingRequest.body).substring(0, ZabbixAPI.MAX_LOG_REQUEST_BODY_LIMIT_LENGTH)} (...)`)
let response_promise_original
try {
const response_promise: Promise<DataSourceFetchResult<Object>> = super.fetch("api_jsonrpc.php", incomingRequest);
try {
const response = await response_promise;
const body = response.parsedBody;
return await new Promise!<DataSourceFetchResult<Object>>((resolve, reject) => {
if (body && body.hasOwnProperty("result")) {
// @ts-ignore
let result: any = body["result"];
response.parsedBody = result;
if (result) {
logger.debug(`Found and returned result - length = ${result.length}`);
if (!Array.isArray(result) || !result.length) {
logger.debug(`Result: ${JSON.stringify(result)}`);
} else {
result.forEach((entry: any) => {
if (entry.hasOwnProperty("tags")) {
entry["tags"].forEach((tag: { tag: string; value: string; }) => {
entry[tag.tag] = tag.value;
});
}
if (entry.hasOwnProperty("inheritedTags")) {
entry["inheritedTags"].forEach((tag_1: { tag: string; value: string; }) => {
entry[tag_1.tag] = tag_1.value;
});
}
});
}
}
resolve(response);
} else {
let error_result: any;
if (body && body.hasOwnProperty("error")) {
// @ts-ignore
error_result = body["error"];
} else {
error_result = body;
}
logger.error(`No result for Zabbix request body=${JSON.stringify(incomingRequest.body)}: ${JSON.stringify(error_result)}`);
resolve(response);
}
});
} catch (reason) {
let msg = `Unable to retrieve response for request body=${JSON.stringify(incomingRequest.body)}: ${JSON.stringify(reason)}`;
logger.error(msg);
return response_promise
}
} catch (e) {
let msg = `Unable to retrieve response for request body=${JSON.stringify(incomingRequest.body)}: ${JSON.stringify(e)}`
logger.error(msg)
// @ts-ignore
return response_promise_original
}
}
public post<TResult = any>(path: string, request?: PostRequest<CacheOptions>): Promise<TResult> {
return super.post(path, request);
}
async executeRequest<T extends ZabbixResult, A extends ParsedArgs>(zabbixRequest: ZabbixRequest<T, A>, args?: A, throwApiError: boolean = true): Promise<T | ZabbixErrorResult> {
return throwApiError ? zabbixRequest.executeRequestThrowError(this, args) : zabbixRequest.executeRequestReturnError(this, args);
}
async requestByPath<T extends ZabbixResult, A extends ParsedArgs = ParsedArgs>(path: string, args?: A, authToken?: string | null, cookies?: string, throwApiError: boolean = true) {
return this.executeRequest<T, A>(new ZabbixRequest<T>(path, authToken, cookies), args, throwApiError);
}
async getLocations(args?: ParsedArgs, authToken?: string, cookies?: string) {
const hosts_promise = this.requestByPath("host.get", args, authToken, cookies);
return hosts_promise.then(response => {
// @ts-ignore
let locations = response.filter((host) => host.hasOwnProperty("inventory")).map(({inventory: x}) => x);
if (args?.distinct_by_name || args?.name_pattern) {
locations = locations.filter((loc: { location: string; }, i: number, arr: any[]) => {
return loc.location && (!args.distinct_by_name || arr.indexOf(arr.find(t => t.location === loc.location)) === i)
&& (!args.name_pattern || new RegExp(args.name_pattern).test(loc.location));
});
}
return locations;
});
}
}
export const zabbixAPI = new ZabbixAPI(process.env.ZABBIX_BASE_URL || "")

View file

@ -0,0 +1,128 @@
import {ZabbixAPI} from "./zabbix-api.js";
import {ApiError, SortOrder, StorageItemType} from "../generated/graphql.js";
import {ZabbixCreateOrUpdateStorageItemRequest} from "./zabbix-items.js";
import {ZabbixForceCacheReloadRequest} from "./zabbix-script.js";
import {logger} from "../logging/logger.js";
import {ApiErrorCode} from "../model/model_enum_values.js";
import {ParsedArgs, ZabbixParams, ZabbixRequest, ZabbixResult} from "./zabbix-request.js";
import {sleep} from "../common_utils";
export interface ZabbixValue {
key?: string,
host?: string,
value: string,
clock: number,
ns: number
}
export interface ZabbixExportValue extends ZabbixValue, ZabbixResult {
itemid?: string
}
export class ZabbixHistoryGetParams extends ParsedArgs {
time_from_ms: number | undefined
time_till_ms: number | undefined
constructor(public itemids: number[] | number | string | string[],
public output: string[] = ["value", "itemid", "clock", "ns"],
public limit: number | null = Array.isArray(itemids) ? itemids.length : 1,
public history: StorageItemType | string = StorageItemType.Text,
time_from?: Date,
time_until?: Date,
public sortfield: string[] = ["clock", "ns"],
public sortorder: SortOrder | null = SortOrder.Desc,
) {
super();
this.time_from_ms = time_from ? Math.floor(new Date(time_from).getTime() / 1000) : undefined
this.time_till_ms = time_until ? Math.floor(new Date(time_until).getTime() / 1000) : undefined
}
}
export class ZabbixQueryHistoryRequest extends ZabbixRequest<ZabbixExportValue[], ZabbixHistoryGetParams> {
constructor(authToken?: string | null, cookie?: string | null) {
super("history.get", authToken, cookie);
}
createZabbixParams(args?: ZabbixHistoryGetParams): ZabbixParams {
return {
itemids: args?.itemids,
output: args?.output,
limit: args?.limit,
history: args?.history?.valueOf(),
sortfield: args?.sortfield,
sortorder: args?.sortorder == SortOrder.Asc ? "ASC" : "DESC",
time_from: args?.time_from_ms,
time_till: args?.time_till_ms,
}
}
}
export interface ZabbixHistoryPushResult {
response: string,
data: { itemid: string, error?: string[] | ApiError }[],
error?: ApiError | string[]
}
export class ZabbixHistoryPushRequest extends ZabbixRequest<ZabbixHistoryPushResult> {
constructor(authToken?: string | null, cookie?: string) {
super("history.push", authToken, cookie);
}
}
export class ZabbixStoreObjectInItemHistoryRequest extends ZabbixRequest<ZabbixHistoryPushResult> {
// After creating an item or host zabbix needs some time before the created object can be referenced in other
// operations - the reason is the config-cache. In case of having ZBX_CACHEUPDATEFREQUENCY=1 (seconds) set within the
// Zabbix - config the delay of 1 second will be sufficient
private static readonly ZABBIX_DELAY_UNTIL_CONFIG_CHANGED: number = 0
public itemid: number | undefined
constructor(authToken?: string | null, cookie?: string) {
super("history.push.jsonobject", authToken, cookie);
}
async prepare(zabbixAPI: ZabbixAPI, args?: ParsedArgs): Promise<any> {
// Create or update zabbix Item
let success = false;
this.itemid = Number(args?.getParam("itemid"))
let timeoutForValueUpdate = this.itemid ? 0 : ZabbixStoreObjectInItemHistoryRequest.ZABBIX_DELAY_UNTIL_CONFIG_CHANGED;
// Create or update controlprogram - item
let result: {
"itemids": string[]
} | undefined = await new ZabbixCreateOrUpdateStorageItemRequest(
this.itemid ? "item.update.storeiteminhistory" : "item.create.storeiteminhistory",
this.authToken, this.cookie).executeRequestThrowError(zabbixAPI, args)
// logger.debug(`Create/update item itemid=${this.itemid}, hostid=${this.zabbixHostId} lead to result=`, JSON.stringify(result));
if (result && result.hasOwnProperty("itemids") && result.itemids.length > 0) {
this.itemid = Number(result.itemids[0]);
let scriptExecResult =
await new ZabbixForceCacheReloadRequest(this.authToken, this.cookie).executeRequestThrowError(zabbixAPI)
if (scriptExecResult.response != "success") {
logger.error(`cache reload not successful: ${scriptExecResult.value}`)
}
await sleep(timeoutForValueUpdate).promise
}
if (!this.itemid) {
this.prepResult = {
error: {
message: "Unable to create/update item",
code: ApiErrorCode.ZABBIX_NO_ITEM_PUSH_ITEM,
path: this.path,
args: args,
}
}
}
}
createZabbixParams(args?: ParsedArgs): ZabbixParams {
return {
itemid: this.itemid,
value: JSON.stringify(args?.getParam("value"))
}
}
}

View file

@ -0,0 +1,119 @@
import {isZabbixErrorResult, ParsedArgs, ZabbixParams, ZabbixRequest} from "./zabbix-request.js";
import {Permission} from "../generated/graphql.js";
import {
FIND_ZABBIX_EDGE_DEVICE_BASE_GROUP_PREFIX,
ZABBIX_EDGE_DEVICE_BASE_GROUP,
ZabbixAPI,
zabbixSuperAuthToken
} from "./zabbix-api";
import {logger} from "../logging/logger";
export interface CreateHostGroupResult {
groupids: string[]
}
const hostGroupReadWritePermissions = {
permissions: [
{
objectName: "Hostgroup/ConstructionSite",
permission: Permission.ReadWrite
}]
}
const hostGroupReadPermissions = {
permissions: [
{
objectName: "Hostgroup/ConstructionSite",
permission: Permission.Read
}]
}
export class ZabbixCreateHostGroupRequest extends ZabbixRequest<CreateHostGroupResult> {
constructor(_authToken?: string | null, cookie?: string) {
super("hostgroup.create", zabbixSuperAuthToken, cookie, hostGroupReadWritePermissions);
}
}
export class ZabbixDeleteHostGroupRequest extends ZabbixRequest<{
"groupids": string []
}> {
constructor(_authToken?: string | null, cookie?: string) {
super("hostgroup.delete", zabbixSuperAuthToken, cookie, {
permissions: [
{
objectName: "Hostgroup/ConstructionSite",
permission: Permission.ReadWrite
}]
});
}
}
export class ZabbixQueryHostgroupsParams extends ParsedArgs {
search_name: string | undefined
constructor(args?: any) {
super(args);
if ("search_name" in args && typeof (args.search_name) == "string") {
this.search_name = args.search_name
delete args.search_name
}
}
}
export type ZabbixQueryHostgroupsResult = {
groupid: string,
name: string,
uuid: string
}
export class ZabbixQueryHostgroupsRequest extends ZabbixRequest<ZabbixQueryHostgroupsResult[],
ZabbixQueryHostgroupsParams> {
constructor(authToken?: string | null, cookie?: string | null, hostGroupReadPermissions?: any) {
super("hostgroup.get", authToken, cookie, hostGroupReadPermissions,);
}
createZabbixParams(args?: ZabbixQueryHostgroupsParams): ZabbixParams {
let search = args?.search_name ? {
"search": {
"name": [
args.search_name
]
}
} : undefined
return {
"searchWildcardsEnabled": true,
"output": ["groupid", "name", "uuid"],
...args?.zabbix_params,
...search
}
};
}
export class GroupHelper {
public static groupFullName(groupName: string) {
return groupName == ZABBIX_EDGE_DEVICE_BASE_GROUP ? groupName : `${ZABBIX_EDGE_DEVICE_BASE_GROUP}/${GroupHelper.groupShortName(groupName)}`
}
static groupShortName(groupName: string) {
return groupName.replace(FIND_ZABBIX_EDGE_DEVICE_BASE_GROUP_PREFIX, "")
}
public static async findHostGroupIdsByName(groupNames: string[], zabbixApi: ZabbixAPI, zabbixAuthToken?: string, cookie?: string) {
let result: number[] = []
for (let groupName of groupNames) {
let queryGroupsArgs = new ZabbixQueryHostgroupsParams({
filter_name: GroupHelper.groupFullName(groupName)
});
let groups = await new ZabbixQueryHostgroupsRequest(zabbixAuthToken, cookie).executeRequestReturnError(zabbixApi, queryGroupsArgs)
if (isZabbixErrorResult(groups) || !groups?.length) {
logger.error(`Unable to find groupName=${groupName}`)
return []
}
result.push(...groups.map((group) => Number(group.groupid)))
}
return result
}
}

View file

@ -0,0 +1,303 @@
import {Host, ZabbixHost} from "../generated/graphql.js";
import {ZabbixAPI} from "./zabbix-api.js";
import {
isZabbixErrorResult,
ParsedArgs,
ZabbixErrorResult,
ZabbixParams,
ZabbixRequest,
ZabbixResult
} from "./zabbix-request.js";
import {QueryZabbixItemResponse} from "./zabbix-items.js";
import {ZabbixExportValue, ZabbixHistoryGetParams, ZabbixQueryHistoryRequest} from "./zabbix-history.js";
export class ZabbixQueryHostsGenericRequest<T extends ZabbixResult> extends ZabbixRequest<T> {
public static PATH = "host.get";
constructor(path: string, authToken?: string | null, cookie?: string | null) {
super(path, authToken, cookie);
}
createZabbixParams(args?: ParsedArgs): ZabbixParams {
return {
...super.createZabbixParams(args),
selectParentTemplates: [
"templateid",
"name"
],
selectTags: [
"tag",
"value"
],
selectInheritedTags: [
"tag",
"value"
],
selectHostGroups: ["groupid", "name", "uuid"],
output: [
"hostid",
"host",
"name",
"hostgroups",
"description",
"parentTemplates"
]
};
}
}
export class ZabbixQueryHostsMetaRequest extends ZabbixQueryHostsGenericRequest<Host[]> {
public static PATH = "host.get.meta";
constructor(authToken?: string | null, cookie?: string | null) {
super(ZabbixQueryHostsMetaRequest.PATH, authToken, cookie);
}
createZabbixParams(args?: ParsedArgs): ZabbixParams {
return {
...super.createZabbixParams(args),
inheritedTags: true
};
}
}
export class ZabbixQueryHostsWithDeviceTypeMetaRequest extends ZabbixQueryHostsGenericRequest<Host[]> {
public static PATH = "host.get.meta_with_device_type"
constructor(authToken?: string | null, cookie?: string | null) {
super(ZabbixQueryHostsWithDeviceTypeMetaRequest.PATH, authToken, cookie);
}
createZabbixParams(args?: ParsedArgs): ZabbixParams {
return {
...super.createZabbixParams(args),
tags: [
{
"tag": "deviceType",
"operator": 4
}
],
inheritedTags: true
};
}
}
export class ZabbixQueryHostsGenericRequestWithItems<T extends ZabbixResult> extends ZabbixQueryHostsGenericRequest<T> {
constructor(path: string, authToken?: string | null, cookie?: string) {
super(path, authToken, cookie);
}
createZabbixParams(args?: ParsedArgs): ZabbixParams {
return {
...super.createZabbixParams(args),
selectItems: [
"itemid",
"key_",
"lastvalue",
"lastclock",
"name",
"type",
"value_type",
"status",
],
output: [
"hostid",
"host",
"name",
"hostgroup",
"items",
"description",
"parentTemplates"
],
};
}
async executeRequestReturnError(zabbixAPI: ZabbixAPI, args?: ParsedArgs): Promise<ZabbixErrorResult | T> {
let result = await super.executeRequestReturnError(zabbixAPI, args);
if (result && !isZabbixErrorResult(result)) {
for (let device of <ZabbixHost[]>result) {
for (let item of device.items || []) {
if (!item.lastclock ) {
let values = await new ZabbixQueryHistoryRequest(this.authToken, this.cookie).executeRequestReturnError(
zabbixAPI, new ZabbixHistoryGetParams(item.itemid, ["clock", "value", "itemid"], 1, item.value_type))
if (isZabbixErrorResult(values)) {
return values;
}
if (values.length) {
let latestValue = values[0];
item.lastvalue = latestValue.value;
item.lastclock = latestValue.clock;
} else {
item.lastvalue = null;
item.lastclock = null;
}
}
}
}
}
return result;
}
}
export class ZabbixQueryHostsGenericRequestWithItemsAndInventory<T extends ZabbixResult> extends ZabbixQueryHostsGenericRequestWithItems<T> {
constructor(path: string, authToken?: string | null, cookie?: string) {
super(path, authToken, cookie);
}
createZabbixParams(args?: ParsedArgs): ZabbixParams {
return {
...super.createZabbixParams(args),
selectInventory: [
"location", "location_lat", "location_lon"
]
};
}
}
export class ZabbixQueryHostsRequestWithItemsAndInventory extends ZabbixQueryHostsGenericRequestWithItemsAndInventory<ZabbixHost[]> {
constructor(authToken?: string | null, cookie?: string) {
super("host.get.with_items", authToken, cookie);
}
}
export class ZabbixQueryHostWithInventoryRequest extends ZabbixRequest<any> {
constructor(authToken?: string | null, cookie?: string) {
super("host.get.with_inventory", authToken, cookie);
}
createZabbixParams(args?: ParsedArgs): ZabbixParams {
return {
...super.createZabbixParams(args),
selectInventory: [
"location", "location_lat", "location_lon"
],
output: [
"hostid",
"host",
"name",
"hostgroup",
"description",
"parentTemplates"
],
};
}
}
const isZabbixCreateHostInputParams = (value: ZabbixParams): value is ZabbixCreateHostInputParams => "host" in value && !!value.host;
export interface ZabbixCreateHostInputParams extends ZabbixParams {
host: string
name: string
description: string
location?: {
name: String,
location_lat?: String,
location_lon?: String,
}
templateids?: [number];
hostgroupids?: [number];
additionalParams?: [number];
}
class ZabbixCreateHostParams implements ZabbixParams {
constructor(inputParams: ZabbixCreateHostInputParams) {
this.host = inputParams.host;
this.name = inputParams.name;
this.description = inputParams.description;
if (inputParams.location) {
this.inventory = {
location: inputParams.location.name,
location_lat: inputParams.location.location_lat,
location_lon: inputParams.location.location_lon,
}
}
if (inputParams.templateids) {
this.templates = inputParams.templateids.map((templateid) => {
return {templateid: templateid}
});
}
if (inputParams.hostgroupids) {
this.groups = inputParams.hostgroupids.map((groupid) => {
return {groupid: groupid}
});
}
}
host: string
name: string
description: string
inventory?: {
location: String
location_lat?: String
location_lon?: String
}
templates?: any
groups?: any
}
export class ZabbixCreateHostRequest extends ZabbixRequest<{
hostids: number[]
}> {
constructor(authToken?: string | null, cookie?: string) {
super("host.create", authToken, cookie);
}
createZabbixParams(args?: ParsedArgs): ZabbixParams {
if (args && isZabbixCreateHostInputParams(args.zabbix_params)) {
return {...new ZabbixCreateHostParams(args.zabbix_params), ...args.zabbix_params.additionalParams};
}
return args?.zabbix_params || {};
}
}
export class ZabbixQueryHostRequest extends ZabbixQueryHostsGenericRequest<any> {
constructor(authToken?: string | null, cookie?: string | null) {
super("host.get", authToken, cookie);
}
}
export class ZabbixCreateOrFindHostRequest extends ZabbixCreateHostRequest {
constructor(protected groupid: number, protected templateid: number, authToken?: string | null, cookie?: string,) {
super(authToken, cookie);
}
createZabbixParams(args?: ParsedArgs): ZabbixParams {
return super.createZabbixParams(args);
}
async prepare(zabbixAPI: ZabbixAPI, args?: ParsedArgs) {
// Lookup host of appropriate type (by template) and groupName
// or create one if not found. If multiple hosts are found the first
// will be taken
let queryHostArgs = new ParsedArgs({
groupids: this.groupid,
templateids: this.templateid,
});
let hosts: {
hostid: number
}[] = await new ZabbixQueryHostRequest(this.authToken, this.cookie)
.executeRequestThrowError(zabbixAPI, queryHostArgs)
// logger.debug("Query hosts args=", JSON.stringify(queryHostArgs), "lead to result=", JSON.stringify(hosts));
if (hosts && hosts.length > 0) {
// If we found a host and return it as prep result the execution of the create host request will be skipped
this.prepResult = {
hostids: [hosts[0].hostid]
}
}
return this.prepResult;
}
}

View file

@ -0,0 +1,192 @@
import {ParsedArgs, ZabbixParams, ZabbixRequest, ZabbixResult, ZabbixValueType} from "./zabbix-request.js";
export class ZabbixQueryItemsMetaRequest extends ZabbixRequest<any> {
createZabbixParams(args?: ParsedArgs) {
return {
"templated": false,
output: [
"itemid",
"key_",
"hostid"
], ...args?.zabbix_params
};
}
}
export type QueryZabbixItemResponse = {
value_type: string;
itemid: string,
name: string,
status?: string,
key_?: string,
lastvalue: string | null
lastclock: string | null
tags?: {
tag: string,
value: string
}[]
hosts?: {
hostid: number,
host: string,
templateid?: number,
name: string
}[]
}
export class ZabbixQueryItemsRequest extends ZabbixRequest<QueryZabbixItemResponse[]> {
constructor(authToken?: string | null, cookie?: string) {
super("item.get", authToken, cookie);
}
createZabbixParams(args?: ParsedArgs) {
return {
"templated": false,
"selectHosts": [
"templateid",
"hostid",
"host",
"name",
"description",
],
"selectTags": [
"tag",
"value"
],
output: [
"itemid",
"name",
"key_",
"hostid",
"status",
"type",
"description",
], ...args?.zabbix_params
};
}
}
export class ZabbixQueryItemsByIdRequest extends ZabbixRequest<QueryZabbixItemResponse[]> {
constructor(authToken?: string | null, cookie?: string) {
super("item.get.itembyid", authToken, cookie);
}
createZabbixParams(args?: ParsedArgs): ZabbixParams {
let filter: { key_: string | null } | null = null
if (args?.zabbix_params?.hasOwnProperty("id")) {
// @ts-ignore
args.zabbix_params["filter"] = {
// @ts-ignore
...args?.zabbix_params.filter, "key_": args?.zabbix_params.id
}
// @ts-ignore
delete args.zabbix_params["id"]
}
return {
filter: filter,
"selectTags": ["tag", "value"],
"inheritedTags": true,
"output": [
"lastvalue",
"lastclock",
"value_type",
"hostid",
"itemid",
"name",
"status",
"key_"
], ...args?.zabbix_params,
}
};
}
export interface ZabbixStoreValueInItemParams extends ZabbixParams {
hostid?: number
itemid?: number
key: string
name: string
tags: {
tag: string,
value?: string
}[]
value: Object
}
const isStoreValueInItem = (value: ZabbixParams): value is ZabbixStoreValueInItemParams =>
"hostid" in value && !!value.hostid && "name" in value && "key" in value && "value" in value;
const isUpdateValueInItemParams = (value: ZabbixParams): value is ZabbixUpdateValueInItemParams =>
"itemid" in value && !!value.itemid && isStoreValueInItem(value);
export interface ZabbixUpdateValueInItemParams extends ZabbixStoreValueInItemParams {
itemid: number
}
export enum ZabbixItemType {
ZABBIX_TRAPPER = 2,
ZABBIX_SCRIPT = 21
}
export class ZabbixCreateOrUpdateStorageItemRequest extends ZabbixRequest<any> {
static MAX_ZABBIX_ITEM_STORAGE_PERIOD = "9125d"; // Maximum possible value is 25 years, which corresponds to 9125 days
createZabbixParams(args?: ParsedArgs): ZabbixParams {
if (args && isStoreValueInItem(args?.zabbix_params)) {
// Attention!! Zabbix status
// can not be used as expected:
// 1. Status 0 means enabled, all other values mean disabled
// 2. If the status of the item is disabled the value will not be
// evaluated - this means we can't use the item status to reflect
// the activation status of the controlProgram, as we also want
// to read the values of disabled controlPrograms..
let createOrUpdateItemParams = {
key_: args.zabbix_params.key,
name: args.zabbix_params.name,
tags: args.zabbix_params.tags,
"type": ZabbixItemType.ZABBIX_TRAPPER.valueOf(),
"history": ZabbixCreateOrUpdateStorageItemRequest.MAX_ZABBIX_ITEM_STORAGE_PERIOD,
"value_type": ZabbixValueType.TEXT.valueOf()
}
if (isUpdateValueInItemParams(args.zabbix_params)) {
return {
itemid: args.zabbix_params.itemid,
...createOrUpdateItemParams
};
}
return {
hostid: args.zabbix_params.hostid,
...createOrUpdateItemParams
}
}
return args?.zabbix_params || {};
}
}
export interface ZabbixDeleteItemResponse extends ZabbixResult {
itemids: {
itemid: string | string[]
}
}
export class ZabbixDeleteItemRequest extends ZabbixRequest<ZabbixDeleteItemResponse> {
constructor(authToken?: string | null, cookie?: string) {
super("item.delete", authToken, cookie);
}
}
export interface ZabbixCreateOrUpdateItemResponse extends ZabbixResult {
"itemids": string[]
}
export class ZabbixCreateOrUpdateItemRequest extends ZabbixRequest<ZabbixCreateOrUpdateItemResponse> {
constructor(path: string, authToken?: string | null, cookie?: string) {
super(path, authToken, cookie);
}
}

View file

@ -0,0 +1,16 @@
import {ParsedArgs, ZabbixParams, ZabbixRequest} from "./zabbix-request.js";
import {UserRoleModule} from "../generated/graphql.js";
export class ZabbixQueryModulesRequest extends ZabbixRequest<UserRoleModule[]> {
constructor(authToken?: string | null, cookie?: string | null) {
super("module.get", authToken, cookie);
}
createZabbixParams(args?: ParsedArgs): ZabbixParams {
return {
...super.createZabbixParams(args),
output: "extend"
}
}
}

View file

@ -0,0 +1,66 @@
import {ParsedArgs, ZabbixRequest} from "./zabbix-request.js";
class ZabbixQueryTemplateGroupPermissionsRequest extends ZabbixRequest<
{
groupid: string,
name: string
}[]> {
constructor(authToken?: string | null, cookie?: string) {
super("templategroup.get.permissions", authToken, cookie);
}
createZabbixParams(args?: ParsedArgs) {
return {
"output": [
"usrgrpid",
"name",
"gui_access",
"users_status"
], ...args?.zabbix_params,
"selectTemplateGroupRights": [
"id",
"permission"
]
};
}
}
interface ZabbixUserGroupResponse {
usrgrpid: string,
name: string,
gui_access: string,
users_status: string,
templategroup_rights:
{
id: string,
permission: string
}[]
}
class ZabbixQueryUserGroupPermissionsRequest extends ZabbixRequest<ZabbixUserGroupResponse[]> {
constructor(authToken?: string | null, cookie?: string) {
super("usergroup.get.permissions", authToken, cookie);
}
createZabbixParams(args?: ParsedArgs) {
return {
...super.createZabbixParams(args),
"params": {
"output": [
"groupid",
"name"
],
"searchWildcardsEnabled": true,
"search": {
"name": [
"Permissions/*"
]
}
},
"id": 1
};
}
}

View file

@ -0,0 +1,530 @@
import {ApiError, InputMaybe, QueryHasPermissionsArgs, UserPermission} from "../generated/graphql.js";
import {ZabbixAPI} from "./zabbix-api.js";
import {ApiErrorCode, Permission, PermissionNumber} from "../model/model_enum_values.js";
import {logger} from "../logging/logger.js";
import {GraphQLError} from "graphql";
class ZabbixRequestBody {
public jsonrpc = "2.0"
public method
public id = 1
public params?: ZabbixParams
constructor(method: string) {
this.method = method;
}
}
export interface ZabbixResult {
}
export type ZabbixErrorResult = {
error: ApiError
}
export const isZabbixErrorResult = (value: any): value is ZabbixErrorResult => value instanceof Object && "error" in value && !!value.error;
export interface ZabbixParams {
}
export interface ZabbixWithTagsParams extends ZabbixParams {
tags?: { tag: string; operator: number; value: any; }[]
}
export enum ZabbixValueType {
TEXT = 4,
}
export class ParsedArgs {
public name_pattern?: string
public distinct_by_name?: boolean;
public zabbix_params: ZabbixParams[] | ZabbixParams
constructor(params?: any) {
if (Array.isArray(params)) {
this.zabbix_params = params.map(arg => this.parseArgObject(arg))
} else if (params instanceof Object) {
this.zabbix_params = this.parseArgObject(params)
}
}
getParam(paramName: string): any {
if (this.zabbix_params instanceof Array) {
return undefined
}
// @ts-ignore
return paramName in this.zabbix_params ? this.zabbix_params[paramName] : undefined
}
parseArgObject(args?: Object) {
let result: ZabbixParams
if (args) {
if ("name_pattern" in args && typeof args["name_pattern"] == "string") {
if (args["name_pattern"]) {
this.name_pattern = args["name_pattern"]
}
delete args["name_pattern"]
}
if ("distinct_by_name" in args) {
this.distinct_by_name = !(!args["distinct_by_name"])
delete args["distinct_by_name"]
}
if ("groupidsbase" in args) {
if (!("groupids" in args) || !args.groupids) {
// @ts-ignore
args["groupids"] = args.groupidsbase
}
delete args.groupidsbase
}
let filterTagStatements: { tag: string; operator: number; value: any; }[] = []
let filterStatements = {}
for (let argsKey in args) {
// @ts-ignore
let argsValue = args[argsKey]
if (argsKey.startsWith("tag_") && argsValue !== null) {
let argsArray = Array.isArray(argsValue) ? argsValue : [argsValue]
argsArray.forEach((tagValue) => {
filterTagStatements.push({
"tag": argsKey.slice(4),
"operator": 1,
"value": tagValue,
})
})
// @ts-ignore
delete args[argsKey]
}
if (argsKey.startsWith("filter_") && argsValue) {
// @ts-ignore
filterStatements[argsKey.slice(7)] = argsValue
// @ts-ignore
delete args[argsKey]
}
}
if (Object.keys(filterStatements).length) {
args = {
...args,
filter: filterStatements
}
}
if (filterTagStatements?.length) {
let tagsFilter: ZabbixWithTagsParams = {
tags: filterTagStatements
}
result = {
...tagsFilter,
...args,
inheritedTags: true,
}
} else {
result = args
}
} else {
result = {}
}
if (this.name_pattern) {
if ("search" in result) {
(<any> result.search).name = this.name_pattern
} else {
(<any> result).search = {
name: this.name_pattern,
}
}
}
return result
}
}
export class ZabbixRequest<T extends ZabbixResult, A extends ParsedArgs = ParsedArgs> {
protected requestBodyTemplate: ZabbixRequestBody;
protected method: string
protected prepResult: T | ZabbixErrorResult | undefined = undefined
constructor(public path: string, public authToken?: string | null, public cookie?: string | null,
protected permissionsNeeded?: QueryHasPermissionsArgs) {
this.method = path.split(".", 2).join(".");
this.requestBodyTemplate = new ZabbixRequestBody(this.method);
}
createZabbixParams(args?: A): ZabbixParams {
return args?.zabbix_params || {}
}
getRequestBody(args?: A, zabbixParams?: ZabbixParams): ZabbixRequestBody {
let params: ZabbixParams
if (Array.isArray(args?.zabbix_params)) {
params = args?.zabbix_params.map(paramsObj => {
return {...this.requestBodyTemplate.params, ...paramsObj}
})
} else {
params = {...this.requestBodyTemplate.params, ...zabbixParams ?? this.createZabbixParams(args)}
}
return params ? {
...this.requestBodyTemplate,
params: params
} : this.requestBodyTemplate
};
headers() {
let headers: {
"Content-Type": string
Accept: string
'Access-Control-Allow-Headers': string
Cookie?: string,
Authorization?: string
} = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Access-Control-Allow-Headers': 'Content-Type'
};
if (this.cookie) {
headers.Cookie = this.cookie
}
if (this.authToken) {
headers.Authorization = `Bearer ${this.authToken}`
}
return headers
}
async assureUserPermissions(zabbixAPI: ZabbixAPI) {
if (this.permissionsNeeded &&
!await ZabbixPermissionsHelper.hasUserPermissions(zabbixAPI, this.permissionsNeeded, this.authToken, this.cookie)) {
return {
error: {
message: "User does not have the required permissions",
code: ApiErrorCode.PERMISSION_ERROR,
path: this.path,
args: this.permissionsNeeded
}
}
} else {
return undefined
}
}
async prepare(zabbixAPI: ZabbixAPI, args?: A): Promise<T | ZabbixErrorResult | undefined> {
// If prepare returns something else than undefined the execution will be skipped and the
// result returned
this.prepResult = await this.assureUserPermissions(zabbixAPI);
return this.prepResult;
}
async executeRequestReturnError(zabbixAPI: ZabbixAPI, args?: A): Promise<T | ZabbixErrorResult> {
let prepareResult = await this.prepare(zabbixAPI, args);
if (prepareResult) {
return prepareResult;
}
let requestBody = this.getRequestBody(args);
try {
const result_promise = zabbixAPI.post<T | ZabbixErrorResult>(this.path, {
body: {...requestBody}, headers: this.headers()
});
return result_promise.then(response => {
if (isZabbixErrorResult(response)) {
return response as ZabbixErrorResult;
}
return response as T;
})
} catch (e) {
const msg = `Unable to execute zabbix request body=${JSON.stringify(requestBody)}: ${JSON.stringify(e)}`
logger.error(msg)
return {
error: {
code: -1,
message: msg,
data: e
}
} as ZabbixErrorResult
}
}
async executeRequestThrowError(zabbixApi: ZabbixAPI, args?: A): Promise<T> {
let response = await this.executeRequestReturnError(zabbixApi, args);
if (isZabbixErrorResult(response)) {
throw new GraphQLError(`Called Zabbix path ${this.path} with error: ${response.error.message || "Zabbix error."} ${response.error.data}`, {
extensions: {
path: response.error.path || this.path,
args: response.error.args || args,
code: response.error.code,
data: response.error.data
},
});
} else {
return response as unknown as T;
}
}
}
export class ZabbixCreateOrUpdateParams extends ParsedArgs {
constructor(args: any, public dryRun = true) {
super(args);
}
}
export class ZabbixCreateOrUpdateRequest<
T extends ZabbixResult,
P extends ZabbixRequest<ZabbixResult>,
A extends ZabbixCreateOrUpdateParams = ZabbixCreateOrUpdateParams> extends ZabbixRequest<T, A> {
constructor(public entity: string,
public updateExistingIdFieldname: string,
private prepareType: new (authToken?: string | null, cookie?: string | null) => P, authToken?: string | null, cookie?: string | null) {
super(entity + ".create.orupdate", authToken, cookie);
}
public message: string = "";
async prepare(zabbixAPI: ZabbixAPI, args?: A): Promise<ZabbixErrorResult | T | undefined> {
let prepResult = await super.prepare(zabbixAPI, args);
let nameParam = args?.getParam("name");
if (prepResult || !nameParam) {
return prepResult;
}
let existingItems = await new this.prepareType(this.authToken, this.cookie)
.executeRequestReturnError(zabbixAPI, new ParsedArgs({
filter: {
name: nameParam
}
})) as Record<string, any>[] | ZabbixErrorResult;
if (isZabbixErrorResult(existingItems)) {
this.message = "Error getting existing " + this.entity + "(s)";
this.prepResult = existingItems;
return existingItems;
}
if (existingItems.length > 1) {
this.message = "Multiple existing " + this.entity + "(s) found for existing args";
this.prepResult = {
error: {
code: ApiErrorCode.ZABBIX_MULTIPLE_USERGROUPS_FOUND,
message: this.message,
data: args,
}
}
return this.prepResult;
}
if (existingItems.length == 1) {
this.message = "Updating existing user group";
if (args?.dryRun) {
this.prepResult = {
error: {
code: ApiErrorCode.OK,
message: "Not updating existing user group, dry run enabled",
data: args,
}
}
} else {
let updateParams: Record<string, any> = {
...args?.zabbix_params
}
updateParams[this.updateExistingIdFieldname] = existingItems[0][this.updateExistingIdFieldname];
this.prepResult = await new ZabbixRequest<any>(this.entity + ".update", this.authToken, this.cookie)
.executeRequestReturnError(zabbixAPI, new ParsedArgs(updateParams));
}
} else {
this.message = "Creating " + this.entity + " - name not found";
if (args?.dryRun) {
this.prepResult = {
error: {
code: ApiErrorCode.OK,
message: "Not creating " + this.entity + ", dry run enabled",
data: args,
}
}
}
}
return this.prepResult
}
}
class ZabbixQueryTemplateGroupPermissionsRequest extends ZabbixRequest<
{
groupid: string,
name: string
}[]> {
constructor(authToken?: string | null, cookie?: string | null) {
super("templategroup.get.permissions", authToken, cookie);
}
createZabbixParams(args?: ParsedArgs) {
return {
...super.createZabbixParams(args),
output: [
"groupid",
"name"
],
searchWildcardsEnabled: true,
search: {
name: [
ZabbixPermissionsHelper.ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX + "/*"
]
}
};
}
}
interface ZabbixUserGroupResponse {
usrgrpid: string,
name: string,
gui_access: string,
users_status: string,
templategroup_rights:
{
id: string,
permission: Permission
}[]
}
class ZabbixQueryUserGroupPermissionsRequest extends ZabbixRequest<ZabbixUserGroupResponse[]> {
constructor(authToken?: string | null, cookie?: string | null) {
super("usergroup.get.permissions", authToken, cookie);
}
createZabbixParams(args?: ParsedArgs) {
return {
...super.createZabbixParams(args),
"output": [
"usrgrpid",
"name",
"gui_access",
"users_status"
], ...args?.zabbix_params,
"selectTemplateGroupRights": [
"id",
"permission"
]
};
}
}
export class ZabbixPermissionsHelper {
private static permissionObjectNameCache: Map<string, string | null> = new Map()
public static ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX = process.env.ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX || "Permissions"
public static async getUserPermissions(zabbixAPI: ZabbixAPI, zabbixAuthToken?: string, cookie?: string,
objectNames?: InputMaybe<string[]> | undefined): Promise<UserPermission[]> {
return Array.from((await this.getUserPermissionNumbers(zabbixAPI, zabbixAuthToken, cookie, objectNames)).entries()).map(value => {
return {
objectName: value[0],
permission: this.mapPermissionToZabbixEnum(value[1])
}
});
}
public static async getUserPermissionNumbers(zabbixAPI: ZabbixAPI, zabbixAuthToken?: string | null, cookie?: string | null, objectNamesFilter?: InputMaybe<string[]> | undefined): Promise<Map<string, PermissionNumber>> {
const userGroupPermissions = await new ZabbixQueryUserGroupPermissionsRequest(zabbixAuthToken, cookie).executeRequestThrowError(zabbixAPI)
// Prepare the list of templateIds that are not loaded yet
const templateIdsToLoad = new Set(userGroupPermissions.flatMap(usergroup => usergroup.templategroup_rights.map(templateGroupRight => templateGroupRight.id)));
// Remove all templateIds that are already in the permissionObjectNameCache
templateIdsToLoad.forEach(id => {
if (this.permissionObjectNameCache.has(id)) {
templateIdsToLoad.delete(id);
}
})
if (templateIdsToLoad.size > 0) {
// Load all templateIds that are not in the permissionObjectNameCache
const missingPermissionGroupNames = await new ZabbixQueryTemplateGroupPermissionsRequest(zabbixAuthToken, cookie)
.executeRequestThrowError(zabbixAPI, new ParsedArgs({groupids: Array.from(templateIdsToLoad)}));
missingPermissionGroupNames.forEach(group => {
this.permissionObjectNameCache.set(group.groupid, group.name.replace(ZabbixPermissionsHelper.ZABBIX_PERMISSION_TEMPLATE_GROUP_NAME_PREFIX + "/", ""))
})
}
// Merge the permissions from the user groups. The merge function will first merge the permissions from the template groups
let permissions = new Map<string, PermissionNumber>();
userGroupPermissions.forEach(usergroup => {
permissions = this.mergeTemplateGroupPermissions(usergroup, permissions, objectNamesFilter);
})
return permissions;
}
private static mergeTemplateGroupPermissions(usergroup: ZabbixUserGroupResponse,
currentTemplateGroupPermissions: Map<string, PermissionNumber>,
objectNames: InputMaybe<string[]> | undefined): Map<string, PermissionNumber> {
// First we have to find the minimum permission for each template group as this is always superseeding the higher permission if it is set within a user group
let minPermissionsInUserGroup: Map<string, PermissionNumber> = new Map();
let objectNamesFilter = this.createMatcherFromWildcardArray(objectNames);
usergroup.templategroup_rights.forEach(templateGroupPermission => {
const objectName = this.permissionObjectNameCache.get(templateGroupPermission.id);
if (objectName && (objectNamesFilter == undefined || objectNamesFilter.test(objectName))) {
const permissionValue = Number(templateGroupPermission.permission) as PermissionNumber;
const minPermissionWithinThisGroup = minPermissionsInUserGroup.get(objectName);
if (minPermissionWithinThisGroup == undefined || minPermissionWithinThisGroup > permissionValue) {
minPermissionsInUserGroup.set(objectName, permissionValue);
}
}
})
// Then we have to find the highest permission compared to the permissions resulting from other user groups as on a
// user group level the higher permission is always superseeding the lower permission
minPermissionsInUserGroup.forEach((minPermissionInUserGroup, objectName) => {
const maxPermissionBetweenGroups = currentTemplateGroupPermissions.get(objectName);
if (maxPermissionBetweenGroups == undefined || maxPermissionBetweenGroups < minPermissionInUserGroup) {
currentTemplateGroupPermissions.set(objectName, minPermissionInUserGroup);
}
})
return currentTemplateGroupPermissions;
}
private static mapZabbixPermission(zabbixPermission: Permission): PermissionNumber {
switch (zabbixPermission) {
case Permission.Read:
return PermissionNumber.Read
case Permission.ReadWrite:
return PermissionNumber.ReadWrite
case Permission.Deny:
default:
return PermissionNumber.Deny
}
}
private static mapPermissionToZabbixEnum(permission: PermissionNumber): Permission {
switch (permission) {
case PermissionNumber.Read:
return Permission.Read
case PermissionNumber.ReadWrite:
return Permission.ReadWrite
case PermissionNumber.Deny:
default:
return Permission.Deny
}
}
private static createMatcherFromWildcardArray(array: InputMaybe<string[]> | undefined): RegExp | undefined {
if (!array) {
return undefined;
}
// Escape all values in the array and create regexp that allows the * wildcard which will be a .* in the regexp
return new RegExp(array.map(value => "^" + this.escapeRegExp(value).replace(/\\\*/gi, ".*") + "$").join("|"));
}
private static escapeRegExp(value: string) {
let result = value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
return result;
}
public static async hasUserPermissions(zabbixAPI: ZabbixAPI, args: QueryHasPermissionsArgs, zabbixAuthToken?: string | null, cookie?: string | null): Promise<boolean> {
let permissions = await this.getUserPermissionNumbers(zabbixAPI, zabbixAuthToken, cookie);
for (const permission of args.permissions) {
const existingPermission = permissions.get(permission.objectName);
if (permission.permission != Permission.Deny) {
if (existingPermission == undefined || existingPermission < this.mapZabbixPermission(permission.permission)) {
return false;
}
}
}
return true;
}
}

View file

@ -0,0 +1,89 @@
import {ZabbixAPI} from "./zabbix-api.js";
import {ApiErrorCode} from "../model/model_enum_values.js";
import {isZabbixErrorResult, ParsedArgs, ZabbixErrorResult, ZabbixParams, ZabbixRequest} from "./zabbix-request.js";
import {ZabbixQueryHostsMetaRequest} from "./zabbix-hosts.js";
export class ZabbixForceCacheReloadParams extends ParsedArgs {
constructor(public hostid: number) {
super();
}
}
export class ZabbixForceCacheReloadRequest extends ZabbixRequest<{
response: string
value: string
}, ZabbixForceCacheReloadParams> {
private static ZABBIX_RELOAD_CACHE_SCRIPT_NAME = "Reload configuration cache";
private static reloadCacheScriptId = 0;
private reloadCacheHostId = 0;
private static ZABBIX_RELOAD_CACHE_SCRIPT_PARAMS = {
"name": this.ZABBIX_RELOAD_CACHE_SCRIPT_NAME,
"command": "/usr/lib/zabbix/alertscripts/config_cache_reload.sh",
"host_access": "2",
"description": "Reload the configuration cache making config changes like new hosts/items visible to zabbix_server. Without this new values cannot be received.",
"type": "0",
"execute_on": "1",
"timeout": "30s",
"scope": "2"
};
constructor(authToken?: string | null, cookie?: string | null) {
super("script.execute", authToken, cookie);
}
async prepare(zabbixAPI: ZabbixAPI, args?: ZabbixForceCacheReloadParams) {
this.reloadCacheHostId = args?.hostid || 0;
if (!this.reloadCacheHostId) {
let someHost = await new ZabbixQueryHostsMetaRequest(this.authToken, this.cookie).executeRequestReturnError(zabbixAPI, new ParsedArgs({limit: 1}))
if (!isZabbixErrorResult(someHost) && someHost && someHost.length && someHost[0].hostid) {
this.reloadCacheHostId = Number(someHost[0].hostid)
} else {
this.prepResult = {
error: {
message: "Unable to find host for executing scripts",
code: ApiErrorCode.ZABBIX_HOST_NOT_FOUND,
path: this.path,
args: args,
}
} as ZabbixErrorResult
return this.prepResult
}
}
if (!args?.zabbix_params?.hasOwnProperty("scriptid") && !ZabbixForceCacheReloadRequest.reloadCacheScriptId) {
let scriptResult = await new ZabbixRequest<{
scriptid: string
}[]>("script.get", this.authToken, this.cookie).executeRequestThrowError(zabbixAPI, new ParsedArgs(
{filter_name: ZabbixForceCacheReloadRequest.ZABBIX_RELOAD_CACHE_SCRIPT_NAME}))
if (scriptResult && scriptResult.length && scriptResult[0].scriptid) {
ZabbixForceCacheReloadRequest.reloadCacheScriptId = Number(scriptResult[0].scriptid)
} else {
let createScriptResult = await new ZabbixRequest<{
scriptids: string[]
}>("script.create", this.authToken, this.cookie).executeRequestThrowError(zabbixAPI,
new ParsedArgs(ZabbixForceCacheReloadRequest.ZABBIX_RELOAD_CACHE_SCRIPT_PARAMS))
if (!isZabbixErrorResult(createScriptResult) && createScriptResult?.scriptids && createScriptResult.scriptids.length) {
ZabbixForceCacheReloadRequest.reloadCacheScriptId = Number(createScriptResult.scriptids[0]);
} else {
this.prepResult = {
error: {
message: "Unable to find or create script for reloading cache",
code: ApiErrorCode.ZABBIX_SCRIPT_NOT_FOUND,
data: createScriptResult,
path: this.path,
args: args,
}
} as ZabbixErrorResult
}
}
return this.prepResult
}
}
createZabbixParams(_args?: ZabbixForceCacheReloadParams): ZabbixParams {
return {
"scriptid": ZabbixForceCacheReloadRequest.reloadCacheScriptId,
"hostid": this.reloadCacheHostId,
}
}
}

View file

@ -0,0 +1,32 @@
import { ZabbixRequest } from "./zabbix-request.js";
export interface ZabbixQueryTemplateResponse {
templateid: string,
uuid: string,
name: string,
}
export class ZabbixQueryTemplatesRequest extends ZabbixRequest<ZabbixQueryTemplateResponse[]> {
constructor(authToken?: string | null, cookie?: string | null,) {
super("template.get", authToken, cookie);
}
}
export interface ZabbixQueryTemplateGroupResponse {
groupid: string,
name: string,
uuid: string
}
export class ZabbixQueryTemplateGroupRequest extends ZabbixRequest<ZabbixQueryTemplateGroupResponse[]> {
constructor(authToken?: string | null, cookie?: string | null) {
super("templategroup.get", authToken, cookie);
}
}

View file

@ -0,0 +1,330 @@
import {
isZabbixErrorResult,
ParsedArgs,
ZabbixCreateOrUpdateParams,
ZabbixCreateOrUpdateRequest,
ZabbixErrorResult,
ZabbixParams,
ZabbixRequest,
ZabbixResult
} from "./zabbix-request.js";
import {
ApiError,
ImportUserRightResult, Permission,
UserGroup,
UserGroupInput,
ZabbixGroupRight,
ZabbixGroupRightInput
} from "../generated/graphql.js";
import {ZabbixAPI} from "./zabbix-api.js";
import {ZabbixQueryTemplateGroupRequest, ZabbixQueryTemplateGroupResponse} from "./zabbix-templates.js";
import {ZabbixQueryHostgroupsRequest, ZabbixQueryHostgroupsResult} from "./zabbix-hostgroups.js";
import {ApiErrorCode} from "../model/model_enum_values.js";
abstract class ZabbixPrepareGetTemplatesAndHostgroupsRequest<T extends ZabbixResult, A extends ParsedArgs = ParsedArgs> extends ZabbixRequest<T, A> {
protected templategroups: ZabbixQueryTemplateGroupResponse[];
protected hostgroups: ZabbixQueryHostgroupsResult[];
constructor(path: string, authToken?: string | null, cookie?: string) {
super(path, authToken, cookie);
}
async prepare(zabbixAPI: ZabbixAPI, args?: A): Promise<ZabbixErrorResult | T | undefined> {
let prepResult = await super.prepare(zabbixAPI, args);
if (prepResult) {
return prepResult;
}
let templategroups = await new ZabbixQueryTemplateGroupRequest(this.authToken, this.cookie)
.executeRequestReturnError(zabbixAPI);
if (isZabbixErrorResult(templategroups)) {
this.prepResult = templategroups;
return templategroups;
}
this.templategroups = templategroups;
let hostgroups =
await new ZabbixQueryHostgroupsRequest(this.authToken, this.cookie)
.executeRequestReturnError(zabbixAPI);
if (isZabbixErrorResult(hostgroups)) {
this.prepResult = hostgroups;
return hostgroups;
}
this.hostgroups = hostgroups;
return undefined
}
}
export class ZabbixExportUserGroupArgs extends ParsedArgs {
public exclude_hostgroups_pattern?: RegExp | undefined = undefined;
constructor(name_pattern?: string | null, exclude_hostgroups_pattern_str?: string | null) {
super(name_pattern? {name_pattern: name_pattern} : undefined);
if (exclude_hostgroups_pattern_str) {
this.exclude_hostgroups_pattern = new RegExp(exclude_hostgroups_pattern_str);
}
}
}
export class ZabbixExportUserGroupsRequest extends ZabbixPrepareGetTemplatesAndHostgroupsRequest<
UserGroup[], ZabbixExportUserGroupArgs> {
constructor(authToken?: string | null, cookie?: string) {
super("usergroup.get.withuuids", authToken, cookie);
}
createZabbixParams(args?: ZabbixExportUserGroupArgs): ZabbixParams {
return {
...super.createZabbixParams(args),
output: "extend",
selectTemplateGroupRights: "extend",
selectHostGroupRights: "extend"
};
}
async executeRequestReturnError(zabbixAPI: ZabbixAPI, args?: ZabbixExportUserGroupArgs): Promise<ZabbixErrorResult | UserGroup[]> {
let result = await super.executeRequestReturnError(zabbixAPI, args);
if (!isZabbixErrorResult(result)) {
for (let userGroup of result) {
for (let template_permission of userGroup.templategroup_rights || []) {
for (let templategroup of this.templategroups) {
if (templategroup.groupid == template_permission.id.toString()) {
template_permission.uuid = templategroup.uuid;
template_permission.name = templategroup.name;
break;
}
}
}
let filtered_hostgroup_permission: ZabbixGroupRight [] = [];
for (let hostgroup_permission of userGroup.hostgroup_rights || []) {
for (let hostgroup of this.hostgroups) {
if (hostgroup.groupid == hostgroup_permission.id.toString()) {
hostgroup_permission.uuid = hostgroup.uuid;
hostgroup_permission.name = hostgroup.name;
break;
}
}
if (!args?.exclude_hostgroups_pattern || !hostgroup_permission.name || !args.exclude_hostgroups_pattern.test(hostgroup_permission.name)) {
filtered_hostgroup_permission.push(hostgroup_permission);
}
}
userGroup.hostgroup_rights = filtered_hostgroup_permission;
}
}
return result;
}
}
export class ZabbixQueryUserGroupsRequest extends ZabbixRequest<UserGroup[]> {
constructor(authToken?: string | null, cookie?: string | null) {
super("usergroup.get", authToken, cookie);
}
createZabbixParams(args?: ParsedArgs): ZabbixParams {
return {
...super.createZabbixParams(args),
output: "extend",
};
}
}
export class ZabbixImportUserGroupsParams extends ParsedArgs {
constructor(public usergroups: UserGroupInput[], public dryRun = true) {
super();
}
}
export class ZabbixImportUserGroupsRequest
extends ZabbixPrepareGetTemplatesAndHostgroupsRequest<ImportUserRightResult[],
ZabbixImportUserGroupsParams> {
constructor(zabbixAuthToken: any, cookie: any) {
super("usergroup.create.import", zabbixAuthToken, cookie);
}
async executeRequestReturnError(zabbixAPI: ZabbixAPI, args?: ZabbixImportUserGroupsParams): Promise<ZabbixErrorResult | ImportUserRightResult[]> {
let prepareResult = await this.prepare(zabbixAPI, args);
if (prepareResult) {
return prepareResult;
}
let results: ImportUserRightResult[] = [];
let hostGroupsToPropagete: number[] = []
let createGroupRequest = new ZabbixCreateOrUpdateRequest<
ZabbixCreateUserGroupResponse, ZabbixQueryUserGroupsRequest, ZabbixCreateOrUpdateParams>(
"usergroup", "usrgrpid", ZabbixQueryUserGroupsRequest, this.authToken, this.cookie);
for (let userGroup of args?.usergroups || []) {
let templategroup_rights = this.calc_templategroup_rights(userGroup);
let hostgroup_rights = this.calc_hostgroup_rights(userGroup);
let errors: ApiError[] = [];
let params = new ZabbixCreateOrUpdateParams({
name: userGroup.name,
gui_access: userGroup.gui_access,
users_status: userGroup.users_status,
hostgroup_rights: hostgroup_rights.hostgroup_rights,
templategroup_rights: templategroup_rights.templategroup_rights,
}, args?.dryRun)
let result = await createGroupRequest.executeRequestReturnError(zabbixAPI, params);
if (isZabbixErrorResult(result)) {
errors.push(result.error);
results.push(
{
name: userGroup.name,
errors: errors,
message: result.error.message || "Error creating user group",
}
)
} else {
hostGroupsToPropagete.push(
...hostgroup_rights.hostgroup_rights.map(
value => value.id));
results.push(
{
name: userGroup.name,
id: result.usrgrpids[0],
message: createGroupRequest.message,
errors: errors,
}
)
}
errors.push(...templategroup_rights.errors);
errors.push(...hostgroup_rights.errors);
}
// If user groups were imported: Propagate group permissions to group children
if (hostGroupsToPropagete.length > 0) {
// Propagate group permissions to group children, filter duplicate groupids first
await new ZabbixPropagateHostGroupsRequest(this.authToken, this.cookie)
.executeRequestThrowError(zabbixAPI,
new ZabbixPropagateHostGroupsParams(hostGroupsToPropagete))
}
return results;
}
calc_hostgroup_rights(usergroup: UserGroupInput): {
errors: ApiError[],
hostgroup_rights: ZabbixGroupRight[]
} {
let result: ZabbixGroupRight [] = [];
let errors: ApiError[] = [];
for (let hostgroup_right of usergroup.hostgroup_rights || []) {
let success = false;
let matchedName = "";
for (let hostgroup of this.hostgroups) {
if (hostgroup.uuid == hostgroup_right.uuid) {
result.push(
{
id: Number(hostgroup.groupid),
permission: hostgroup_right.permission,
}
)
success = true;
matchedName = hostgroup.name;
break;
}
}
if (success && hostgroup_right.name && hostgroup_right.name != matchedName) {
errors.push(
{
code: ApiErrorCode.OK,
message: `WARNING: Hostgroup found and permissions set, but target name=${matchedName} does not match`,
data: hostgroup_right,
}
)
}
if (!success) {
errors.push(
{
code: ApiErrorCode.ZABBIX_HOSTGROUP_NOT_FOUND,
message: `Hostgroup with UUID ${hostgroup_right.uuid} not found`,
data: hostgroup_right,
}
)
}
}
return {
hostgroup_rights: result,
errors: errors,
};
}
calc_templategroup_rights(usergroup: UserGroupInput): {
errors: ApiError[],
templategroup_rights: ZabbixGroupRightInput[]
} {
let result: ZabbixGroupRight [] = [];
let errors: ApiError[] = [];
for (let templategroup_right of usergroup.templategroup_rights || []) {
let success = false;
let matchedName = "";
for (let templategroup of this.templategroups) {
if (templategroup.uuid == templategroup_right.uuid) {
result.push(
{
id: Number(templategroup.groupid),
permission: templategroup_right.permission,
}
)
success = true;
matchedName = templategroup.name
break;
}
}
if (success && templategroup_right.name && templategroup_right.name != matchedName) {
errors.push(
{
code: ApiErrorCode.OK,
message: `WARNING: Templategroup found and permissions set, but target name=${matchedName} does not match`,
data: templategroup_right,
}
)
}
if (!success) {
errors.push(
{
code: ApiErrorCode.ZABBIX_TEMPLATEGROUP_NOT_FOUND,
message: `Templategroup with UUID ${templategroup_right.uuid} not found`,
data: templategroup_right,
}
)
}
}
return {
templategroup_rights: result,
errors: errors,
};
}
}
export type ZabbixCreateUserGroupResponse = {
usrgrpids: string[];
}
class ZabbixPropagateHostGroupsParams extends ParsedArgs {
constructor(public groups: number[]) {
super();
}
}
export class ZabbixPropagateHostGroupsRequest extends ZabbixRequest<ZabbixCreateUserGroupResponse,
ZabbixPropagateHostGroupsParams> {
constructor(authToken?: string | null, cookie?: string | null) {
super("hostgroup.propagate", authToken, cookie);
}
createZabbixParams(args?: ZabbixPropagateHostGroupsParams): ZabbixParams {
return {
groups: [...new Set(args?.groups || [])].map(value => {
return {
groupid: value
}
}) || [],
permissions: true
}
}
}

View file

@ -0,0 +1,175 @@
import {
isZabbixErrorResult,
ParsedArgs,
ZabbixCreateOrUpdateParams,
ZabbixCreateOrUpdateRequest,
ZabbixErrorResult,
ZabbixParams,
ZabbixRequest,
ZabbixResult
} from "./zabbix-request.js";
import {ApiError, ImportUserRightResult, UserRole, UserRoleInput, UserRoleModule} from "../generated/graphql.js";
import {ZabbixAPI} from "./zabbix-api.js";
import {ZabbixQueryModulesRequest} from "./zabbix-module.js";
import {ApiErrorCode} from "../model/model_enum_values.js";
export class ZabbixPrepareGetModulesRequest<T extends ZabbixResult, A extends ParsedArgs = ParsedArgs> extends ZabbixRequest<T, A> {
protected modules: UserRoleModule[];
async prepare(zabbixAPI: ZabbixAPI, args?: A): Promise<ZabbixErrorResult | T | undefined> {
let result = super.prepare(zabbixAPI, args);
if (!isZabbixErrorResult(result)) {
this.modules = await new ZabbixQueryModulesRequest(
this.authToken, this.cookie).executeRequestThrowError(zabbixAPI);
}
return result;
}
}
export class ZabbixQueryUserRolesRequest extends ZabbixPrepareGetModulesRequest<UserRole[]> {
constructor(authToken?: string | null, cookie?: string | null) {
super("role.get", authToken, cookie);
}
createZabbixParams(args?: ParsedArgs): ZabbixParams {
return {
...super.createZabbixParams(args),
output: "extend",
selectRules: "extend",
};
}
async executeRequestReturnError(zabbixApi: ZabbixAPI, args?: ParsedArgs): Promise<UserRole[] | ZabbixErrorResult> {
let prepResult = await this.prepare(zabbixApi, args);
if (prepResult) {
return prepResult
}
let result = await super.executeRequestReturnError(zabbixApi, args);
if (isZabbixErrorResult(result)) {
return result
}
for (let userRole of result) {
for (let userRoleModule of userRole.rules?.modules || []) {
for (let module of this.modules) {
if (module.moduleid == userRoleModule.moduleid) {
userRoleModule.id = module.id;
userRoleModule.relative_path = module.relative_path;
break;
}
}
}
}
return result;
}
}
export class ZabbixImportUserRolesParams extends ParsedArgs {
constructor(public userRoles: UserRoleInput[], public dryRun: boolean = false) {
super();
}
}
export class ZabbixImportUserRolesRequest extends ZabbixPrepareGetModulesRequest<ImportUserRightResult[],
ZabbixImportUserRolesParams> {
constructor(zabbixAuthToken: any, cookie: any) {
super("role.create.import", zabbixAuthToken, cookie);
}
async executeRequestReturnError(zabbixAPI: ZabbixAPI, args?: ZabbixImportUserRolesParams): Promise<ImportUserRightResult[] | ZabbixErrorResult> {
let prepResult = await this.prepare(zabbixAPI, args);
if (isZabbixErrorResult(prepResult)) {
return prepResult
}
let results: ImportUserRightResult[] = [];
for (let userRole of args?.userRoles || []) {
let errors: ApiError[] = [];
let rules: any = undefined;
if (userRole.rules) {
let modules: UserRoleModule[] = []
for (let userRoleModule of userRole.rules.modules || []) {
let found = false;
for (let module of this.modules) {
if (module.moduleid == userRoleModule.moduleid &&
(!userRoleModule.id || module.id == userRoleModule.id)) {
found = true;
modules.push(
{
moduleid: userRoleModule.moduleid,
status: userRoleModule.status,
}
);
break;
}
if (module.id == userRoleModule.id) {
if (userRoleModule.moduleid && userRoleModule.moduleid != module.moduleid) {
errors.push(
{
code: ApiErrorCode.OK,
message: `WARNING: Module found and permissions set, but target moduleid=${module.moduleid} does not match`,
data: userRoleModule,
}
)
modules.push(
{
moduleid: module.moduleid,
status: userRoleModule.status,
}
)
found = true;
break;
}
}
}
if (!found) {
errors.push(
{
code: ApiErrorCode.ZABBIX_MODULE_NOT_FOUND,
message: `Module with ID ${userRoleModule.id} not found`,
data: userRoleModule,
}
)
}
}
rules = {
ui: userRole.rules.ui,
"ui.default_access": userRole.rules.ui_default_access,
modules: modules,
"modules.default_access": userRole.rules.modules_default_access,
"api.access": userRole.rules.api_access,
"api.mode": userRole.rules.api_mode,
api: userRole.rules.api,
actions: userRole.rules.actions,
"actions.default_access": userRole.rules.actions_default_access,
}
}
let params = new ZabbixCreateOrUpdateParams({
name: userRole.name,
type: userRole.type,
rules: rules,
}, args?.dryRun)
let createUserRoleRequest = new ZabbixCreateOrUpdateRequest<
{ roleids: string[] }, ZabbixQueryUserRolesRequest, ZabbixCreateOrUpdateParams>(
"role", "roleid", ZabbixQueryUserRolesRequest, this.authToken, this.cookie);
let result = await createUserRoleRequest.executeRequestReturnError(zabbixAPI, params);
if (isZabbixErrorResult(result)) {
errors.push(result.error);
results.push(
{
name: userRole.name,
errors: errors,
message: result.error.message || "Error creating user role",
}
)
} else {
results.push({
name: userRole.name,
id: result.roleids[0],
errors: errors,
message: createUserRoleRequest.message,
});
}
}
return results;
}
}

View file

@ -0,0 +1,118 @@
import {
ApiError,
DeviceValueExportResponse,
QueryExportDeviceValueHistoryArgs,
StorageItemType
} from "../generated/graphql.js";
import {ApiErrorCode, ApiErrorMessage} from "../model/model_enum_values.js";
import {QueryZabbixItemResponse, ZabbixQueryItemsRequest} from "../datasources/zabbix-items.js";
import {isZabbixErrorResult, ParsedArgs, ZabbixErrorResult} from "../datasources/zabbix-request.js";
import {ZabbixHistoryGetParams, ZabbixQueryHistoryRequest} from "../datasources/zabbix-history.js";
import {zabbixAPI} from "../datasources/zabbix-api";
type FilterCombo = {
deviceKey: string,
attributeName: string
}
type ItemMapResponse = {
items?: Map<number, FilterCombo>,
error?: ApiError
}
export class HostValueExporter {
static async exportDeviceData(args: QueryExportDeviceValueHistoryArgs, zabbixAuthToken?: string, cookie?: string): Promise<DeviceValueExportResponse> {
let itemMapResponse: ItemMapResponse = await HostValueExporter.queryItemsForFilterArgs(args, zabbixAuthToken, cookie);
if (itemMapResponse.error || !itemMapResponse.items) {
return {
error: itemMapResponse.error
}
}
let itemMap = itemMapResponse.items;
let items = Array.from(itemMap.keys());
if (!items.length) {
return {
result: []
}
}
let history = await new ZabbixQueryHistoryRequest(zabbixAuthToken, cookie).executeRequestThrowError(
zabbixAPI, new ZabbixHistoryGetParams(
items, ["value", "itemid", "clock", "ns"],
args.limit,
args.type || StorageItemType.Float,
args.time_from, args.time_until,
["clock"],
args.sortOrder,
))
if (isZabbixErrorResult(history)) {
return {
error: {
data: history.error.data,
code: ApiErrorCode.ZABBIX_ITEM_NOT_FOUND,
message: ApiErrorMessage.ZABBIX_UNABLE_TO_RETRIEVE_HISTORY
}
}
}
return {
result: history.map(historyItem => {
let itemid = +historyItem.itemid!;
let filter = itemMap.get(itemid);
if (!filter) {
return undefined
}
let timestamp: Date = new Date((+historyItem.clock * 1000) + (historyItem.ns / 1000));
return {
attributeKey: filter?.attributeName,
value: historyItem.value,
deviceKey: filter?.deviceKey,
itemid: itemid,
timestamp: timestamp,
}
}).filter(result => !!result)
}
}
static async queryItemsForFilterArgs(args: QueryExportDeviceValueHistoryArgs, zabbixAuthToken?: string, cookie?: string): Promise<ItemMapResponse> {
let deviceKeys = args.deviceKey_filter
let attributeNames = args.attribute_filter
let items: QueryZabbixItemResponse[] | ZabbixErrorResult = await new ZabbixQueryItemsRequest(zabbixAuthToken, cookie)
.executeRequestReturnError(zabbixAPI, new ParsedArgs(
{
filter: {
host: deviceKeys,
key_: attributeNames
},
tags: [{"tag": "hasValue", "operator": 1, "value": "true"}]
}))
if (isZabbixErrorResult(items)) {
return {
error: {
data: items.error.data,
code: ApiErrorCode.ZABBIX_ITEM_NOT_FOUND,
message: ApiErrorMessage.ZABBIX_UNABLE_TO_RETRIEVE_ITEMS_ACCORDING_TO_FILTER
}
}
}
let result: Map<number, FilterCombo> = new Map();
items.forEach(item => {
let deviceKey = item.hosts?.length ? item.hosts[0].host : undefined
if (!item.itemid || !deviceKey || !item.key_) {
return
}
result.set(+item.itemid, {
deviceKey: deviceKey,
attributeName: item.key_
})
})
return {
items: result
}
}
}

View file

@ -0,0 +1,158 @@
import {
CreateHost,
CreateHostResponse,
CreateHostGroupResponse,
InputMaybe,CreateHostGroup
} from "../generated/graphql.js";
import {logger} from "../logging/logger.js";
import {ZabbixQueryTemplatesRequest} from "../datasources/zabbix-templates.js";
import {isZabbixErrorResult, ParsedArgs, ZabbixErrorResult} from "../datasources/zabbix-request.js";
import {CreateHostGroupResult, GroupHelper, ZabbixCreateHostGroupRequest} from "../datasources/zabbix-hostgroups.js";
import {ZABBIX_EDGE_DEVICE_BASE_GROUP, zabbixAPI} from "../datasources/zabbix-api";
export class HostImporter {
public static getHostGroupHierarchyNames(hostGroups: Array<CreateHostGroup>) {
let resultSet: Set<CreateHostGroup> = new Set<CreateHostGroup>(hostGroups)
for (let group of hostGroups || []) {
let levelNames = group.groupName.split("/", hostGroups?.length - 1)
let leafName = ""
for (let level of levelNames) {
leafName += (leafName ? "/" + level : level)
resultSet.add({groupName: leafName})
}
}
return resultSet
}
public static async importHostGroups(hostGroups: InputMaybe<Array<CreateHostGroup>> | undefined, zabbixAuthToken?: string, cookie?: string) {
if (!hostGroups) {
return null
}
let result: CreateHostGroupResponse[] = []
for (let group of HostImporter.getHostGroupHierarchyNames(hostGroups)) {
let createGroupResult: CreateHostGroupResult | ZabbixErrorResult | undefined = undefined;
let groups = await GroupHelper.findHostGroupIdsByName([group.groupName], zabbixAPI, zabbixAuthToken, cookie)
let groupid = 0
let message: string | undefined = undefined
if (groups?.length) {
groupid = groups[0]
message = `Group ${group.groupName} already exists with groupid=${groupid} - skipping`
logger.debug(message)
} else {
createGroupResult = await new ZabbixCreateHostGroupRequest(zabbixAuthToken, cookie)
.executeRequestReturnError(zabbixAPI,
new ParsedArgs({
name: GroupHelper.groupFullName(group.groupName),
uuid: group.uuid
}))
if (isZabbixErrorResult(createGroupResult)) {
result.push(
{
groupName: group.groupName,
message: `Unable to create groupName=${group.groupName}: ${JSON.stringify(createGroupResult)}`,
error: createGroupResult!.error
}
)
continue
} else {
if (createGroupResult?.groupids?.length) {
groupid = Number(createGroupResult.groupids[0])
}
}
}
if (groupid) {
result.push(
{
groupName: group.groupName,
groupid: groupid,
message: message
}
)
} else {
result.push(
{
groupName: group.groupName,
message: `Unable to create groupName=${group.groupName}: ${JSON.stringify(createGroupResult)}`,
error: {
message: "Unknown error - no groupid returned",
}
}
)
}
}
return result
}
static async importHosts(devices: InputMaybe<Array<CreateHost>> | undefined, zabbixAuthToken?: string, cookie?: string) {
if (!devices) {
return null
}
let result: CreateHostResponse[] = []
for (let device of devices) {
let groupids = device.groupids
if (!groupids) {
groupids = await GroupHelper.findHostGroupIdsByName([ZABBIX_EDGE_DEVICE_BASE_GROUP, ...device.groupNames], zabbixAPI, zabbixAuthToken, cookie)
if (!groupids?.length) {
result.push(
{
deviceKey: device.deviceKey,
message: `Unable to find groupNames=${device.groupNames}`
}
)
break
}
}
let deviceImportResult: {
hostids?: string[];
error?: any;
} = await zabbixAPI.requestByPath("host.create", new ParsedArgs(
{
host: device.deviceKey,
name: device.name,
location: device.location,
templateids: [
await HostImporter.getTemplateIdForDeviceType(
device.deviceType, zabbixAuthToken, cookie)],
hostgroupids: groupids
}
), zabbixAuthToken, cookie)
if (deviceImportResult?.hostids?.length) {
result.push({
deviceKey: device.deviceKey,
hostid: deviceImportResult.hostids[0],
})
} else {
result.push({
deviceKey: device.deviceKey,
message: `Unable to import deviceKey=${device.deviceKey}: ${deviceImportResult.error.message}`,
error: deviceImportResult.error
})
}
}
return result
}
private static async getTemplateIdForDeviceType(deviceType: String, zabbixAuthToken?: string, cookie?: string): Promise<number | undefined> {
let result: number | undefined
let templates = await new ZabbixQueryTemplatesRequest(zabbixAuthToken, cookie)
.executeRequestThrowError(zabbixAPI, new ParsedArgs(
{
tag_deviceType: deviceType
}
));
if (templates?.length) {
result = Number(templates[0].templateid)
} else {
logger.error(`Unable to get template for deviceType=${deviceType}: ${result}`)
}
return result
}
}

5
src/index.ts Normal file
View file

@ -0,0 +1,5 @@
import {startAPi} from "./api/start.js";
startAPi()

64
src/logging/logger.ts Normal file
View file

@ -0,0 +1,64 @@
export enum Loglevel {
ERROR="ERROR", WARN="WARN", INFO="INFO", TRACE="TRACE", DEBUG="DEBUG"
}
export class Logger {
public levels:Set<Loglevel> | undefined = undefined
public logMqtt = true
constructor() {
this.readEnvironmentLogLevel()
}
readEnvironmentLogLevel() {
const levels = process.env.LOG_LEVELS
if (levels) {
const enumLevels = levels.split(",").map(v=> Loglevel[v as keyof typeof Loglevel])
this.levels = new Set<Loglevel>(enumLevels)
}
}
public trace(...data: any[]) {
if (!this.levels || this.levels.has(Loglevel.TRACE)) {
console.log(...data)
if (this.logMqtt) {
// TODO Push to mqtt TEST_STATS_LOG_TOPIC topic
}
}
}
public warn(...data: any[]) {
if (!this.levels || this.levels.has(Loglevel.WARN)) {
console.warn(...data)
if (this.logMqtt) {
// TODO Push to mqtt TEST_STATS_LOG_TOPIC topic
}
}
}
public info(...data: any[]) {
if (!this.levels || this.levels.has(Loglevel.INFO)) {
console.log(...data)
if (this.logMqtt) {
// TODO Push to mqtt TEST_STATS_LOG_TOPIC topic
}
}
}
public error(...data: any[]) {
if (!this.levels || this.levels.has(Loglevel.ERROR)) {
console.error(...data)
if (this.logMqtt) {
// TODO Push to mqtt TEST_STATS_LOG_TOPIC topic
}
}
}
public debug(...data: any[]) {
if (!this.levels || this.levels.has(Loglevel.DEBUG)) {
console.debug(...data)
if (this.logMqtt) {
// TODO Push to mqtt TEST_STATS_LOG_TOPIC topic
}
}
}
}
export const logger = new Logger()

View file

@ -0,0 +1,70 @@
// Zabbix value enum mappings
export enum DeviceCommunicationType {
ZABBIX_AGENT = "0",
ZABBIX_TRAP = "2",
SIMPLE_CHECK = "3",
ZABBIX_INTERNAL_ITEM = "5",
ZABBIX_AGENT_ACTIVE = "7",
DATABASE_MONITOR = "11",
IPMI_AGENT = "12",
SIMULATOR_CALCULATED = "15",
JMX_AGENT = "16",
SNMP_TRAP = "17",
DEPENDANT_ITEM = "18",
HTTP_AGENT = "19",
SNMP_AGENT = "20",
SIMULATOR_JAVASCRIPT = "21",
}
export enum DeviceStatus {
DISABLED = "0",
ENABLED = "1"
}
export enum StorageItemType {
Float = 0,
Int = 3,
Text = 4,
}
export enum ApiErrorCode {
OK = 0,
ZABBIX_ERROR = 1000,
ZABBIX_NO_ITEM_PUSH_ITEM = 1001,
ZABBIX_HOST_NOT_FOUND = 1002,
ZABBIX_ITEM_NOT_FOUND = 1003,
ZABBIX_HISTORY_NOT_FOUND = 1004,
ZABBIX_TEMPLATE_NOT_FOUND = 1005,
ZABBIX_SCRIPT_NOT_FOUND = 1006,
ZABBIX_HISTORY_PUSH_FAILED = 1007,
ZABBIX_TEMPLATEGROUP_NOT_FOUND= 1008,
ZABBIX_HOSTGROUP_NOT_FOUND = 1009,
ZABBIX_MULTIPLE_USERGROUPS_FOUND = 1010,
ZABBIX_MODULE_NOT_FOUND= 1011,
VALIDATION_ERROR = 2001,
PERMISSION_ERROR = 2002,
}
export enum ApiErrorMessage {
OK = "",
ZABBIX_NO_TRAPPER_ITEMS_FOR_PUSHING_VALUES_FOUND = "Unable to push value to history, didn't find corresponding trapper item",
ZABBIX_ITEM_NOT_FOUND = "Find a zabbix item with corresponding id",
UPDATE_SKIPPED_NO_CHANGES = "Update skipped - nothing changed",
ZABBIX_REQUEST_EXCEPTION = "Unable to access zabbix api",
ZABBIX_UNABLE_TO_PUSH_VALUE = "Unable to push value to history",
ZABBIX_UNABLE_TO_RETRIEVE_ITEMS_ACCORDING_TO_FILTER = "Unable to retrieve items for specified filter",
ZABBIX_UNABLE_TO_RETRIEVE_HISTORY = "Unable to retrieve history"
}
export const enum Permission {
Read = "2",
ReadWrite = "3",
Deny = "0"
}
export const enum PermissionNumber {
Read = 2,
ReadWrite = 3,
Deny = 0
}