Ability to execute "Manual event actions" on Zabbix Problems panel (#2024)

This PR resolves the below issues:
#2022 #1465 

Specifically, when executing a script on the problems panel, we do check
if the script scope is event or host.
Based on the script scope, the Zabbix API call is constructed
differently.

---------

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
This commit is contained in:
Christos Diamantis
2025-07-09 16:56:14 +03:00
committed by GitHub
parent 04ef3774b0
commit 30c0b0e982
7 changed files with 34 additions and 11 deletions

View File

@@ -0,0 +1,5 @@
---
'grafana-zabbix': minor
---
Add possibility to run "Manual event actions"

View File

@@ -44,6 +44,7 @@ export interface ZBXScript {
confirmation?: string; confirmation?: string;
type?: string; type?: string;
execute_on?: string; execute_on?: string;
scope?: string;
} }
export interface APIExecuteScriptResponse { export interface APIExecuteScriptResponse {

View File

@@ -930,11 +930,14 @@ export class ZabbixAPIConnector {
return this.request('script.get', params).then(utils.mustArray); return this.request('script.get', params).then(utils.mustArray);
} }
executeScript(hostid: string, scriptid: string): Promise<APIExecuteScriptResponse> { executeScript(scriptid: string, hostid?: string, eventid?: string): Promise<APIExecuteScriptResponse> {
const params: any = { const params: { scriptid: string; hostid?: string; eventid?: string } = { scriptid };
hostid, if (hostid) {
scriptid, params.hostid = hostid;
}; }
if (eventid) {
params.eventid = eventid;
}
return this.request('script.execute', params); return this.request('script.execute', params);
} }

View File

@@ -190,10 +190,22 @@ export const ProblemsPanel = (props: ProblemsPanelProps): JSX.Element => {
return ds.zabbix.getScripts([hostid]); return ds.zabbix.getScripts([hostid]);
}; };
const onExecuteScript = async (problem: ProblemDTO, scriptid: string): Promise<APIExecuteScriptResponse> => { const onExecuteScript = async (
problem: ProblemDTO,
scriptid: string,
scope: string
): Promise<APIExecuteScriptResponse> => {
const hostid = problem.hosts?.length ? problem.hosts[0].hostid : null; const hostid = problem.hosts?.length ? problem.hosts[0].hostid : null;
const ds: any = await getDataSourceSrv().get(problem.datasource); const ds: any = await getDataSourceSrv().get(problem.datasource);
return ds.zabbix.executeScript(hostid, scriptid);
switch (scope) {
case '4': // Event action
return ds.zabbix.executeScript(scriptid, undefined, problem.eventid);
case '2': // Host action
return ds.zabbix.executeScript(scriptid, hostid, undefined);
default:
return ds.zabbix.executeScript(scriptid);
}
}; };
const onProblemAck = async (problem: ProblemDTO, data: AckProblemData) => { const onProblemAck = async (problem: ProblemDTO, data: AckProblemData) => {

View File

@@ -24,6 +24,7 @@ interface State {
export interface ExecScriptData { export interface ExecScriptData {
scriptid: string; scriptid: string;
scope: string;
} }
export class ExecScriptModalUnthemed extends PureComponent<Props, State> { export class ExecScriptModalUnthemed extends PureComponent<Props, State> {
@@ -83,6 +84,7 @@ export class ExecScriptModalUnthemed extends PureComponent<Props, State> {
const data: ExecScriptData = { const data: ExecScriptData = {
scriptid: selectedScript.value, scriptid: selectedScript.value,
scope: this.state.script.scope,
}; };
this.props this.props

View File

@@ -29,7 +29,7 @@ interface Props extends RTRow<ProblemDTO> {
getProblemEvents: (problem: ProblemDTO) => Promise<ZBXEvent[]>; getProblemEvents: (problem: ProblemDTO) => Promise<ZBXEvent[]>;
getProblemAlerts: (problem: ProblemDTO) => Promise<ZBXAlert[]>; getProblemAlerts: (problem: ProblemDTO) => Promise<ZBXAlert[]>;
getScripts: (problem: ProblemDTO) => Promise<ZBXScript[]>; getScripts: (problem: ProblemDTO) => Promise<ZBXScript[]>;
onExecuteScript(problem: ProblemDTO, scriptid: string): Promise<APIExecuteScriptResponse>; onExecuteScript(problem: ProblemDTO, scriptid: string, scope: string): Promise<APIExecuteScriptResponse>;
onProblemAck?: (problem: ProblemDTO, data: AckProblemData) => Promise<any> | any; onProblemAck?: (problem: ProblemDTO, data: AckProblemData) => Promise<any> | any;
onTagClick?: (tag: ZBXTag, datasource: DataSourceRef | string, ctrlKey?: boolean, shiftKey?: boolean) => void; onTagClick?: (tag: ZBXTag, datasource: DataSourceRef | string, ctrlKey?: boolean, shiftKey?: boolean) => void;
} }
@@ -90,9 +90,9 @@ export const ProblemDetails = ({
return getScripts(problem); return getScripts(problem);
}; };
const onExecuteScriptInternal = (data: ExecScriptData) => { const onExecuteScriptInternal = ({ scriptid, scope }: ExecScriptData) => {
const problem = original as ProblemDTO; const problem = original as ProblemDTO;
return onExecuteScript(problem, data.scriptid); return onExecuteScript(problem, scriptid, scope);
}; };
const problem = original as ProblemDTO; const problem = original as ProblemDTO;

View File

@@ -28,7 +28,7 @@ export interface ProblemListProps {
getProblemEvents: (problem: ProblemDTO) => Promise<ZBXEvent[]>; getProblemEvents: (problem: ProblemDTO) => Promise<ZBXEvent[]>;
getProblemAlerts: (problem: ProblemDTO) => Promise<ZBXAlert[]>; getProblemAlerts: (problem: ProblemDTO) => Promise<ZBXAlert[]>;
getScripts: (problem: ProblemDTO) => Promise<ZBXScript[]>; getScripts: (problem: ProblemDTO) => Promise<ZBXScript[]>;
onExecuteScript: (problem: ProblemDTO, scriptid: string) => Promise<APIExecuteScriptResponse>; onExecuteScript: (problem: ProblemDTO, scriptid: string, scope: string) => Promise<APIExecuteScriptResponse>;
onProblemAck?: (problem: ProblemDTO, data: AckProblemData) => void; onProblemAck?: (problem: ProblemDTO, data: AckProblemData) => void;
onTagClick?: (tag: ZBXTag, datasource: DataSourceRef, ctrlKey?: boolean, shiftKey?: boolean) => void; onTagClick?: (tag: ZBXTag, datasource: DataSourceRef, ctrlKey?: boolean, shiftKey?: boolean) => void;
onPageSizeChange?: (pageSize: number, pageIndex: number) => void; onPageSizeChange?: (pageSize: number, pageIndex: number) => void;