mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
fix monitoring elasticsearch nodes listing sort and filtering (#20321)
closes https://github.com/elastic/kibana/issues/20132
This commit is contained in:
parent
e1778497b9
commit
f7070890d4
4 changed files with 328 additions and 40 deletions
|
@ -33,14 +33,14 @@ const metricVal = (metric, format, isPercent) => {
|
|||
return formatMetric(metric, format);
|
||||
};
|
||||
|
||||
function MetricCell({ isOnline, metric = {}, isPercent }) {
|
||||
function MetricCell({ isOnline, metric = {}, isPercent, ...props }) {
|
||||
if (isOnline) {
|
||||
const { lastVal, maxVal, minVal, slope } = get(metric, 'summary', {});
|
||||
const format = get(metric, 'metric.format');
|
||||
|
||||
return (
|
||||
<KuiTableRowCell>
|
||||
<div className="monitoringTableCell__MetricCell__metric">
|
||||
<div className="monitoringTableCell__MetricCell__metric" data-test-subj={props['data-test-subj']}>
|
||||
{ metricVal(lastVal, format, isPercent) }
|
||||
</div>
|
||||
<span className={`monitoringTableCell__MetricCell__slopeArrow fa fa-long-arrow-${getSlopeArrow(slope)}`} />
|
||||
|
|
|
@ -15,23 +15,23 @@ import { MetricCell, OfflineCell } from './cells';
|
|||
import { EuiLink, EuiToolTip } from '@elastic/eui';
|
||||
import { KuiTableRowCell, KuiTableRow } from '@kbn/ui-framework/components';
|
||||
|
||||
const filterFields = ['node.name', 'status', 'type', 'transport_address'];
|
||||
const filterFields = ['name'];
|
||||
const getColumns = showCgroupMetricsElasticsearch => {
|
||||
const cols = [];
|
||||
cols.push({ title: 'Name', sortKey: 'node.name', sortOrder: SORT_ASCENDING });
|
||||
cols.push({ title: 'Status', sortKey: 'online' });
|
||||
cols.push({ title: 'Name', sortKey: 'name', sortOrder: SORT_ASCENDING });
|
||||
cols.push({ title: 'Status', sortKey: 'isOnline' });
|
||||
if (showCgroupMetricsElasticsearch) {
|
||||
cols.push({ title: 'CPU Usage', sortKey: 'node_cgroup_quota.lastVal' });
|
||||
cols.push({ title: 'CPU Usage', sortKey: 'node_cgroup_quota' });
|
||||
cols.push({
|
||||
title: 'CPU Throttling',
|
||||
sortKey: 'node_cgroup_throttled.lastVal'
|
||||
sortKey: 'node_cgroup_throttled'
|
||||
});
|
||||
} else {
|
||||
cols.push({ title: 'CPU Usage', sortKey: 'node_cpu_utilization.lastVal' });
|
||||
cols.push({ title: 'Load Average', sortKey: 'node_load_average.lastVal' });
|
||||
cols.push({ title: 'CPU Usage', sortKey: 'node_cpu_utilization' });
|
||||
cols.push({ title: 'Load Average', sortKey: 'node_load_average' });
|
||||
}
|
||||
cols.push({ title: 'JVM Memory', sortKey: 'node_jvm_mem_percent.lastVal' });
|
||||
cols.push({ title: 'Disk Free Space', sortKey: 'node_free_space.lastVal' });
|
||||
cols.push({ title: 'JVM Memory', sortKey: 'node_jvm_mem_percent' });
|
||||
cols.push({ title: 'Disk Free Space', sortKey: 'node_free_space' });
|
||||
cols.push({ title: 'Shards', sortKey: 'shardCount' });
|
||||
return cols;
|
||||
};
|
||||
|
@ -55,12 +55,14 @@ const nodeRowFactory = showCgroupMetricsElasticsearch => {
|
|||
isOnline={isOnline}
|
||||
metric={get(this.props, 'node_cgroup_quota')}
|
||||
isPercent={true}
|
||||
data-test-subj="cpuQuota"
|
||||
/>,
|
||||
<MetricCell
|
||||
key="cpuCol2"
|
||||
isOnline={isOnline}
|
||||
metric={get(this.props, 'node_cgroup_throttled')}
|
||||
isPercent={false}
|
||||
data-test-subj="cpuThrottled"
|
||||
/>
|
||||
];
|
||||
}
|
||||
|
@ -70,12 +72,14 @@ const nodeRowFactory = showCgroupMetricsElasticsearch => {
|
|||
isOnline={isOnline}
|
||||
metric={get(this.props, 'node_cpu_utilization')}
|
||||
isPercent={true}
|
||||
data-test-subj="cpuUsage"
|
||||
/>,
|
||||
<MetricCell
|
||||
key="cpuCol2"
|
||||
isOnline={isOnline}
|
||||
metric={get(this.props, 'node_load_average')}
|
||||
isPercent={false}
|
||||
data-test-subj="loadAverage"
|
||||
/>
|
||||
];
|
||||
}
|
||||
|
@ -84,7 +88,7 @@ const nodeRowFactory = showCgroupMetricsElasticsearch => {
|
|||
if (this.isOnline()) {
|
||||
return (
|
||||
<KuiTableRowCell>
|
||||
<div className="monitoringTableCell__number">
|
||||
<div className="monitoringTableCell__number" data-test-subj="shards">
|
||||
{get(this.props, 'shardCount')}
|
||||
</div>
|
||||
</KuiTableRowCell>
|
||||
|
@ -108,12 +112,14 @@ const nodeRowFactory = showCgroupMetricsElasticsearch => {
|
|||
<span className={`fa ${this.props.nodeTypeClass}`} />
|
||||
</EuiToolTip>
|
||||
|
||||
<EuiLink
|
||||
href={`#/elasticsearch/nodes/${this.props.resolver}`}
|
||||
data-test-subj={`nodeLink-${this.props.resolver}`}
|
||||
>
|
||||
{this.props.name}
|
||||
</EuiLink>
|
||||
<span data-test-subj="name">
|
||||
<EuiLink
|
||||
href={`#/elasticsearch/nodes/${this.props.resolver}`}
|
||||
data-test-subj={`nodeLink-${this.props.resolver}`}
|
||||
>
|
||||
{this.props.name}
|
||||
</EuiLink>
|
||||
</span>
|
||||
</div>
|
||||
<div className="monitoringTableCell__transportAddress">
|
||||
{extractIp(this.props.transport_address)}
|
||||
|
@ -133,11 +139,13 @@ const nodeRowFactory = showCgroupMetricsElasticsearch => {
|
|||
isOnline={isOnline}
|
||||
metric={get(this.props, 'node_jvm_mem_percent')}
|
||||
isPercent={true}
|
||||
data-test-subj="jvmMemory"
|
||||
/>
|
||||
<MetricCell
|
||||
isOnline={isOnline}
|
||||
metric={get(this.props, 'node_free_space')}
|
||||
isPercent={false}
|
||||
data-test-subj="diskFreeSpace"
|
||||
/>
|
||||
{this.getShardCount()}
|
||||
</KuiTableRow>
|
||||
|
|
|
@ -13,34 +13,204 @@ export default function ({ getService, getPageObjects }) {
|
|||
const esClusterSummaryStatus = getService('monitoringElasticsearchSummaryStatus');
|
||||
|
||||
describe('Elasticsearch nodes listing', () => {
|
||||
const { setup, tearDown } = getLifecycleMethods(getService, getPageObjects);
|
||||
describe('with offline node', () => {
|
||||
const { setup, tearDown } = getLifecycleMethods(getService, getPageObjects);
|
||||
|
||||
before(async () => {
|
||||
await setup('monitoring/singlecluster-three-nodes-shard-relocation', {
|
||||
from: '2017-10-05 20:31:48.354',
|
||||
to: '2017-10-05 20:35:12.176',
|
||||
before(async () => {
|
||||
await setup('monitoring/singlecluster-three-nodes-shard-relocation', {
|
||||
from: '2017-10-05 20:28:28.475',
|
||||
to: '2017-10-05 20:34:38.341',
|
||||
});
|
||||
|
||||
// go to nodes listing
|
||||
await overview.clickEsNodes();
|
||||
expect(await nodesList.isOnListing()).to.be(true);
|
||||
});
|
||||
|
||||
// go to nodes listing
|
||||
await overview.clickEsNodes();
|
||||
expect(await nodesList.isOnListing()).to.be(true);
|
||||
after(async () => {
|
||||
await tearDown();
|
||||
});
|
||||
|
||||
it('should have an Elasticsearch Cluster Summary Status with correct info', async () => {
|
||||
expect(await esClusterSummaryStatus.getContent()).to.eql({
|
||||
nodesCount: 'Nodes: 2',
|
||||
indicesCount: 'Indices: 20',
|
||||
memory: 'Memory: 696.6 MB / 1.3 GB',
|
||||
totalShards: 'Total Shards: 79',
|
||||
unassignedShards: 'Unassigned Shards: 7',
|
||||
documentCount: 'Documents: 25,758',
|
||||
dataSize: 'Data: 100.0 MB',
|
||||
health: 'Health: yellow',
|
||||
});
|
||||
});
|
||||
|
||||
it('should have a nodes table with correct rows with default sorting', async () => {
|
||||
const rows = await nodesList.getRows();
|
||||
expect(rows.length).to.be(3);
|
||||
|
||||
const nodesAll = await nodesList.getNodesAll();
|
||||
const tableData = [
|
||||
{ name: 'whatever-01', status: 'Status: Online', cpu: '0%', load: '3.28', memory: '39%', disk: '173.9 GB', shards: '38', },
|
||||
{ name: 'whatever-02', status: 'Status: Online', cpu: '2%', load: '3.28', memory: '25%', disk: '173.9 GB', shards: '38', },
|
||||
{ name: 'whatever-03', status: 'Status: Offline' },
|
||||
];
|
||||
nodesAll.forEach((obj, node) => {
|
||||
expect(nodesAll[node].name).to.be(tableData[node].name);
|
||||
expect(nodesAll[node].status).to.be(tableData[node].status);
|
||||
expect(nodesAll[node].cpu).to.be(tableData[node].cpu);
|
||||
expect(nodesAll[node].load).to.be(tableData[node].load);
|
||||
expect(nodesAll[node].memory).to.be(tableData[node].memory);
|
||||
expect(nodesAll[node].disk).to.be(tableData[node].disk);
|
||||
expect(nodesAll[node].shards).to.be(tableData[node].shards);
|
||||
});
|
||||
});
|
||||
|
||||
it('should sort by name', async () => {
|
||||
await nodesList.clickNameCol();
|
||||
await nodesList.clickNameCol();
|
||||
|
||||
const nodesAll = await nodesList.getNodesAll();
|
||||
const tableData = [
|
||||
{ name: 'whatever-01' },
|
||||
{ name: 'whatever-02' },
|
||||
{ name: 'whatever-03' },
|
||||
];
|
||||
nodesAll.forEach((obj, node) => {
|
||||
expect(nodesAll[node].name).to.be(tableData[node].name);
|
||||
});
|
||||
});
|
||||
|
||||
it('should sort by status', async () => {
|
||||
await nodesList.clickStatusCol();
|
||||
await nodesList.clickStatusCol();
|
||||
|
||||
const nodesAll = await nodesList.getNodesAll();
|
||||
const tableData = [
|
||||
{ status: 'Status: Online' },
|
||||
{ status: 'Status: Online' },
|
||||
{ status: 'Status: Offline' },
|
||||
];
|
||||
nodesAll.forEach((obj, node) => {
|
||||
expect(nodesAll[node].status).to.be(tableData[node].status);
|
||||
});
|
||||
});
|
||||
|
||||
it('should sort by cpu', async () => {
|
||||
await nodesList.clickCpuCol();
|
||||
await nodesList.clickCpuCol();
|
||||
|
||||
const nodesAll = await nodesList.getNodesAll();
|
||||
const tableData = [{ cpu: '0%' }, { cpu: '2%' }, { cpu: undefined }];
|
||||
nodesAll.forEach((obj, node) => {
|
||||
expect(nodesAll[node].cpu).to.be(tableData[node].cpu);
|
||||
});
|
||||
});
|
||||
|
||||
it('should sort by load average', async () => {
|
||||
await nodesList.clickLoadCol();
|
||||
await nodesList.clickLoadCol();
|
||||
|
||||
const nodesAll = await nodesList.getNodesAll();
|
||||
const tableData = [
|
||||
{ load: '3.28' },
|
||||
{ load: '3.28' },
|
||||
{ load: undefined },
|
||||
];
|
||||
nodesAll.forEach((obj, node) => {
|
||||
expect(nodesAll[node].load).to.be(tableData[node].load);
|
||||
});
|
||||
});
|
||||
|
||||
it('should sort by memory', async () => {
|
||||
await nodesList.clickMemoryCol();
|
||||
await nodesList.clickMemoryCol();
|
||||
|
||||
const nodesAll = await nodesList.getNodesAll();
|
||||
const tableData = [
|
||||
{ memory: '39%' },
|
||||
{ memory: '25%' },
|
||||
{ memory: undefined },
|
||||
];
|
||||
nodesAll.forEach((obj, node) => {
|
||||
expect(nodesAll[node].memory).to.be(tableData[node].memory);
|
||||
});
|
||||
});
|
||||
|
||||
it('should sort by disk', async () => {
|
||||
await nodesList.clickDiskCol();
|
||||
await nodesList.clickDiskCol();
|
||||
|
||||
const nodesAll = await nodesList.getNodesAll();
|
||||
const tableData = [
|
||||
{ disk: '173.9 GB' },
|
||||
{ disk: '173.9 GB' },
|
||||
{ disk: undefined },
|
||||
];
|
||||
nodesAll.forEach((obj, node) => {
|
||||
expect(nodesAll[node].disk).to.be(tableData[node].disk);
|
||||
});
|
||||
});
|
||||
|
||||
it('should sort by shards', async () => {
|
||||
await nodesList.clickShardsCol();
|
||||
await nodesList.clickShardsCol();
|
||||
|
||||
const nodesAll = await nodesList.getNodesAll();
|
||||
const tableData = [
|
||||
{ shards: '38' },
|
||||
{ shards: '38' },
|
||||
{ shards: undefined },
|
||||
];
|
||||
nodesAll.forEach((obj, node) => {
|
||||
expect(nodesAll[node].shards).to.be(tableData[node].shards);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await tearDown();
|
||||
});
|
||||
describe('with only online nodes', () => {
|
||||
const { setup, tearDown } = getLifecycleMethods(getService, getPageObjects);
|
||||
|
||||
it('should have an Elasticsearch Cluster Summary Status with correct info', async () => {
|
||||
expect(await esClusterSummaryStatus.getContent()).to.eql({
|
||||
nodesCount: 'Nodes: 3',
|
||||
indicesCount: 'Indices: 20',
|
||||
memory: 'Memory: 575.3 MB / 2.0 GB',
|
||||
totalShards: 'Total Shards: 80',
|
||||
unassignedShards: 'Unassigned Shards: 5',
|
||||
documentCount: 'Documents: 25,927',
|
||||
dataSize: 'Data: 101.6 MB',
|
||||
health: 'Health: yellow',
|
||||
before(async () => {
|
||||
await setup('monitoring/singlecluster-three-nodes-shard-relocation', {
|
||||
from: '2017-10-05 20:31:48.354',
|
||||
to: '2017-10-05 20:35:12.176',
|
||||
});
|
||||
|
||||
// go to nodes listing
|
||||
await overview.clickEsNodes();
|
||||
expect(await nodesList.isOnListing()).to.be(true);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await tearDown();
|
||||
});
|
||||
|
||||
it('should have an Elasticsearch Cluster Summary Status with correct info', async () => {
|
||||
expect(await esClusterSummaryStatus.getContent()).to.eql({
|
||||
nodesCount: 'Nodes: 3',
|
||||
indicesCount: 'Indices: 20',
|
||||
memory: 'Memory: 575.3 MB / 2.0 GB',
|
||||
totalShards: 'Total Shards: 80',
|
||||
unassignedShards: 'Unassigned Shards: 5',
|
||||
documentCount: 'Documents: 25,927',
|
||||
dataSize: 'Data: 101.6 MB',
|
||||
health: 'Health: yellow',
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter for specific indices', async () => {
|
||||
await nodesList.setFilter('01');
|
||||
const rows = await nodesList.getRows();
|
||||
expect(rows.length).to.be(1);
|
||||
await nodesList.clearFilter();
|
||||
});
|
||||
|
||||
it('should filter for non-existent index', async () => {
|
||||
await nodesList.setFilter('foobar');
|
||||
await nodesList.assertNoData();
|
||||
await nodesList.clearFilter();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
|
|
@ -4,12 +4,36 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export function MonitoringElasticsearchNodesProvider({ getService/*, getPageObjects */ }) {
|
||||
import { range } from 'lodash';
|
||||
|
||||
export function MonitoringElasticsearchNodesProvider({ getService, getPageObjects }) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const PageObjects = getPageObjects(['monitoring']);
|
||||
const retry = getService('retry');
|
||||
|
||||
const SUBJ_LISTING_PAGE = 'elasticsearchNodesListingPage';
|
||||
|
||||
const SUBJ_TABLE_CONTAINER = 'elasticsearchNodesTableContainer';
|
||||
const SUBJ_TABLE_NO_DATA = `${SUBJ_TABLE_CONTAINER} monitoringTableNoData`;
|
||||
const SUBJ_SEARCH_BAR = `${SUBJ_TABLE_CONTAINER} monitoringTableToolBar`;
|
||||
|
||||
const SUBJ_TABLE_SORT_NAME_COL = `${SUBJ_TABLE_CONTAINER} tableHeaderCell-name`;
|
||||
const SUBJ_TABLE_SORT_STATUS_COL = `${SUBJ_TABLE_CONTAINER} tableHeaderCell-status`;
|
||||
const SUBJ_TABLE_SORT_CPU_COL = `${SUBJ_TABLE_CONTAINER} tableHeaderCell-cpuUsage`;
|
||||
const SUBJ_TABLE_SORT_LOAD_COL = `${SUBJ_TABLE_CONTAINER} tableHeaderCell-loadAverage`;
|
||||
const SUBJ_TABLE_SORT_MEM_COL = `${SUBJ_TABLE_CONTAINER} tableHeaderCell-jvmMemory`;
|
||||
const SUBJ_TABLE_SORT_DISK_COL = `${SUBJ_TABLE_CONTAINER} tableHeaderCell-diskFreeSpace`;
|
||||
const SUBJ_TABLE_SORT_SHARDS_COL = `${SUBJ_TABLE_CONTAINER} tableHeaderCell-shards`;
|
||||
|
||||
const SUBJ_TABLE_BODY = 'elasticsearchNodesTableBody';
|
||||
const SUBJ_NODES_NAMES = `${SUBJ_TABLE_BODY} name`;
|
||||
const SUBJ_NODES_STATUSES = `${SUBJ_TABLE_BODY} statusIcon`;
|
||||
const SUBJ_NODES_CPUS = `${SUBJ_TABLE_BODY} cpuUsage`;
|
||||
const SUBJ_NODES_LOADS = `${SUBJ_TABLE_BODY} loadAverage`;
|
||||
const SUBJ_NODES_MEMS = `${SUBJ_TABLE_BODY} jvmMemory`;
|
||||
const SUBJ_NODES_DISKS = `${SUBJ_TABLE_BODY} diskFreeSpace`;
|
||||
const SUBJ_NODES_SHARDS = `${SUBJ_TABLE_BODY} shards`;
|
||||
|
||||
const SUBJ_NODE_LINK_PREFIX = `${SUBJ_TABLE_BODY} nodeLink-`;
|
||||
|
||||
return new class ElasticsearchIndices {
|
||||
|
@ -22,5 +46,91 @@ export function MonitoringElasticsearchNodesProvider({ getService/*, getPageObje
|
|||
return testSubjects.click(SUBJ_NODE_LINK_PREFIX + nodeResolver);
|
||||
}
|
||||
|
||||
async clickNameCol() {
|
||||
const headerCell = await testSubjects.find(SUBJ_TABLE_SORT_NAME_COL);
|
||||
const button = await headerCell.findByTagName('button');
|
||||
return button.click();
|
||||
}
|
||||
|
||||
async clickStatusCol() {
|
||||
const headerCell = await testSubjects.find(SUBJ_TABLE_SORT_STATUS_COL);
|
||||
const button = await headerCell.findByTagName('button');
|
||||
return button.click();
|
||||
}
|
||||
|
||||
async clickCpuCol() {
|
||||
const headerCell = await testSubjects.find(SUBJ_TABLE_SORT_CPU_COL);
|
||||
const button = await headerCell.findByTagName('button');
|
||||
return button.click();
|
||||
}
|
||||
|
||||
async clickLoadCol() {
|
||||
const headerCell = await testSubjects.find(SUBJ_TABLE_SORT_LOAD_COL);
|
||||
const button = await headerCell.findByTagName('button');
|
||||
return button.click();
|
||||
}
|
||||
|
||||
async clickMemoryCol() {
|
||||
const headerCell = await testSubjects.find(SUBJ_TABLE_SORT_MEM_COL);
|
||||
const button = await headerCell.findByTagName('button');
|
||||
return button.click();
|
||||
}
|
||||
|
||||
async clickDiskCol() {
|
||||
const headerCell = await testSubjects.find(SUBJ_TABLE_SORT_DISK_COL);
|
||||
const button = await headerCell.findByTagName('button');
|
||||
return button.click();
|
||||
}
|
||||
|
||||
async clickShardsCol() {
|
||||
const headerCell = await testSubjects.find(SUBJ_TABLE_SORT_SHARDS_COL);
|
||||
const button = await headerCell.findByTagName('button');
|
||||
return button.click();
|
||||
}
|
||||
|
||||
getRows() {
|
||||
return PageObjects.monitoring.tableGetRows(SUBJ_TABLE_BODY);
|
||||
}
|
||||
|
||||
setFilter(text) {
|
||||
return PageObjects.monitoring.tableSetFilter(SUBJ_SEARCH_BAR, text);
|
||||
}
|
||||
|
||||
clearFilter() {
|
||||
return PageObjects.monitoring.tableClearFilter(SUBJ_SEARCH_BAR);
|
||||
}
|
||||
|
||||
assertNoData() {
|
||||
return PageObjects.monitoring.assertTableNoData(SUBJ_TABLE_NO_DATA);
|
||||
}
|
||||
|
||||
async getNodesAll() {
|
||||
const names = await testSubjects.getVisibleTextAll(SUBJ_NODES_NAMES);
|
||||
const statuses = await testSubjects.getPropertyAll(SUBJ_NODES_STATUSES, 'alt');
|
||||
const cpus = await testSubjects.getVisibleTextAll(SUBJ_NODES_CPUS);
|
||||
const loads = await testSubjects.getVisibleTextAll(SUBJ_NODES_LOADS);
|
||||
const memories = await testSubjects.getVisibleTextAll(SUBJ_NODES_MEMS);
|
||||
const disks = await testSubjects.getVisibleTextAll(SUBJ_NODES_DISKS);
|
||||
const shards = await testSubjects.getVisibleTextAll(SUBJ_NODES_SHARDS);
|
||||
|
||||
// tuple-ize the icons and texts together into an array of objects
|
||||
const tableRows = await this.getRows();
|
||||
const iterator = range(tableRows.length);
|
||||
return iterator.reduce((all, current) => {
|
||||
return [
|
||||
...all,
|
||||
{
|
||||
name: names[current],
|
||||
status: statuses[current],
|
||||
cpu: cpus[current],
|
||||
load: loads[current],
|
||||
memory: memories[current],
|
||||
disk: disks[current],
|
||||
shards: shards[current],
|
||||
}
|
||||
];
|
||||
}, []);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue