instance metrics

This commit is contained in:
Sérgio Ramos 2017-01-11 21:11:48 +00:00
parent 520b178281
commit 6f48ec1d23
10 changed files with 239 additions and 91 deletions

View File

@ -0,0 +1,56 @@
const React = require('react');
const Styled = require('styled-components');
const Column = require('@ui/components/column');
const MiniMetric = require('@ui/components/mini-metric');
const PropTypes = require('@root/prop-types');
const Row = require('@ui/components/row');
const {
default: styled
} = Styled;
const {
MiniMetricGraph,
MiniMetricMeta,
MiniMetricTitle,
MiniMetricSubtitle,
MiniMetricView
} = MiniMetric;
const StyledRow = styled(Row)`
margin: 0;
& > div {
padding-left: 0;
padding-right: 0;
}
`;
const MetricsRow = ({
datasets = []
}) => {
const _datasets = datasets.map((metric, i) => (
<Column key={i} xs={4}>
<MiniMetricView borderless>
<MiniMetricMeta>
<MiniMetricTitle>Memory: 54%</MiniMetricTitle>
<MiniMetricSubtitle>(1280/3000 MB)</MiniMetricSubtitle>
</MiniMetricMeta>
<MiniMetricGraph data={metric.data} />
</MiniMetricView>
</Column>
));
return (
<StyledRow>
{_datasets}
</StyledRow>
);
};
MetricsRow.propTypes = {
datasets: React.PropTypes.arrayOf(PropTypes.dataset)
};
module.exports = MetricsRow;

View File

@ -1,23 +1,16 @@
const forceArray = require('force-array');
const React = require('react');
const ReactRouter = require('react-router');
const Styled = require('styled-components');
const Anchor = require('@ui/components/anchor');
const Column = require('@ui/components/column');
const List = require('@ui/components/list');
const MiniMetric = require('@ui/components/mini-metric');
const MetricsRow = require('@components/metrics-row');
const PropTypes = require('@root/prop-types');
const Row = require('@ui/components/row');
const {
Link
} = ReactRouter;
const {
default: styled
} = Styled;
const {
ListItem,
ListItemView,
@ -31,23 +24,6 @@ const {
ListItemHeader
} = List;
const {
MiniMetricGraph,
MiniMetricMeta,
MiniMetricTitle,
MiniMetricSubtitle,
MiniMetricView
} = MiniMetric;
const MetricsRow = styled(Row)`
margin: 0;
& > div {
padding-left: 0;
padding-right: 0;
}
`;
const ServiceItem = ({
org = '',
project = '',
@ -99,18 +75,6 @@ const ServiceItem = ({
</ListItemHeader>
);
const metrics = service.metrics.map((metric, i) => (
<Column key={i} xs={4}>
<MiniMetricView borderless>
<MiniMetricMeta>
<MiniMetricTitle>Memory: 54%</MiniMetricTitle>
<MiniMetricSubtitle>(1280/3000 MB)</MiniMetricSubtitle>
</MiniMetricMeta>
<MiniMetricGraph data={metric.data} />
</MiniMetricView>
</Column>
));
const view = childs.length ? (
<ListItemGroupView>
{childs}
@ -123,9 +87,7 @@ const ServiceItem = ({
{description}
</ListItemMeta>
<ListItemOutlet>
<MetricsRow>
{metrics}
</MetricsRow>
<MetricsRow metrics={service.metrics} />
</ListItemOutlet>
</ListItemView>
);

View File

@ -1,11 +1,17 @@
const React = require('react');
const ReactRedux = require('react-redux');
const actions = require('@state/actions');
const EmptyInstances = require('@components/empty/instances');
const PropTypes = require('@root/prop-types');
const List = require('@ui/components/list');
const DatasetsRow = require('@components/metrics-row');
const selectors = require('@state/selectors');
const {
toggleInstanceCollapsed
} = actions;
const {
connect
} = ReactRedux;
@ -19,22 +25,29 @@ const {
ListItemView,
ListItemMeta,
ListItemTitle,
ListItemOptions
ListItemOptions,
ListItemOutlet
} = List;
const Instances = ({
instances = []
instances = [],
toggleCollapsed = () => null
}) => {
const onClick = (uuid) => () => toggleCollapsed(uuid);
const empty = instances.length ? null : (
<EmptyInstances />
);
const instanceList = instances.map((service) => (
<ListItem collapsed key={service.uuid}>
const instanceList = instances.map((instance) => (
<ListItem collapsed={!instance.collapsed} key={instance.uuid} >
<ListItemView>
<ListItemMeta>
<ListItemTitle>{service.name}</ListItemTitle>
<ListItemMeta onClick={onClick(instance.uuid)}>
<ListItemTitle>{instance.name}</ListItemTitle>
</ListItemMeta>
<ListItemOutlet>
<DatasetsRow metrics={instance.metrics} />
</ListItemOutlet>
</ListItemView>
<ListItemOptions>
@ -51,7 +64,8 @@ const Instances = ({
};
Instances.propTypes = {
instances: React.PropTypes.arrayOf(PropTypes.instance)
instances: React.PropTypes.arrayOf(PropTypes.instance),
toggleCollapsed: React.PropTypes.func
};
const mapStateToProps = (state, {
@ -60,6 +74,11 @@ const mapStateToProps = (state, {
instances: instancesByServiceIdSelector(params.serviceId)(state)
});
const mapDispatchToProps = (dispatch) => ({
toggleCollapsed: (uuid) => dispatch(toggleInstanceCollapsed(uuid))
});
module.exports = connect(
mapStateToProps
mapStateToProps,
mapDispatchToProps
)(Instances);

View File

@ -378,72 +378,130 @@
}]
},
"instances": {
"ui": {
"collapsed": []
},
"data": [{
"uuid": "309ecd9f-ac03-474b-aff7-4bd2e743296c",
"name": "wordpress_01",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "be227788-74f1-4e5b-a85f-b5c71cbae8d8",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
}, {
"uuid": "0db6db53-de6f-4378-839e-5d5b452fbaf2",
"name": "nfs_01",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "6a0eee76-c019-413b-9d5f-44712b55b993",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
}, {
"uuid": "250c8a6c-7d02-49a9-8abd-e1c22773041d",
"name": "consul",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "97c68055-db88-45c9-ad49-f26da4264777",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
}, {
"uuid": "2c921f3a-8bc3-4f57-9cd7-789ebae72061",
"name": "memcache_01",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
}, {
"uuid": "68d3046e-8e34-4f5d-a0e5-db3795a250fd",
"name": "memcache_02",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
}, {
"uuid": "2ea99763-3b44-4179-8393-d66d94961051",
"name": "memcache_03",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
}, {
"uuid": "25f6bc62-63b8-4959-908e-1f6d7ff6341d",
"name": "memcache_04",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
}, {
"uuid": "8be01042-0281-4a77-a357-25979e87bf3d",
"name": "memcache_05",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
}, {
"uuid": "3d652e9d-73e8-4a6f-8171-84fa83740662",
"name": "nginx",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "081a792c-47e0-4439-924b-2efa9788ae9e",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
}, {
"uuid": "c3ec7633-a02b-4615-86a0-9e6faeaae94b",
"name": "percona-primary",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "4ee4103e-1a52-4099-a48e-01588f597c70",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
}, {
"uuid": "c2b5fec2-31e2-41a7-b7fc-cd0bb1822e76",
"name": "percona-secundary",
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "4ee4103e-1a52-4099-a48e-01588f597c70",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401"
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
}]
}
}

View File

@ -39,6 +39,20 @@ const Metric = React.PropTypes.shape({
...BaseObject
});
const Dataset = React.PropTypes.shape({
uuid: React.PropTypes.string,
type: React.PropTypes.string,
data: React.PropTypes.arrayOf(
React.PropTypes.shape({
firstQuartile: React.PropTypes.string,
thirdQuartile: React.PropTypes.string,
median: React.PropTypes.string,
max: React.PropTypes.string,
min: React.PropTypes.string
})
)
});
const Sections = React.PropTypes.arrayOf(
React.PropTypes.string
);
@ -58,5 +72,6 @@ module.exports = {
instance: Instance,
metric: Metric,
// consinder renaming this to 'Types' as it could be used for any
metricTypes: MetricTypes
metricTypes: MetricTypes,
dataset: Dataset
};

View File

@ -11,5 +11,6 @@ module.exports = {
...require('@state/thunks'),
updateRouter: createAction(`${APP}/APP/UPDATE_ROUTER`),
toggleHeaderTooltip: createAction(`${APP}/APP/TOGGLE_HEADER_TOOLTIP`),
toggleServiceCollapsed: createAction(`${APP}/APP/TOGGLE_SERVICE_COLLAPSED`)
toggleServiceCollapsed: createAction(`${APP}/APP/TOGGLE_SERVICE_COLLAPSED`),
toggleInstanceCollapsed: createAction(`${APP}/APP/TOGGLE_INSTANCE_COLLAPSED`)
};

View File

@ -0,0 +1,25 @@
const toggleCollapsed = (state, action) => {
const {
ui
} = state;
const {
collapsed = []
} = ui;
const _collapsed = collapsed.indexOf(action.payload) >= 0
? collapsed.filter((uuid) => uuid !== action.payload)
: [...collapsed, action.payload];
return {
...state,
ui: {
...ui,
collapsed: _collapsed
}
};
};
module.exports = {
toggleCollapsed
};

View File

@ -1,9 +1,20 @@
const ReduxActions = require('redux-actions');
const actions = require('@state/actions');
const common = require('@state/reducers/common');
const {
handleActions
} = ReduxActions;
const {
toggleInstanceCollapsed
} = actions;
const {
toggleCollapsed
} = common;
module.exports = handleActions({
'x': (state) => state // somehow handleActions needs at least one reducer
[toggleInstanceCollapsed.toString()]: toggleCollapsed
}, {});

View File

@ -1,6 +1,7 @@
const ReduxActions = require('redux-actions');
const actions = require('@state/actions');
const common = require('@state/reducers/common');
const {
handleActions
@ -10,14 +11,10 @@ const {
toggleServiceCollapsed
} = actions;
const {
toggleCollapsed
} = common;
module.exports = handleActions({
[toggleServiceCollapsed.toString()]: (state, action) => ({
...state,
ui: {
...state.ui,
collapsed: state.ui.collapsed.indexOf(action.payload) >= 0
? state.ui.collapsed.filter((uuid) => uuid !== action.payload)
: [...state.ui.collapsed, action.payload]
}
})
[toggleServiceCollapsed.toString()]: toggleCollapsed
}, {});

View File

@ -16,6 +16,7 @@ const orgs = (state) => get(state, 'orgs.data', []);
const projects = (state) => get(state, 'projects.data', []);
const services = (state) => get(state, 'services.data', []);
const collapsedServices = (state) => get(state, 'services.ui.collapsed', []);
const collapsedInstances = (state) => get(state, 'instances.ui.collapsed', []);
const instances = (state) => get(state, 'instances.data', []);
const metricDatasets = (state) => get(state, 'metrics.data.datasets', []);
@ -46,6 +47,13 @@ const orgSections = (orgId) => createSelector(
)
);
const isCollapsed = (collapsed, uuid) => collapsed.indexOf(uuid) >= 0;
const datasets = (metrics, uuids) => uuids.map((uuid) => find(metrics, [
'uuid',
uuid
]));
const servicesByProjectId = (projectId) => createSelector(
[services, projectById(projectId), collapsedServices, metricDatasets],
(services, project, collapsed, metrics) =>
@ -55,31 +63,27 @@ const servicesByProjectId = (projectId) => createSelector(
services: services.filter((s) => s.parent === service.uuid)
}))
.filter((s) => !s.parent)
.map((service) => {
const isCollapsed = (uuid) => collapsed.indexOf(uuid) >= 0;
const datasets = (uuids) => uuids.map((uuid) => find(metrics, [
'uuid',
uuid
]));
return {
.map((service) => ({
...service,
metrics: datasets(metrics, service.metrics),
collapsed: isCollapsed(collapsed, service.uuid),
services: service.services.map((service) => ({
...service,
metrics: datasets(service.metrics),
collapsed: isCollapsed(service.uuid),
services: service.services.map((service) => ({
...service,
metrics: datasets(service.metrics),
collapsed: isCollapsed(service.uuid)
}))
};
})
metrics: datasets(metrics, service.metrics),
collapsed: isCollapsed(collapsed, service.uuid)
}))
}))
);
const instancesByServiceId = (serviceId) => createSelector(
[instances, serviceById(serviceId)],
(instances, service) =>
[instances, serviceById(serviceId), collapsedInstances, metricDatasets],
(instances, service, collapsed, metrics) =>
instances.filter((i) => i.service === service.uuid)
.map((instance) => ({
...instance,
metrics: datasets(metrics, instance.metrics),
collapsed: isCollapsed(collapsed, instance.uuid)
}))
);