Made the searching of generic tables work on the UI

Extracted table headers common code on the frontend

Fixed the search matching and extracted further common code in the UI
This commit is contained in:
Filip Barl
2017-01-03 16:43:41 +01:00
parent e475a09ee6
commit 6888108b83
12 changed files with 275 additions and 251 deletions

View File

@@ -5,6 +5,7 @@ import { Map as makeMap } from 'immutable';
import { clickCloseDetails, clickShowTopologyForNode } from '../actions/app-actions';
import { brightenColor, getNeutralColor, getNodeColorDark } from '../utils/color-utils';
import { isGenericTable, isPropertyList } from '../utils/node-details-utils';
import { resetDocumentTitle, setDocumentTitle } from '../utils/title-utils';
import MatchedText from './matched-text';
@@ -25,9 +26,6 @@ function getTruncationText(count) {
+ ` (${count} extra entries not included). We are working to remove this limitation.`;
}
const TABLE_TYPE_PROPERTY_LIST = 'property-list';
const TABLE_TYPE_GENERIC = 'multicolumn-table';
class NodeDetails extends React.Component {
constructor(props, context) {
super(props, context);
@@ -215,7 +213,7 @@ class NodeDetails extends React.Component {
return (
<div className="node-details-content-section" key={table.id}>
<div className="node-details-content-section-header">
{table.label}
{table.label.length > 0 && table.label}
{table.truncationCount > 0 && <span
className="node-details-content-section-header-warning">
<Warning text={getTruncationText(table.truncationCount)} />
@@ -234,25 +232,25 @@ class NodeDetails extends React.Component {
renderTable(table) {
const { nodeMatches = makeMap() } = this.props;
switch (table.type) {
case TABLE_TYPE_GENERIC:
return (
<NodeDetailsGenericTable
rows={table.rows} columns={table.columns}
matches={nodeMatches.get('tables')}
/>
);
case TABLE_TYPE_PROPERTY_LIST:
return (
<NodeDetailsLabels
rows={table.rows} controls={table.controls}
matches={nodeMatches.get('tables')}
/>
);
default:
logError(`Undefined type '${table.type}' for table ${table.id}`);
return null;
if (isGenericTable(table)) {
return (
<NodeDetailsGenericTable
rows={table.rows} columns={table.columns}
matches={nodeMatches.get('tables')}
/>
);
} else if (isPropertyList(table)) {
return (
<NodeDetailsLabels
rows={table.rows} controls={table.controls}
matches={nodeMatches.get('labels')}
/>
);
}
logError(`Undefined type '${table.type}' for table ${table.id}`);
return null;
}
componentDidUpdate() {

View File

@@ -2,28 +2,25 @@ import React from 'react';
import { Map as makeMap } from 'immutable';
import { sortBy } from 'lodash';
import { NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT } from '../../constants/limits';
import {
isNumber, getTableColumnsStyles, genericTableEntryKey
} from '../../utils/node-details-utils';
import NodeDetailsTableHeaders from './node-details-table-headers';
import MatchedText from '../matched-text';
import ShowMore from '../show-more';
function columnStyle(column) {
return {
textAlign: column.dataType === 'number' ? 'right' : 'left',
paddingRight: '10px',
maxWidth: '140px'
};
}
function sortedRows(rows, sortedByColumn, sortedDesc) {
const orderedRows = sortBy(rows, row => row.id);
const sorted = sortBy(orderedRows, (row) => {
let value = row.entries[sortedByColumn.id];
if (sortedByColumn.dataType === 'number') {
function sortedRows(rows, columns, sortedBy, sortedDesc) {
const column = columns.find(c => c.id === sortedBy);
const sorted = sortBy(rows, (row) => {
let value = row.entries[sortedBy];
if (isNumber(column)) {
value = parseFloat(value);
}
return value;
});
if (!sortedDesc) {
if (sortedDesc) {
sorted.reverse();
}
return sorted;
@@ -32,85 +29,68 @@ function sortedRows(rows, sortedByColumn, sortedDesc) {
export default class NodeDetailsGenericTable extends React.Component {
constructor(props, context) {
super(props, context);
this.DEFAULT_LIMIT = 5;
this.state = {
limit: this.DEFAULT_LIMIT,
sortedByColumn: props.columns[0],
limit: NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT,
sortedBy: props.columns[0].id,
sortedDesc: true
};
this.handleLimitClick = this.handleLimitClick.bind(this);
this.updateSorted = this.updateSorted.bind(this);
}
handleHeaderClick(ev, column) {
ev.preventDefault();
this.setState({
sortedByColumn: column,
sortedDesc: this.state.sortedByColumn.id === column.id
? !this.state.sortedDesc : true
});
updateSorted(sortedBy, sortedDesc) {
this.setState({ sortedBy, sortedDesc });
}
handleLimitClick() {
const limit = this.state.limit ? 0 : this.DEFAULT_LIMIT;
this.setState({limit});
this.setState({
limit: this.state.limit ? 0 : NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT
});
}
render() {
const { sortedByColumn, sortedDesc } = this.state;
const { sortedBy, sortedDesc } = this.state;
const { columns, matches = makeMap() } = this.props;
let rows = this.props.rows;
let notShown = 0;
const limited = rows && this.state.limit > 0 && rows.length > this.state.limit;
const expanded = this.state.limit === 0;
if (rows && limited) {
const hasNotShownMatch = rows.filter((row, index) => index >= this.state.limit
&& matches.has(row.id)).length > 0;
if (!hasNotShownMatch) {
notShown = rows.length - this.DEFAULT_LIMIT;
// Stabilize the order of rows
let rows = sortBy(this.props.rows || [], row => row.id);
let notShown = 0;
// If there are rows that would be hidden behind 'show more', keep them
// expanded if any of them match the search query; otherwise hide them.
if (this.state.limit > 0 && rows.length > this.state.limit) {
const hasHiddenMatch = rows.slice(this.state.limit).some(row =>
columns.some(column => matches.has(genericTableEntryKey(row, column)))
);
if (!hasHiddenMatch) {
notShown = rows.length - NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT;
rows = rows.slice(0, this.state.limit);
}
}
const styles = getTableColumnsStyles(columns);
return (
<div className="node-details-generic-table">
<table>
<thead>
<tr>
{columns.map((column) => {
const onHeaderClick = (ev) => {
this.handleHeaderClick(ev, column);
};
const isSorted = column.id === this.state.sortedByColumn.id;
const isSortedDesc = isSorted && this.state.sortedDesc;
const isSortedAsc = isSorted && !isSortedDesc;
const style = Object.assign(columnStyle(column), {
cursor: 'pointer',
fontSize: '11px'
});
return (
<th
className="node-details-generic-table-header"
key={column.id} style={style} onClick={onHeaderClick}>
{isSortedAsc
&& <span className="node-details-table-header-sorter fa fa-caret-up" />}
{isSortedDesc
&& <span className="node-details-table-header-sorter fa fa-caret-down" />}
{column.label}
</th>
);
})}
</tr>
<NodeDetailsTableHeaders
headers={columns}
sortedBy={sortedBy}
sortedDesc={sortedDesc}
onClick={this.updateSorted}
/>
</thead>
<tbody>
{sortedRows(rows, sortedByColumn, sortedDesc).map(row => (
{sortedRows(rows, columns, sortedBy, sortedDesc).map(row => (
<tr className="node-details-generic-table-row" key={row.id}>
{columns.map((column) => {
{columns.map((column, index) => {
const match = matches.get(genericTableEntryKey(row, column));
const value = row.entries[column.id];
const match = matches.get(column.id);
return (
<td
className="node-details-generic-table-field-value truncate"
title={value} key={column.id} style={columnStyle(column)}>
className="node-details-generic-table-value truncate"
title={value} key={column.id} style={styles[index]}>
<MatchedText text={value} match={match} />
</td>
);

View File

@@ -2,8 +2,9 @@ import React from 'react';
import { Map as makeMap } from 'immutable';
import sortBy from 'lodash/sortBy';
import MatchedText from '../matched-text';
import { NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT } from '../../constants/limits';
import NodeDetailsControlButton from './node-details-control-button';
import MatchedText from '../matched-text';
import ShowMore from '../show-more';
const Controls = controls => (
@@ -17,15 +18,14 @@ export default class NodeDetailsLabels extends React.Component {
constructor(props, context) {
super(props, context);
this.DEFAULT_LIMIT = 5;
this.state = {
limit: this.DEFAULT_LIMIT,
limit: NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT,
};
this.handleLimitClick = this.handleLimitClick.bind(this);
}
handleLimitClick() {
const limit = this.state.limit ? 0 : this.DEFAULT_LIMIT;
const limit = this.state.limit ? 0 : NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT;
this.setState({limit});
}
@@ -39,7 +39,7 @@ export default class NodeDetailsLabels extends React.Component {
const hasNotShownMatch = rows.filter((row, index) => index >= this.state.limit
&& matches.has(row.id)).length > 0;
if (!hasNotShownMatch) {
notShown = rows.length - this.DEFAULT_LIMIT;
notShown = rows.length - NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT;
rows = rows.slice(0, this.state.limit);
}
}

View File

@@ -1,22 +1,22 @@
import React from 'react';
import { Map as makeMap } from 'immutable';
import { NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT } from '../../constants/limits';
import NodeDetailsRelativesLink from './node-details-relatives-link';
export default class NodeDetailsRelatives extends React.Component {
constructor(props, context) {
super(props, context);
this.DEFAULT_LIMIT = 5;
this.state = {
limit: this.DEFAULT_LIMIT
limit: NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT
};
this.handleLimitClick = this.handleLimitClick.bind(this);
}
handleLimitClick(ev) {
ev.preventDefault();
const limit = this.state.limit ? 0 : this.DEFAULT_LIMIT;
const limit = this.state.limit ? 0 : NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT;
this.setState({limit});
}
@@ -26,7 +26,7 @@ export default class NodeDetailsRelatives extends React.Component {
const limited = this.state.limit > 0 && relatives.length > this.state.limit;
const showLimitAction = limited || (this.state.limit === 0
&& relatives.length > this.DEFAULT_LIMIT);
&& relatives.length > NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT);
const limitActionText = limited ? 'Show more' : 'Show less';
if (limited) {
relatives = relatives.slice(0, this.state.limit);

View File

@@ -0,0 +1,55 @@
import React from 'react';
import { defaultSortDesc, getTableColumnsStyles } from '../../utils/node-details-utils';
import { NODE_DETAILS_TABLE_CW, NODE_DETAILS_TABLE_XS_LABEL } from '../../constants/styles';
export default class NodeDetailsTableHeaders extends React.Component {
handleClick(ev, headerId, currentSortedBy, currentSortedDesc) {
ev.preventDefault();
const header = this.props.headers.find(h => h.id === headerId);
const sortedBy = header.id;
const sortedDesc = sortedBy === currentSortedBy
? !currentSortedDesc : defaultSortDesc(header);
this.props.onClick(sortedBy, sortedDesc);
}
render() {
const { headers, sortedBy, sortedDesc } = this.props;
const colStyles = getTableColumnsStyles(headers);
return (
<tr>
{headers.map((header, index) => {
const headerClasses = ['node-details-table-header', 'truncate'];
const onClick = (ev) => {
this.handleClick(ev, header.id, sortedBy, sortedDesc);
};
// sort by first metric by default
const isSorted = header.id === sortedBy;
const isSortedDesc = isSorted && sortedDesc;
const isSortedAsc = isSorted && !isSortedDesc;
if (isSorted) {
headerClasses.push('node-details-table-header-sorted');
}
const style = colStyles[index];
const label =
(style.width === NODE_DETAILS_TABLE_CW.XS && NODE_DETAILS_TABLE_XS_LABEL[header.id]) ?
NODE_DETAILS_TABLE_XS_LABEL[header.id] : header.label;
return (
<td
className={headerClasses.join(' ')} style={style} onClick={onClick}
title={header.label} key={header.id}>
{isSortedAsc
&& <span className="node-details-table-header-sorter fa fa-caret-up" />}
{isSortedDesc
&& <span className="node-details-table-header-sorter fa fa-caret-down" />}
{label}
</td>
);
})}
</tr>
);
}
}

View File

@@ -2,59 +2,15 @@ import React from 'react';
import classNames from 'classnames';
import { find, get, union, sortBy, groupBy, concat } from 'lodash';
import { NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT } from '../../constants/limits';
import ShowMore from '../show-more';
import NodeDetailsTableRow from './node-details-table-row';
import NodeDetailsTableHeaders from './node-details-table-headers';
import { ipToPaddedString } from '../../utils/string-utils';
function isNumber(data) {
return data.dataType && data.dataType === 'number';
}
function isIP(data) {
return data.dataType && data.dataType === 'ip';
}
const CW = {
XS: '32px',
S: '50px',
M: '70px',
L: '120px',
XL: '140px',
XXL: '170px',
};
const XS_LABEL = {
count: '#',
// TODO: consider changing the name of this field on the BE
container: '#',
};
const COLUMN_WIDTHS = {
count: CW.XS,
container: CW.XS,
docker_container_created: CW.XXL,
docker_container_restart_count: CW.M,
docker_container_state_human: CW.XXL,
docker_container_uptime: '85px',
docker_cpu_total_usage: CW.M,
docker_memory_usage: CW.M,
open_files_count: CW.M,
pid: CW.S,
port: CW.S,
ppid: CW.S,
process_cpu_usage_percent: CW.M,
process_memory_usage_bytes: CW.M,
threads: CW.M,
// e.g. details panel > pods
kubernetes_ip: CW.L,
kubernetes_state: CW.M,
};
import {
isIP, isNumber, defaultSortDesc, getTableColumnsStyles
} from '../../utils/node-details-utils';
function getDefaultSortedBy(columns, nodes) {
@@ -158,54 +114,27 @@ function getSortedNodes(nodes, sortedByHeader, sortedDesc) {
}
function getColumnWidth(headers, h) {
//
// More beauty hacking, ports and counts can only get so big, free up WS for other longer
// fields like IPs!
//
return COLUMN_WIDTHS[h.id];
}
function getColumnsStyles(headers) {
return headers.map((h, i) => ({
width: getColumnWidth(headers, h, i),
textAlign: h.dataType === 'number' ? 'right' : 'left',
}));
}
function defaultSortDesc(header) {
return header && header.dataType === 'number';
}
export default class NodeDetailsTable extends React.Component {
constructor(props, context) {
super(props, context);
this.DEFAULT_LIMIT = 5;
this.state = {
limit: props.limit || this.DEFAULT_LIMIT,
limit: props.limit || NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT,
sortedDesc: this.props.sortedDesc,
sortedBy: this.props.sortedBy
};
this.handleLimitClick = this.handleLimitClick.bind(this);
this.updateSorted = this.updateSorted.bind(this);
}
handleHeaderClick(ev, headerId, currentSortedBy, currentSortedDesc) {
ev.preventDefault();
const header = this.getColumnHeaders().find(h => h.id === headerId);
const sortedBy = header.id;
const sortedDesc = header.id === currentSortedBy
? !currentSortedDesc : defaultSortDesc(header);
this.setState({sortedBy, sortedDesc});
updateSorted(sortedBy, sortedDesc) {
this.setState({ sortedBy, sortedDesc });
this.props.onSortChange(sortedBy, sortedDesc);
}
handleLimitClick() {
const limit = this.state.limit ? 0 : this.DEFAULT_LIMIT;
this.setState({limit});
const limit = this.state.limit ? 0 : NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT;
this.setState({ limit });
}
getColumnHeaders() {
@@ -213,60 +142,13 @@ export default class NodeDetailsTable extends React.Component {
return [{id: 'label', label: this.props.label}].concat(columns);
}
renderHeaders(sortedBy, sortedDesc) {
if (!this.props.nodes || this.props.nodes.length === 0) {
return null;
}
const headers = this.getColumnHeaders();
const colStyles = getColumnsStyles(headers);
return (
<tr>
{headers.map((header, i) => {
const headerClasses = ['node-details-table-header', 'truncate'];
const onHeaderClick = (ev) => {
this.handleHeaderClick(ev, header.id, sortedBy, sortedDesc);
};
// sort by first metric by default
const isSorted = header.id === sortedBy;
const isSortedDesc = isSorted && sortedDesc;
const isSortedAsc = isSorted && !isSortedDesc;
if (isSorted) {
headerClasses.push('node-details-table-header-sorted');
}
const style = colStyles[i];
const label = (style.width === CW.XS && XS_LABEL[header.id]) ?
XS_LABEL[header.id] :
header.label;
return (
<td
className={headerClasses.join(' ')} style={style} onClick={onHeaderClick}
title={header.label} key={header.id}>
{isSortedAsc
&& <span className="node-details-table-header-sorter fa fa-caret-up" />}
{isSortedDesc
&& <span className="node-details-table-header-sorter fa fa-caret-down" />}
{label}
</td>
);
})}
</tr>
);
}
render() {
const { nodeIdKey, columns, topologyId, onClickRow, onMouseEnter, onMouseLeave,
onMouseEnterRow, onMouseLeaveRow } = this.props;
const sortedBy = this.state.sortedBy || getDefaultSortedBy(columns, this.props.nodes);
const sortedByHeader = this.getColumnHeaders().find(h => h.id === sortedBy);
const sortedDesc = this.state.sortedDesc !== null ?
this.state.sortedDesc :
defaultSortDesc(sortedByHeader);
const sortedDesc = this.state.sortedDesc || defaultSortDesc(sortedByHeader);
let nodes = getSortedNodes(this.props.nodes, sortedByHeader, sortedDesc);
const limited = nodes && this.state.limit > 0 && nodes.length > this.state.limit;
@@ -277,13 +159,20 @@ export default class NodeDetailsTable extends React.Component {
}
const className = classNames('node-details-table-wrapper-wrapper', this.props.className);
const headers = this.getColumnHeaders();
const styles = getTableColumnsStyles(headers);
return (
<div className={className} style={this.props.style}>
<div className="node-details-table-wrapper">
<table className="node-details-table">
<thead>
{this.renderHeaders(sortedBy, sortedDesc)}
{this.props.nodes && this.props.nodes.length > 0 && <NodeDetailsTableHeaders
headers={headers}
sortedBy={sortedBy}
sortedDesc={sortedDesc}
onClick={this.updateSorted}
/>}
</thead>
<tbody
style={this.props.tbodyStyle}
@@ -296,7 +185,7 @@ export default class NodeDetailsTable extends React.Component {
selected={this.props.selectedNodeId === node.id}
node={node}
nodeIdKey={nodeIdKey}
colStyles={getColumnsStyles(this.getColumnHeaders())}
colStyles={styles}
columns={columns}
onClick={onClickRow}
onMouseLeaveRow={onMouseLeaveRow}

View File

@@ -0,0 +1,2 @@
export const NODE_DETAILS_DATA_ROWS_DEFAULT_LIMIT = 5;

View File

@@ -26,3 +26,47 @@ export const MIN_NODE_SIZE = 24;
export const MAX_NODE_SIZE = 96;
export const BASE_NODE_LABEL_SIZE = 14;
export const MIN_NODE_LABEL_SIZE = 12;
// Node details table constants
export const NODE_DETAILS_TABLE_CW = {
XS: '32px',
S: '50px',
M: '70px',
L: '85px',
XL: '120px',
XXL: '140px',
XXXL: '170px',
};
export const NODE_DETAILS_TABLE_COLUMN_WIDTHS = {
count: NODE_DETAILS_TABLE_CW.XS,
container: NODE_DETAILS_TABLE_CW.XS,
docker_container_created: NODE_DETAILS_TABLE_CW.XXXL,
docker_container_restart_count: NODE_DETAILS_TABLE_CW.M,
docker_container_state_human: NODE_DETAILS_TABLE_CW.XXXL,
docker_container_uptime: NODE_DETAILS_TABLE_CW.L,
docker_cpu_total_usage: NODE_DETAILS_TABLE_CW.M,
docker_memory_usage: NODE_DETAILS_TABLE_CW.M,
open_files_count: NODE_DETAILS_TABLE_CW.M,
pid: NODE_DETAILS_TABLE_CW.S,
port: NODE_DETAILS_TABLE_CW.S,
ppid: NODE_DETAILS_TABLE_CW.S,
process_cpu_usage_percent: NODE_DETAILS_TABLE_CW.M,
process_memory_usage_bytes: NODE_DETAILS_TABLE_CW.M,
threads: NODE_DETAILS_TABLE_CW.M,
// e.g. details panel > pods
kubernetes_ip: NODE_DETAILS_TABLE_CW.XL,
kubernetes_state: NODE_DETAILS_TABLE_CW.M,
// weave connections
weave_connection_connection: NODE_DETAILS_TABLE_CW.XXL,
weave_connection_state: NODE_DETAILS_TABLE_CW.L,
weave_connection_info: NODE_DETAILS_TABLE_CW.XL,
};
export const NODE_DETAILS_TABLE_XS_LABEL = {
count: '#',
// TODO: consider changing the name of this field on the BE
container: '#',
};

View File

@@ -0,0 +1,36 @@
import { NODE_DETAILS_TABLE_COLUMN_WIDTHS } from '../constants/styles';
export function isGenericTable(table) {
return (table.type || (table.get && table.get('type'))) === 'multicolumn-table';
}
export function isPropertyList(table) {
return (table.type || (table.get && table.get('type'))) === 'property-list';
}
export function isNumber(data) {
return data.dataType && data.dataType === 'number';
}
export function isIP(data) {
return data.dataType && data.dataType === 'ip';
}
export function genericTableEntryKey(row, column) {
const columnId = column.id || column.get('id');
const rowId = row.id || row.get('id');
return `${rowId}_${columnId}`;
}
export function defaultSortDesc(header) {
return header && isNumber(header);
}
export function getTableColumnsStyles(headers) {
return headers.map(header => ({
// More beauty hacking, ports and counts can only get
// so big, free up WS for other longer fields like IPs!
width: NODE_DETAILS_TABLE_COLUMN_WIDTHS[header.id],
textAlign: isNumber(header) ? 'right' : 'left'
}));
}

View File

@@ -1,6 +1,7 @@
import { Map as makeMap, Set as makeSet, List as makeList } from 'immutable';
import { escapeRegExp } from 'lodash';
import { isGenericTable, isPropertyList, genericTableEntryKey } from './node-details-utils';
import { slugify } from './string-utils';
// topolevel search fields
@@ -148,20 +149,26 @@ export function searchTopology(nodes, { prefix, query, metric, comp, value }) {
});
}
// tables (envvars and labels)
const tables = node.get('tables');
if (tables) {
tables.forEach((table) => {
if (table.get('rows')) {
table.get('rows').forEach((field) => {
const entries = field.get('entries');
const keyPath = [nodeId, 'tables', field.get('id')];
nodeMatches = findNodeMatch(nodeMatches, keyPath, entries.get('value'),
query, prefix, entries.get('label'));
});
}
// property lists
(node.get('tables') || []).filter(isPropertyList).forEach((propertyList) => {
(propertyList.get('rows') || []).forEach((row) => {
const entries = row.get('entries');
const keyPath = [nodeId, 'labels', row.get('id')];
nodeMatches = findNodeMatch(nodeMatches, keyPath, entries.get('value'),
query, prefix, entries.get('label'));
});
}
});
// generic tables
(node.get('tables') || []).filter(isGenericTable).forEach((table) => {
(table.get('rows') || []).forEach((row) => {
table.get('columns').forEach((column) => {
const val = row.get('entries').get(column.get('id'));
const keyPath = [nodeId, 'tables', genericTableEntryKey(row, column)];
nodeMatches = findNodeMatch(nodeMatches, keyPath, val, query);
});
});
});
} else if (metric) {
const metrics = node.get('metrics');
if (metrics) {

View File

@@ -897,6 +897,17 @@ h2 {
}
}
&-generic-table {
width: 100%;
tr {
display: flex;
th, td {
padding: 0 5px;
}
}
}
&-table {
width: 100%;
border-spacing: 0;

View File

@@ -46,6 +46,9 @@ const (
WeavePluginTableID = "weave_plugin_table"
WeavePluginStatus = "weave_plugin_status"
WeavePluginDriver = "weave_plugin_driver"
WeaveConnectionsConnection = "weave_connection_connection"
WeaveConnectionsState = "weave_connection_state"
WeaveConnectionsInfo = "weave_connection_info"
WeaveConnectionsTablePrefix = "weave_connections_table_"
WeaveConnectionsMulticolumnTablePrefix = "weave_connections_multicolumn_table_"
)
@@ -115,26 +118,25 @@ var (
},
WeaveConnectionsTablePrefix: {
ID: WeaveConnectionsTablePrefix,
Label: "Connections",
Label: "Connections (old)",
Type: report.PropertyListType,
Prefix: WeaveConnectionsTablePrefix,
},
WeaveConnectionsMulticolumnTablePrefix: {
ID: WeaveConnectionsMulticolumnTablePrefix,
Label: "Connections (new)",
Type: report.MulticolumnTableType,
Prefix: WeaveConnectionsMulticolumnTablePrefix,
Columns: []report.Column{
report.Column{
ID: "ip",
Label: "IP",
ID: WeaveConnectionsConnection,
Label: "Connections",
},
report.Column{
ID: "state",
ID: WeaveConnectionsState,
Label: "State",
},
report.Column{
ID: "info",
ID: WeaveConnectionsInfo,
Label: "Info",
},
},
@@ -488,9 +490,9 @@ func getConnectionsTable(router weave.Router) []report.Row {
table = append(table, report.Row{
ID: conn.Address,
Entries: map[string]string{
"ip": fmt.Sprintf("%s %s", arrow, conn.Address),
"state": conn.State,
"info": conn.Info,
WeaveConnectionsConnection: fmt.Sprintf("%s %s", arrow, conn.Address),
WeaveConnectionsState: conn.State,
WeaveConnectionsInfo: conn.Info,
},
})
}