Skip to content

Commit

Permalink
Merge branch 'feat-418', closes #418
Browse files Browse the repository at this point in the history
  • Loading branch information
alexanderzobnin committed Nov 15, 2018
2 parents c58cb42 + 61177cd commit c524df1
Show file tree
Hide file tree
Showing 11 changed files with 274 additions and 68 deletions.
15 changes: 6 additions & 9 deletions src/datasource-zabbix/specs/datasource.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import _ from 'lodash';
import mocks from '../../test-setup/mocks';
import { Datasource } from "../module";
import { zabbixTemplateFormat } from "../datasource";

Expand All @@ -17,15 +18,11 @@ describe('ZabbixDatasource', () => {
dbConnectionEnable: false
}
};
ctx.templateSrv = {};
ctx.backendSrv = {
datasourceRequest: jest.fn()
};
ctx.datasourceSrv = {};
ctx.zabbixAlertingSrv = {
setPanelAlertState: jest.fn(),
removeZabbixThreshold: jest.fn(),
};

ctx.templateSrv = mocks.templateSrvMock;
ctx.backendSrv = mocks.backendSrvMock;
ctx.datasourceSrv = mocks.datasourceSrvMock;
ctx.zabbixAlertingSrv = mocks.zabbixAlertingSrvMock;

ctx.ds = new Datasource(ctx.instanceSettings, ctx.templateSrv, ctx.backendSrv, ctx.datasourceSrv, ctx.zabbixAlertingSrv);
});
Expand Down
30 changes: 13 additions & 17 deletions src/datasource-zabbix/specs/dbConnector.test.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import mocks from '../../test-setup/mocks';
import DBConnector from '../zabbix/connectors/dbConnector';

describe('DBConnector', () => {
let ctx = {};
const backendSrvMock = {};
const datasourceSrvMock = {
loadDatasource: jest.fn().mockResolvedValue(
{ id: 42, name: 'foo', meta: {} }
),
getAll: jest.fn().mockReturnValue([
{ id: 42, name: 'foo' }
])
};
const backendSrv = mocks.backendSrvMock;
const datasourceSrv = mocks.datasourceSrvMock;
datasourceSrv.loadDatasource.mockResolvedValue({ id: 42, name: 'foo', meta: {} });
datasourceSrv.getAll.mockReturnValue([{ id: 42, name: 'foo' }]);

describe('When init DB connector', () => {
beforeEach(() => {
Expand All @@ -24,28 +20,28 @@ describe('DBConnector', () => {
ctx.options = {
datasourceName: 'bar'
};
const dbConnector = new DBConnector(ctx.options, backendSrvMock, datasourceSrvMock);
const dbConnector = new DBConnector(ctx.options, backendSrv, datasourceSrv);
dbConnector.loadDBDataSource();
expect(datasourceSrvMock.getAll).not.toHaveBeenCalled();
expect(datasourceSrvMock.loadDatasource).toHaveBeenCalledWith('bar');
expect(datasourceSrv.getAll).not.toHaveBeenCalled();
expect(datasourceSrv.loadDatasource).toHaveBeenCalledWith('bar');
});

it('should load datasource by id if name not present', () => {
const dbConnector = new DBConnector(ctx.options, backendSrvMock, datasourceSrvMock);
const dbConnector = new DBConnector(ctx.options, backendSrv, datasourceSrv);
dbConnector.loadDBDataSource();
expect(datasourceSrvMock.getAll).toHaveBeenCalled();
expect(datasourceSrvMock.loadDatasource).toHaveBeenCalledWith('foo');
expect(datasourceSrv.getAll).toHaveBeenCalled();
expect(datasourceSrv.loadDatasource).toHaveBeenCalledWith('foo');
});

it('should throw error if no name and id specified', () => {
ctx.options = {};
const dbConnector = new DBConnector(ctx.options, backendSrvMock, datasourceSrvMock);
const dbConnector = new DBConnector(ctx.options, backendSrv, datasourceSrv);
return expect(dbConnector.loadDBDataSource()).rejects.toBe('SQL Data Source name should be specified');
});

it('should throw error if datasource with given id is not found', () => {
ctx.options.datasourceId = 45;
const dbConnector = new DBConnector(ctx.options, backendSrvMock, datasourceSrvMock);
const dbConnector = new DBConnector(ctx.options, backendSrv, datasourceSrv);
return expect(dbConnector.loadDBDataSource()).rejects.toBe('SQL Data Source with ID 45 not found');
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ export class ZabbixAPIConnector {
value: 1
},
selectGroups: ['name'],
selectHosts: ['name', 'host', 'maintenance_status'],
selectHosts: ['name', 'host', 'maintenance_status', 'proxy_hostid'],
selectItems: ['name', 'key_', 'lastvalue'],
selectLastEvent: 'extend',
selectTags: 'extend'
Expand Down Expand Up @@ -463,6 +463,14 @@ export class ZabbixAPIConnector {
return triggers;
});
}

getProxies() {
var params = {
output: ['proxyid', 'host'],
};

return this.request('proxy.get', params);
}
}

function filterTriggersByAcknowledge(triggers, acknowledged) {
Expand Down
44 changes: 35 additions & 9 deletions src/datasource-zabbix/zabbix/zabbix.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ import { ZabbixNotImplemented } from './connectors/dbConnector';

const REQUESTS_TO_PROXYFY = [
'getHistory', 'getTrend', 'getGroups', 'getHosts', 'getApps', 'getItems', 'getMacros', 'getItemsByIDs',
'getEvents', 'getAlerts', 'getHostAlerts', 'getAcknowledges', 'getITService', 'getSLA', 'getVersion'
'getEvents', 'getAlerts', 'getHostAlerts', 'getAcknowledges', 'getITService', 'getSLA', 'getVersion', 'getProxies'
];

const REQUESTS_TO_CACHE = [
'getGroups', 'getHosts', 'getApps', 'getItems', 'getMacros', 'getItemsByIDs', 'getITService'
'getGroups', 'getHosts', 'getApps', 'getItems', 'getMacros', 'getItemsByIDs', 'getITService', 'getProxies'
];

const REQUESTS_TO_BIND = [
'getHistory', 'getTrend', 'getMacros', 'getItemsByIDs', 'getEvents', 'getAlerts', 'getHostAlerts',
'getAcknowledges', 'getITService', 'getVersion', 'login', 'acknowledgeEvent'
'getAcknowledges', 'getITService', 'getVersion', 'login', 'acknowledgeEvent', 'getProxies'
];

export class Zabbix {
Expand Down Expand Up @@ -243,7 +243,7 @@ export class Zabbix {
/**
* Build query - convert target filters to array of Zabbix items
*/
getTriggers(groupFilter, hostFilter, appFilter, options) {
getTriggers(groupFilter, hostFilter, appFilter, options, proxyFilter) {
let promises = [
this.getGroups(groupFilter),
this.getHosts(groupFilter, hostFilter),
Expand All @@ -252,9 +252,7 @@ export class Zabbix {

return Promise.all(promises)
.then(results => {
let filteredGroups = results[0];
let filteredHosts = results[1];
let filteredApps = results[2];
let [filteredGroups, filteredHosts, filteredApps] = results;
let query = {};

if (appFilter) {
Expand All @@ -268,8 +266,36 @@ export class Zabbix {
}

return query;
}).then(query => {
return this.zabbixAPI.getTriggers(query.groupids, query.hostids, query.applicationids, options);
})
.then(query => this.zabbixAPI.getTriggers(query.groupids, query.hostids, query.applicationids, options))
.then(triggers => this.filterTriggersByProxy(triggers, proxyFilter));
}

filterTriggersByProxy(triggers, proxyFilter) {
return this.getFilteredProxies(proxyFilter)
.then(proxies => {
if (proxyFilter && proxyFilter !== '/.*/' && triggers) {
const proxy_ids = proxies.map(proxy => proxy.proxyid);
triggers = triggers.filter(trigger => {
let filtered = false;
for(let i = 0; i < trigger.hosts.length; i++) {
const host = trigger.hosts[i];
if (proxy_ids.includes(host.proxy_hostid)) {
filtered = true;
}
}
return filtered;
});
}
return triggers;
});
}

getFilteredProxies(proxyFilter) {
return this.zabbixAPI.getProxies()
.then(proxies => {
proxies.forEach(proxy => proxy.name = proxy.host);
return findByFilter(proxies, proxyFilter);
});
}

Expand Down
98 changes: 98 additions & 0 deletions src/datasource-zabbix/zabbix/zabbix.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import mocks from '../../test-setup/mocks';
import { Zabbix } from './zabbix';

describe('Zabbix', () => {
let ctx = {};
let zabbix;
let options = {
url: 'http://localhost',
username: 'zabbix',
password: 'zabbix',
zabbixVersion: 4,
};

beforeEach(() => {
ctx.options = options;
ctx.backendSrv = mocks.backendSrvMock;
ctx.datasourceSrv = mocks.datasourceSrvMock;
zabbix = new Zabbix(ctx.options, ctx.backendSrvMock, ctx.datasourceSrvMock);
});

describe('When querying proxies', () => {
beforeEach(() => {
zabbix.zabbixAPI.getProxies = jest.fn().mockResolvedValue([
{ host: 'proxy-foo', proxyid: '10101' },
{ host: 'proxy-bar', proxyid: '10102' },
]);
});

it("should return all proxies if filter set to /.*/", done => {
zabbix.getFilteredProxies('/.*/').then(proxies => {
expect(proxies).toMatchObject([{ host: 'proxy-foo' }, { host: 'proxy-bar' }]);
done();
});
});

it("should return matched proxies if regex filter used", done => {
zabbix.getFilteredProxies('/.*-foo/').then(proxies => {
expect(proxies).toMatchObject([{ host: 'proxy-foo' }]);
done();
});
});

it("should return matched proxies if simple filter used", done => {
zabbix.getFilteredProxies('proxy-bar').then(proxies => {
expect(proxies).toMatchObject([{ host: 'proxy-bar' }]);
done();
});
});

it("should return empty list for empty filter", done => {
zabbix.getFilteredProxies('').then(proxies => {
expect(proxies).toEqual([]);
done();
});
});
});

describe('When filtering triggers by proxy', () => {
beforeEach(() => {
zabbix.zabbixAPI.getProxies = jest.fn().mockResolvedValue([
{ host: 'proxy-foo', proxyid: '10101' },
{ host: 'proxy-bar', proxyid: '10102' },
]);
ctx.triggers = [
{ triggerid: '1', hosts: [{ name: 'backend01', proxy_hostid: '0' }] },
{ triggerid: '2', hosts: [{ name: 'backend02', proxy_hostid: '0' }] },
{ triggerid: '3', hosts: [{ name: 'frontend01', proxy_hostid: '10101' }] },
{ triggerid: '4', hosts: [{ name: 'frontend02', proxy_hostid: '10101' }] },
{ triggerid: '5', hosts: [{ name: 'db01', proxy_hostid: '10102' }] },
{ triggerid: '6', hosts: [{ name: 'db02', proxy_hostid: '10102' }] },
];
});

it("should return all triggers for empty filter", done => {
zabbix.filterTriggersByProxy(ctx.triggers, '').then(triggers => {
const triggerids = triggers.map(t => t.triggerid);
expect(triggerids).toEqual(['1', '2', '3', '4', '5', '6']);
done();
});
});

it("should return triggers belonging proxy matched regex filter", done => {
zabbix.filterTriggersByProxy(ctx.triggers, '/.*-foo/').then(triggers => {
const triggerids = triggers.map(t => t.triggerid);
expect(triggerids).toEqual(['3', '4']);
done();
});
});

it("should return triggers belonging proxy matched name filter", done => {
zabbix.filterTriggersByProxy(ctx.triggers, 'proxy-bar').then(triggers => {
const triggerids = triggers.map(t => t.triggerid);
expect(triggerids).toEqual(['5', '6']);
done();
});
});
});
});
4 changes: 2 additions & 2 deletions src/panel-triggers/migrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import _ from 'lodash';
import {DEFAULT_TARGET} from './triggers_panel_ctrl';

// Actual schema version
export const CURRENT_SCHEMA_VERSION = 4;
export const CURRENT_SCHEMA_VERSION = 5;

export function migratePanelSchema(panel) {
if (isEmptyPanel(panel)) {
Expand Down Expand Up @@ -31,7 +31,7 @@ export function migratePanelSchema(panel) {
delete panel.hideHostsInMaintenance;
}

if (schemaVersion < 4) {
if (schemaVersion < 5) {
if (panel.targets && !_.isEmpty(panel.targets)) {
_.each(panel.targets, (target) => {
_.defaultsDeep(target, DEFAULT_TARGET);
Expand Down
6 changes: 6 additions & 0 deletions src/panel-triggers/partials/options_tab.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ <h5 class="section-heading">Show fields</h5>
checked="ctrl.panel.hostGroups"
on-change="ctrl.render()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label-class="width-9"
label="Host proxy"
checked="ctrl.panel.hostProxy"
on-change="ctrl.refresh()">
</gf-form-switch>
<gf-form-switch class="gf-form"
label-class="width-9"
label="Tags"
Expand Down
28 changes: 21 additions & 7 deletions src/panel-triggers/partials/triggers_tab.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@
<div class="section gf-form-group">
<h5 class="section-heading">{{ ds }}</h5>
<div class="gf-form-inline">
<div class="gf-form max-width-20">
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">Group</label>
<input type="text"
ng-model="ctrl.panel.targets[ds].group.filter"
bs-typeahead="editor.getGroupNames[ds]"
ng-blur="editor.parseTarget()"
data-min-length=0
data-items=100
class="gf-form-input"
class="gf-form-input width-14"
ng-class="{
'zbx-variable': editor.isVariable(ctrl.panel.targets[ds].group.filter),
'zbx-regex': editor.isRegex(ctrl.panel.targets[ds].group.filter)
Expand All @@ -41,24 +41,38 @@ <h5 class="section-heading">{{ ds }}</h5>
ng-blur="editor.parseTarget()"
data-min-length=0
data-items=100
class="gf-form-input"
class="gf-form-input width-14"
ng-class="{
'zbx-variable': editor.isVariable(ctrl.panel.targets[ds].host.filter),
'zbx-regex': editor.isRegex(ctrl.panel.targets[ds].host.filter)
}">
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">Proxy</label>
<input type="text"
ng-model="ctrl.panel.targets[ds].proxy.filter"
bs-typeahead="editor.getProxyNames[ds]"
ng-blur="editor.parseTarget()"
data-min-length=0
data-items=100
class="gf-form-input width-14"
ng-class="{
'zbx-variable': editor.isVariable(ctrl.panel.targets[ds].proxy.filter),
'zbx-regex': editor.isRegex(ctrl.panel.targets[ds].proxy.filter)
}">
</div>
</div>

<div class="gf-form-inline">
<div class="gf-form max-width-20">
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">Application</label>
<input type="text"
ng-model="ctrl.panel.targets[ds].application.filter"
bs-typeahead="editor.getApplicationNames[ds]"
ng-blur="editor.parseTarget()"
data-min-length=0
data-items=100
class="gf-form-input"
class="gf-form-input width-14"
ng-class="{
'zbx-variable': editor.isVariable(ctrl.panel.targets[ds].application.filter),
'zbx-regex': editor.isRegex(ctrl.panel.targets[ds].application.filter)
Expand All @@ -70,7 +84,7 @@ <h5 class="section-heading">{{ ds }}</h5>
ng-model="ctrl.panel.targets[ds].trigger.filter"
ng-blur="editor.parseTarget()"
placeholder="trigger name"
class="gf-form-input"
class="gf-form-input width-14"
ng-style="ctrl.panel.targets[ds].trigger.style"
ng-class="{
'zbx-variable': editor.isVariable(ctrl.panel.targets[ds].trigger.filter),
Expand All @@ -80,7 +94,7 @@ <h5 class="section-heading">{{ ds }}</h5>
</div>
<div class="gf-form">
<label class="gf-form-label query-keyword width-7">Tags</label>
<input type="text" class="gf-form-input"
<input type="text" class="gf-form-input width-14"
ng-model="ctrl.panel.targets[ds].tags.filter"
ng-blur="editor.parseTarget()"
placeholder="tag1:value1, tag2:value2">
Expand Down
Loading

0 comments on commit c524df1

Please sign in to comment.