Improved error checking + syscollector performance

This commit is contained in:
Jesús Ángel 2019-06-10 10:29:17 +02:00
parent 3cf22232a5
commit 94d0a83e43
6 changed files with 105 additions and 245 deletions

View File

@ -780,13 +780,13 @@ export class AgentsController {
* @param {String} binding_text
* @param {String} date
*/
offsetTimestamp = (text, time) => {
offsetTimestamp(text, time) {
try {
return text + this.timeService.offset(time);
} catch (error) {
return `${text}${time} (UTC)`;
return time !== '-' ? `${text}${time} (UTC)` : time;
}
};
}
/**
* Navigate to the groups of an agent

View File

@ -1,100 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { EuiBasicTable, EuiHealth } from '@elastic/eui';
export class InventoryInterfacesTable extends Component {
constructor(props) {
super(props);
this.state = {
items: this.props.items,
pageIndex: 0,
pageSize: 10
};
}
UNSAFE_componentWillReceiveProps(nextProps) {
this.setState({
items: nextProps.items
});
}
onTableChange({ page = {} }) {
const { index: pageIndex, size: pageSize } = page;
this.setState({
pageIndex,
pageSize
});
}
filterItems(pageIndex, pageSize) {
const { items } = this.state;
const pages = [];
const len = items.length;
for (let i = 0, j = len; i < j; i += pageSize) {
pages.push(items.slice(i, i + pageSize));
}
return { pageOfItems: pages[pageIndex] || [], totalItemCount: len };
}
render() {
const { pageIndex, pageSize } = this.state;
const { pageOfItems, totalItemCount } = this.filterItems(
pageIndex,
pageSize
);
const columns = [
{
field: 'name',
name: 'Name'
},
{
field: 'mac',
name: 'MAC'
},
{
field: 'state',
name: 'State',
render: state => (
<EuiHealth color={state === 'up' ? 'success' : 'danger'}>
{state}
</EuiHealth>
)
},
{
field: 'mtu',
name: 'MTU'
},
{
field: 'type',
name: 'Type'
}
];
const pagination = {
pageIndex,
pageSize,
totalItemCount,
pageSizeOptions: [10, 20, 50],
hidePerPageOptions: this.state.items.length <= 10
};
return (
<div>
<EuiBasicTable
items={pageOfItems}
columns={columns}
pagination={pagination}
onChange={obj => this.onTableChange(obj)}
/>
</div>
);
}
}
InventoryInterfacesTable.propTypes = {
items: PropTypes.array
};

View File

@ -14,7 +14,6 @@ import { AgentsPreviewController } from './agents-preview';
import { AgentsController } from './agents';
import { WelcomeScreen } from './components/welcome';
import { Stats } from './components/stats';
import { InventoryInterfacesTable } from './components/inventory-interfaces-table';
const app = uiModules.get('app/wazuh', []);
@ -22,5 +21,4 @@ app
.controller('agentsController', AgentsController)
.controller('agentsPreviewController', AgentsPreviewController)
.value('WelcomeScreenAgent', WelcomeScreen)
.value('StatsAgent', Stats)
.value('InventoryInterfacesTable', InventoryInterfacesTable);
.value('StatsAgent', Stats);

View File

@ -18,6 +18,8 @@ import * as listeners from './lib/listeners';
import { searchData, filterData, queryData } from './lib/data';
import { initTable } from './lib/init';
import { sort } from './lib/sort';
import React, { Component } from 'react';
import { EuiHealth } from '@elastic/eui';
const app = uiModules.get('app/wazuh', []);
@ -29,7 +31,14 @@ app.directive('wzTableEui', function() {
keys: '=keys',
initialSortField: '=initialSortField'
},
controller($scope, apiReq, errorHandler, wzTableFilter) {
controller($scope, apiReq, errorHandler, wzTableFilter, timeService) {
const health = (state, config) => (
<EuiHealth color={state === config.success ? 'success' : 'danger'}>
{state}
</EuiHealth>
);
const defaultRender = value => value || '-';
const parseColumns = columnsArray => {
return columnsArray.map(item => ({
@ -37,7 +46,8 @@ app.directive('wzTableEui', function() {
field: item.value || item,
width: item.width || undefined,
sortable: typeof item.sortable !== 'undefined' ? item.sortable : true,
render: value => value || '-'
render: value =>
item.isHealth ? health(value, item.isHealth) : defaultRender(value)
}));
};
@ -63,12 +73,23 @@ app.directive('wzTableEui', function() {
}
};
const offsetTimestamp = (text, time) => {
try {
return text + timeService.offset(time);
} catch (error) {
return time !== '-' ? `${text}${time} (UTC)` : time;
}
};
$scope.basicTableProps = {
path: $scope.path,
initialSortField: $scope.initialSortField || false,
columns: parseColumns($scope.keys),
items: [],
getData: options => fetch(options),
sortByField: field => sort(field, $scope, instance, fetch, errorHandler)
sortByField: field =>
sort(field, $scope, instance, fetch, errorHandler),
offsetTimestamp: (text, time) => offsetTimestamp(text, time)
//noItemsMessage: 'Change this'
};

View File

@ -54,40 +54,31 @@
<div layout="row" class="layout-padding" ng-if="agent && agent.status === 'Active' && hasSize(syscollector)">
<md-card flex class="wz-md-card">
<md-card-content class="wz-text-center"
ng-if="!syscollector.netiface || !syscollector.netiface.items || !syscollector.netiface.items.length">
<i class="fa fa-fw fa-info-circle" aria-hidden="true"></i> <span class="wz-headline-title">No network
interfaces information available</span>
</md-card-content>
<md-card-content ng-if="syscollector.netiface && syscollector.netiface.items.length">
<md-card-content>
<span class="wz-headline-title">
<react-component name="EuiIcon" props="{type:'indexMapping'}" /> Network interfaces</span>
<span class="color-grey pull-right"
ng-bind="offsetTimestamp('Last scan: ', syscollector.netiface.items[0].scan.time)"></span>
<react-component name="EuiIcon" props="{type:'indexMapping'}" /> Network interfaces
</span>
<md-divider class="wz-margin-top-10"></md-divider>
<react-component name="InventoryInterfacesTable" props="{items: syscollector.netiface.items}" />
<!--<react-component name="InventoryInterfacesTable" props="{items: syscollector.netiface.items}" />-->
<wz-table-eui flex path="'/syscollector/' + agent.id + '/netiface'" initial-sort-field="'name'"
keys="['name','mac',{value:'state',isHealth: {success: 'up', danger: 'down'}},'mtu','type']">
</wz-table-eui>
</md-card-content>
</md-card>
<md-card flex class="wz-md-card">
<md-card-content class="wz-text-center" ng-if="syscollector.ports && !syscollector.ports.items.length">
<i class="fa fa-fw fa-info-circle" aria-hidden="true"></i> <span class="wz-headline-title">No network
ports information available</span>
</md-card-content>
<md-card-content ng-if="syscollector.ports && syscollector.ports.items.length">
<md-card-content>
<span class="wz-headline-title">
<react-component name="EuiIcon" props="{type:'inputOutput'}" /> Network ports</span>
<span class="color-grey pull-right"
ng-bind="offsetTimestamp('Last scan: ', syscollector.ports.items[0].scan.time)"></span>
<react-component name="EuiIcon" props="{type:'inputOutput'}" /> Network ports
</span>
<md-divider class="wz-margin-top-10"></md-divider>
<wz-table-eui flex ng-if="agent && agent.os && agent.os.platform === 'windows'"
path="'/syscollector/' + agent.id + '/ports'" initial-sort-field="'protocol'"
keys="['process','local.ip','local.port','state','protocol']">
path="'/syscollector/' + agent.id + '/ports'" initial-sort-field="'process'"
keys="['process',{value:'local.ip', sortable:false},{value:'local.port', sortable:false},'state','protocol']">
</wz-table-eui>
<wz-table-eui flex ng-if="agent && agent.os && agent.os.platform !== 'windows'"
path="'/syscollector/' + agent.id + '/ports'" initial-sort-field="'protocol'"
keys="['local.ip','local.port','state','protocol']">
keys="[{value:'local.ip', sortable:false},{value:'local.port', sortable:false}','state','protocol']">
</wz-table-eui>
</md-card-content>
</md-card>
@ -95,12 +86,7 @@
<div layout="row" class="layout-padding" ng-if="agent && agent.status === 'Active' && hasSize(syscollector)">
<md-card flex class="wz-md-card">
<md-card-content class="wz-text-center"
ng-if="!syscollector.netaddr || !syscollector.netaddr.items || !syscollector.netaddr.items.length">
<i class="fa fa-fw fa-info-circle" aria-hidden="true"></i> <span class="wz-headline-title">No network
addresses information available</span>
</md-card-content>
<md-card-content ng-if="syscollector.netaddr && syscollector.netaddr.items.length">
<md-card-content>
<span class="wz-headline-title">
<react-component name="EuiIcon" props="{type:'controlsHorizontal'}" /> Network settings</span>
<md-divider class="wz-margin-top-10"></md-divider>
@ -115,9 +101,8 @@
<md-card flex class="wz-md-card">
<md-card-content>
<span class="wz-headline-title">
<react-component name="EuiIcon" props="{type:'apps'}" /> Packages</span>
<span class="color-grey pull-right"
ng-bind="offsetTimestamp('Last scan: ', syscollector.packagesDate)"></span>
<react-component name="EuiIcon" props="{type:'apps'}" /> Packages
</span>
<md-divider class="wz-margin-top-10"></md-divider>
<div layout="row"
class="wz-margin-top-10 euiFlexGroup euiFlexGroup--alignItemsCenter euiFormControlLayout__childrenWrapper">
@ -164,8 +149,6 @@
<md-card-content>
<span class="wz-headline-title">
<react-component name="EuiIcon" props="{type:'console'}" /> Processes</span>
<span class="color-grey pull-right"
ng-bind="offsetTimestamp('Last scan: ', syscollector.processesDate)"></span>
<md-divider class="wz-margin-top-10"></md-divider>
<div layout="row"
class="wz-margin-top-10 euiFlexGroup euiFlexGroup--alignItemsCenter euiFormControlLayout__childrenWrapper">

View File

@ -57,13 +57,7 @@ export class WazuhApiCtrl {
);
}
log('wazuh-api:checkStoredAPI', `${req.payload} exists`, 'debug');
// Always check the daemons before requesting any endpoint
try {
await this.checkDaemons(api, null);
} catch (error) {
const isDown = (error || {}).code === 'ECONNREFUSED';
if (!isDown) return ErrorResponse('ERROR3099', 3099, 500, reply);
}
const credInfo = ApiHelper.buildOptionsObject(api);
let response = await needle(
@ -73,6 +67,10 @@ export class WazuhApiCtrl {
credInfo
);
if (this.checkResponseIsDown(response)) {
return ErrorResponse('ERROR3099', 3099, 500, reply);
}
if (parseInt(response.body.error) === 0 && response.body.data) {
// Checking the cluster status
response = await needle(
@ -82,6 +80,10 @@ export class WazuhApiCtrl {
credInfo
);
if (this.checkResponseIsDown(response)) {
return ErrorResponse('ERROR3099', 3099, 500, reply);
}
if (!response.body.error) {
try {
const managerInfo = await needle(
@ -90,6 +92,11 @@ export class WazuhApiCtrl {
{},
credInfo
);
if (this.checkResponseIsDown(managerInfo)) {
return ErrorResponse('ERROR3099', 3099, 500, reply);
}
const updatedManagerName = managerInfo.body.data.name;
api.cluster_info.manager = updatedManagerName;
await this.wzWrapper.updateWazuhIndexDocument(null, req.payload, {
@ -108,6 +115,10 @@ export class WazuhApiCtrl {
credInfo
);
if (this.checkResponseIsDown(response)) {
return ErrorResponse('ERROR3099', 3099, 500, reply);
}
if (!response.body.error) {
let managerName = api.cluster_info.manager;
delete api.cluster_info;
@ -195,6 +206,11 @@ export class WazuhApiCtrl {
{},
options
);
if (this.checkResponseIsDown(response)) {
return ErrorResponse('ERROR3099', 3099, 500, reply);
}
if (
((response || {}).body || {}).error === 0 &&
((response || {}).body || {}).data
@ -561,6 +577,24 @@ export class WazuhApiCtrl {
}
}
checkResponseIsDown(response) {
const responseBody = (response || {}).body || {};
const responseError = responseBody.error || false;
// Avoid "Error communicating with socket" like errors
const socketErrorCodes = [1013, 1014, 1017, 1018, 1019];
const isDown = socketErrorCodes.includes(responseError || 1);
isDown &&
log(
'wazuh-api:makeRequest',
'Wazuh API is online but Wazuh is not ready yet'
);
return isDown;
}
/**
* Check main Wazuh daemons status
* @param {*} api API entry stored in .wazuh
@ -691,21 +725,19 @@ export class WazuhApiCtrl {
return { error: 0, message: 'Success' };
}
// Always check the daemons before requesting any endpoint
try {
const check = await this.checkDaemons(api, path);
if (path === '/ping') {
if (path === '/ping') {
try {
const check = await this.checkDaemons(api, path);
return check;
}
} catch (error) {
const isDown = (error || {}).code === 'ECONNREFUSED';
if (!isDown) {
log(
'wazuh-api:makeRequest',
'Wazuh API is online but Wazuh is not ready yet'
);
return ErrorResponse('ERROR3099', 3099, 500, reply);
} catch (error) {
const isDown = (error || {}).code === 'ECONNREFUSED';
if (!isDown) {
log(
'wazuh-api:makeRequest',
'Wazuh API is online but Wazuh is not ready yet'
);
return ErrorResponse('ERROR3099', 3099, 500, reply);
}
}
}
@ -725,22 +757,16 @@ export class WazuhApiCtrl {
options
);
const responseIsDown = this.checkResponseIsDown(response);
if (responseIsDown) {
return ErrorResponse('ERROR3099', 3099, 500, reply);
}
const responseBody = (response || {}).body || {};
const responseData = responseBody.data || false;
const responseError = responseBody.error || false;
// Avoid "1014 - Error communicating with socket" like errors
const socketErrorCodes = [1013, 1014, 1017, 1018, 1019];
if (socketErrorCodes.includes(responseError || 1)) {
if (counter > 3) {
throw new Error(
`Tried to execute ${method} ${path} three times with no success, aborted.`
);
}
await this.sleep(500);
return this.makeRequest(method, path, data, id, reply, counter + 1);
}
if (!responseError && responseData) {
cleanKeys(response);
return response.body;
@ -1203,74 +1229,15 @@ export class WazuhApiCtrl {
`${config.url}:${config.port}/syscollector/${agent}/os`,
{},
headers
),
needle(
'get',
`${config.url}:${config.port}/syscollector/${agent}/ports`,
{ limit: 1 },
headers
),
needle(
'get',
`${config.url}:${config.port}/syscollector/${agent}/packages`,
{
limit: 1,
select: 'scan_time'
},
headers
),
needle(
'get',
`${config.url}:${config.port}/syscollector/${agent}/processes`,
{
limit: 1,
select: 'scan_time'
},
headers
)
]);
const result = data.map(item => (item.body || {}).data || false);
const [
hardwareResponse,
osResponse,
portsResponse,
packagesDateResponse,
processesDateResponse
osResponse
] = result;
let netifaceResponse = false;
try {
const resultNetiface = await needle(
'get',
`${config.url}:${config.port}/syscollector/${agent}/netiface`,
{},
headers
);
netifaceResponse = ((resultNetiface || {}).body || {}).data || false;
} catch (error) {} // eslint-disable-line
// This API call may fail so we put it out of Promise.all
let netaddrResponse = false;
try {
const resultNetaddrResponse = await needle(
'get',
`${config.url}:${config.port}/syscollector/${agent}/netaddr`,
{
limit: 1
},
headers
);
netaddrResponse =
((resultNetaddrResponse || {}).body || {}).data || false;
} catch (error) {} // eslint-disable-line
const packagesDate = packagesDateResponse
? { ...packagesDateResponse }
: false;
const processesDate = processesDateResponse
? { ...processesDateResponse }
: false;
// Fill syscollector object
const syscollector = {
@ -1282,16 +1249,7 @@ export class WazuhApiCtrl {
os:
typeof osResponse === 'object' && Object.keys(osResponse).length
? { ...osResponse }
: false,
netiface: netifaceResponse ? { ...netifaceResponse } : false,
ports: portsResponse ? { ...portsResponse } : false,
netaddr: netaddrResponse ? { ...netaddrResponse } : false,
packagesDate: ((packagesDate || {}).items || []).length
? packagesDate.items[0].scan_time
: '-',
processesDate: ((processesDate || {}).items || []).length
? processesDate.items[0].scan_time
: '-'
: false
};
return syscollector;