Restructure redux state enable duration changing for metrics

This commit is contained in:
JUDIT GRESKOVITS 2017-01-24 17:37:08 +00:00
parent 390adddd30
commit 636a478f40
12 changed files with 671 additions and 222 deletions

View File

@ -28,15 +28,15 @@
"metric": {
"settings-label": "Settings"
},
"cpu_agg_usage": {
"cpu-agg-usage": {
"title": "Aggregated CPU usage",
"description": "CPU usages accross all of the CPU cores."
},
"cpu_wait_time": {
"cpu-wait-time": {
"title": "Memory resident set size",
"description": "Process memory that is actually stored in the RAM."
},
"zfs_used": {
"zfs-used": {
"title": "Apache HTTP requests",
"description": "Number of website requests to apache if it is used."
}

View File

@ -16,13 +16,13 @@ const {
} = AddMetric;
const AddMetrics = ({
datasets,
metricTypes,
metrics,
onAddMetric
}) => {
const added = (metric) =>
Boolean(metrics.filter((m) => m.id === metric).length);
Boolean(datasets.filter((dataset) => dataset.type.id === metric).length);
const addButton = (metric) => (
<AddMetricButton metric={metric} onClick={onAddMetric}>
<FormattedMessage id={'metrics.add.add-label'} onClick={onAddMetric} />
@ -35,17 +35,17 @@ const AddMetrics = ({
);
const metricList = metricTypes.map((metric) => (
<AddMetricTile key={metric}>
<AddMetricTile key={metric.id}>
<AddMetricTitle>
<FormattedMessage id={`metrics.${metric}.title`} />
<FormattedMessage id={`metrics.${metric.id}.title`} />
</AddMetricTitle>
<AddMetricDescription>
<FormattedMessage id={`metrics.${metric}.description`} />
<FormattedMessage id={`metrics.${metric.id}.description`} />
</AddMetricDescription>
<AddMetricLink href='http://somelink.com'>
<FormattedMessage id={'metrics.add.link-label'} />
</AddMetricLink>
{ added(metric) ? addedButton : addButton(metric) }
{ added(metric.id) ? addedButton : addButton(metric.id) }
</AddMetricTile>
));
@ -57,8 +57,8 @@ const AddMetrics = ({
};
AddMetrics.propTypes = {
metricTypes: React.PropTypes.arrayOf(React.PropTypes.string),
metrics: React.PropTypes.arrayOf(PropTypes.metric).isRequired,
datasets: React.PropTypes.arrayOf(PropTypes.dataset),
metricTypes: React.PropTypes.arrayOf(PropTypes.metric),
onAddMetric: React.PropTypes.func.isRequired,
};

View File

@ -20,13 +20,16 @@ const {
const Metrics = ({
addMetric,
metrics,
datasets,
metricTypes,
metricTypeUuid = '',
metricDurationChange,
service,
toggleMonitorView = () => null
}) => {
const onMonitorsClick = (ev) => toggleMonitorView(metricTypeUuid);
const onRemoveMetric = (ev) => {};
return (
<div>
@ -37,12 +40,14 @@ const Metrics = ({
</Row>
<Monitors />
<MetricCharts
datasets={metrics.datasets}
onSettingsClick={toggleMonitorView}
datasets={datasets}
onDurationChange={metricDurationChange}
onRemoveMetric={onRemoveMetric}
onSettingsClick={onMonitorsClick}
/>
<AddMetrics
datasets={datasets}
metricTypes={metricTypes}
metrics={metrics.types}
onAddMetric={addMetric}
/>
</div>
@ -51,9 +56,10 @@ const Metrics = ({
Metrics.propTypes = {
addMetric: React.PropTypes.func.isRequired,
datasets: React.PropTypes.arrayOf(PropTypes.dataset),
metricDurationChange: React.PropTypes.func.isRequired,
metricTypeUuid: React.PropTypes.string,
metricTypes: React.PropTypes.arrayOf(React.PropTypes.string),
metrics: React.PropTypes.arrayOf(PropTypes.metric),
metricTypes: React.PropTypes.arrayOf(PropTypes.metric),
service: PropTypes.service,
toggleMonitorView: React.PropTypes.func.isRequired
};

View File

@ -20,34 +20,41 @@ const {
const MetricCharts = ({
datasets,
duration = 360,
durations = [
360,
720,
1440,
2880
],
onDurationChange = () => {},
onSettingsClick = () => {},
onDurationChange,
onSettingsClick,
onRemoveMetric = () => {}
}) => {
const optionList = durations.map(duration => (
<option key={duration} value={duration}>
<option key={String(duration)} value={duration}>
{moment.duration(duration, 'minutes').humanize()}
</option>
));
const metricList = datasets.map((dataset) => {
// TODO
// - yMeasurement '%' or not
// - yMin & yMax should all come from the metric type description
const metricList = datasets.map((dataset, index) => {
const {
data,
duration=durations[0],
type
} = dataset;
const onSelectChange = (evt) =>
onDurationChange(Number(evt.target.value), dataset.uuid);
return (
<MetricView key={dataset.uuid + Math.random()}>
<MetricView key={type.id}>
<MetricHeader>
<MetricTitle>{dataset.uuid}</MetricTitle>
<MetricSelect onChange={onDurationChange} value={durations[0]}>
<MetricTitle>
<FormattedMessage id={`metrics.${type.id}.title`} />
</MetricTitle>
<MetricSelect onChange={onSelectChange} value={String(duration)}>
{optionList}
</MetricSelect>
<MetricSettingsButton onClick={onSettingsClick}>
@ -56,11 +63,11 @@ const MetricCharts = ({
<MetricCloseButton onClick={onRemoveMetric} />
</MetricHeader>
<MetricGraph
data={dataset.data}
data={data}
duration={duration}
yMax={100}
yMeasurement='%'
yMin={0}
yMax={type.max}
yMeasurement={type.measurement}
yMin={type.min}
/>
</MetricView>
);
@ -74,8 +81,7 @@ const MetricCharts = ({
};
MetricCharts.propTypes = {
datasets: React.PropTypes.arrayOf(PropTypes.Dataset),
duration: React.PropTypes.number,
datasets: React.PropTypes.arrayOf(PropTypes.dataset),
durations: React.PropTypes.arrayOf(React.PropTypes.number),
onDurationChange: React.PropTypes.func.isRequired,
onRemoveMetric: React.PropTypes.func.isRequired,

View File

@ -10,33 +10,41 @@ const {
const {
metricsByServiceIdSelector,
metricTypesSelector,
serviceByIdSelector
} = selectors;
const {
addMetric
addMetric,
metricDurationChange
} = actions;
const mapStateToProps = (state, {
params = {}
}) => ({
metrics: metricsByServiceIdSelector(params.serviceId)(state),
metricTypes: state.metrics.ui.types,
datasets: metricsByServiceIdSelector(params.serviceId)(state),
metricTypes: metricTypesSelector(state),
service: serviceByIdSelector(params.serviceId)(state)
});
const mapDispatchToProps = (dispatch) => ({
addMetric: (service) => (metric) => dispatch(addMetric({
id: metric,
metric: metric,
service: service.uuid
}))
})),
metricDurationChange: (service) =>
(duration, dataset) => dispatch(metricDurationChange({
duration,
dataset
}))
});
const mergeProps = (stateProps, dispatchProps, ownProps) => ({
...stateProps,
...dispatchProps,
...ownProps,
addMetric: dispatchProps.addMetric(stateProps.service)
addMetric: dispatchProps.addMetric(stateProps.service),
metricDurationChange: dispatchProps.metricDurationChange(stateProps.service)
});
module.exports = connect(

View File

@ -53,44 +53,112 @@
},
"metrics": {
"ui": {
"types": [
"cpu_agg_usage",
"cpu_wait_time",
"zfs_used",
"zfs_available",
"load_average",
"mem_agg_usage",
"mem_limit",
"mem_swap",
"mem_swap_limit",
"net_agg_packets_in",
"net_agg_packets_out",
"net_agg_bytes_in",
"net_agg_bytes_out",
"time_of_day"
],
"durations": [
360,
720,
1440,
2880
"360",
"720",
"1440",
"2880"
]
},
"data": {
"types": [{
"uuid": "dca08514-72e5-46ce-ad91-e68b3b0914d4",
"name": "Aggregated CPU usage",
"id": "agg-cpu-usage"
}, {
"uuid": "9e77b50e-42d7-425d-8daf-c0e98e2bdd6a",
"id": "mem-res-set-size"
}, {
"uuid": "347dbdc7-15e3-4e12-8dfb-865d38526e14",
"id": "apache-http-reqs"
}, {
"uuid": "2aaa237d-42b3-442f-9094-a17aa470014b",
"name": "Memory",
"id": "memory"
"name": "Aggregated CPU usage",
"id": "cpu-agg-usage",
"min": 0,
"max": 100,
"measurement": "%"
}, {
"uuid": "dca08514-72e5-46ce-ad91-e68b3b0914d6",
"name": "Aggregated CPU usage",
"id": "cpu-wait-time",
"min": 0,
"max": 100,
"measurement": "%"
}, {
"uuid": "dca08514-72e5-46ce-ad91-e68b3b0914d7",
"name": "Aggregated CPU usage",
"id": "zfs-used",
"min": 1,
"max": 2,
"measurement": "kb"
}, {
"uuid": "dca08514-72e5-46ce-ad91-e68b3b0914d8",
"name": "Aggregated CPU usage",
"id": "zfs-available",
"min": 0,
"max": 100,
"measurement": "%"
}, {
"uuid": "dca08514-72e5-46ce-ad91-e68b3b0914d9",
"name": "Aggregated CPU usage",
"id": "load-average",
"min": 0,
"max": 20,
"measurement": "kb"
}, {
"uuid": "dca08514-72e5-46ce-ad92-e68b3b0914d4",
"name": "Aggregated CPU usage",
"id": "mem-agg-usage",
"min": 0,
"max": 100,
"measurement": "%"
}, {
"uuid": "dca08514-72e5-46ce-ad93-e68b3b0914d4",
"name": "Aggregated CPU usage",
"id": "mem-limit",
"min": 0,
"max": 100,
"measurement": "%"
}, {
"uuid": "dca08514-72e5-46ce-ad94-e68b3b0914d4",
"name": "Aggregated CPU usage",
"id": "mem-swap",
"min": 0,
"max": 100,
"measurement": "%"
}, {
"uuid": "dca08514-72e5-46ce-ad95-e68b3b0914d4",
"name": "Aggregated CPU usage",
"id": "mem-swap-limit",
"min": 0,
"max": 100,
"measurement": "%"
}, {
"uuid": "dca08514-72e5-46ce-ad96-e68b3b0914d4",
"name": "Aggregated CPU usage",
"id": "net-agg-packets-in",
"min": 0,
"max": 100,
"measurement": "%"
}, {
"uuid": "dca08514-72e5-46ce-ad97-e68b3b0914d4",
"name": "Aggregated CPU usage",
"id": "net-agg-packets-out",
"min": 0,
"max": 100,
"measurement": "%"
}, {
"uuid": "dca08514-72e5-47ce-ad91-e68b3b0914d4",
"name": "Aggregated CPU usage",
"id": "net-agg-bytes-in",
"min": 0,
"max": 100,
"measurement": "%"
}, {
"uuid": "dca08514-72e5-48ce-ad91-e68b3b0914d4",
"name": "Aggregated CPU usage",
"id": "net-agg-bytes-out",
"min": 0,
"max": 100,
"measurement": "%"
}, {
"uuid": "dca08514-72e5-49ce-ad91-e68b3b0914d4",
"name": "Aggregated CPU usage",
"id": "time-of-day",
"min": 0,
"max": 100,
"measurement": "%"
}],
"datasets": [{
"type": "2aaa237d-42b3-442f-9094-a17aa470014b",
@ -197,7 +265,215 @@
"median": 30,
"max": 49,
"min": 30
}, {
"firstQuartile": 15,
"thirdQuartile": 15,
"median": 15,
"max": 15,
"min": 15
}, {
"firstQuartile": 26,
"thirdQuartile": 26,
"median": 26,
"max": 26,
"min": 26
}, {
"firstQuartile": 17,
"thirdQuartile": 17,
"median": 17,
"max": 17,
"min": 17
}, {
"firstQuartile": 15,
"thirdQuartile": 25,
"median": 19,
"max": 19,
"min": 20
}, {
"firstQuartile": 19,
"thirdQuartile": 25,
"median": 21,
"max": 20,
"min": 25
}, {
"firstQuartile": 24,
"thirdQuartile": 30,
"median": 25,
"max": 26,
"min": 10
}, {
"firstQuartile": 28,
"thirdQuartile": 34,
"median": 30,
"max": 30,
"min": 30
}, {
"firstQuartile": 30,
"thirdQuartile": 45,
"median": 35,
"max": 40,
"min": 40
}, {
"firstQuartile": 20,
"thirdQuartile": 55,
"median": 45,
"max": 44,
"min": 44
}, {
"firstQuartile": 55,
"thirdQuartile": 55,
"median": 55,
"max": 55,
"min": 55
}, {
"firstQuartile": 57,
"thirdQuartile": 56,
"median": 57,
"max": 58,
"min": 57
}, {
"firstQuartile": 57,
"thirdQuartile": 56,
"median": 56,
"max": 56,
"min": 56
}, {
"firstQuartile": 60,
"thirdQuartile": 56,
"median": 60,
"max": 60,
"min": 60
}, {
"firstQuartile": 57,
"thirdQuartile": 57,
"median": 57,
"max": 57,
"min": 57
}, {
"firstQuartile": 57,
"thirdQuartile": 55,
"median": 55,
"max": 55,
"min": 55
}, {
"firstQuartile": 20,
"thirdQuartile": 45,
"median": 45,
"max": 45,
"min": 45
}, {
"firstQuartile": 15,
"thirdQuartile": 40,
"median": 30,
"max": 49,
"min": 30
}, {
"firstQuartile": 15,
"thirdQuartile": 15,
"median": 15,
"max": 15,
"min": 15
}, {
"firstQuartile": 26,
"thirdQuartile": 26,
"median": 26,
"max": 26,
"min": 26
}, {
"firstQuartile": 17,
"thirdQuartile": 17,
"median": 17,
"max": 17,
"min": 17
}, {
"firstQuartile": 15,
"thirdQuartile": 25,
"median": 19,
"max": 19,
"min": 20
}, {
"firstQuartile": 19,
"thirdQuartile": 25,
"median": 21,
"max": 20,
"min": 25
}, {
"firstQuartile": 24,
"thirdQuartile": 30,
"median": 25,
"max": 26,
"min": 27
}, {
"firstQuartile": 28,
"thirdQuartile": 34,
"median": 30,
"max": 30,
"min": 30
}, {
"firstQuartile": 30,
"thirdQuartile": 45,
"median": 35,
"max": 40,
"min": 40
}, {
"firstQuartile": 20,
"thirdQuartile": 55,
"median": 45,
"max": 44,
"min": 44
}, {
"firstQuartile": 55,
"thirdQuartile": 55,
"median": 55,
"max": 55,
"min": 55
}, {
"firstQuartile": 57,
"thirdQuartile": 56,
"median": 57,
"max": 58,
"min": 57
}, {
"firstQuartile": 57,
"thirdQuartile": 56,
"median": 56,
"max": 56,
"min": 56
}, {
"firstQuartile": 60,
"thirdQuartile": 56,
"median": 60,
"max": 60,
"min": 60
}, {
"firstQuartile": 57,
"thirdQuartile": 57,
"median": 57,
"max": 57,
"min": 57
}, {
"firstQuartile": 57,
"thirdQuartile": 55,
"median": 55,
"max": 55,
"min": 55
}, {
"firstQuartile": 20,
"thirdQuartile": 45,
"median": 45,
"max": 45,
"min": 45
}, {
"firstQuartile": 15,
"thirdQuartile": 40,
"median": 30,
"max": 49,
"min": 30
}]
},{
"type": "dca08514-72e5-46ce-ad91-e68b3b0914d9",
"uuid": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed",
"data": [{"firstQuartile":1.62,"thirdQuartile":1.62,"median":1.62,"max":1.62,"min":1.62},{"firstQuartile":1.67,"thirdQuartile":1.67,"median":1.67,"max":1.67,"min":1.67},{"firstQuartile":1.63,"thirdQuartile":1.63,"median":1.63,"max":1.63,"min":1.63},{"firstQuartile":1.62,"thirdQuartile":1.66,"median":1.64,"max":1.64,"min":1.64},{"firstQuartile":1.64,"thirdQuartile":1.66,"median":1.64,"max":1.64,"min":1.66},{"firstQuartile":1.66,"thirdQuartile":1.69,"median":1.66,"max":1.67,"min":1.67},{"firstQuartile":1.68,"thirdQuartile":1.70,"median":1.69,"max":1.69,"min":1.69},{"firstQuartile":1.69,"thirdQuartile":1.75,"median":1.71,"max":1.73,"min":1.73},{"firstQuartile":1.64,"thirdQuartile":1.80,"median":1.75,"max":1.75,"min":1.75},{"firstQuartile":1.80,"thirdQuartile":1.80,"median":1.80,"max":1.80,"min":1.80},{"firstQuartile":1.81,"thirdQuartile":1.80,"median":1.81,"max":1.81,"min":1.81},{"firstQuartile":1.81,"thirdQuartile":1.80,"median":1.80,"max":1.80,"min":1.80},{"firstQuartile":1.82,"thirdQuartile":1.80,"median":1.82,"max":1.82,"min":1.82},{"firstQuartile":1.81,"thirdQuartile":1.81,"median":1.81,"max":1.81,"min":1.81},{"firstQuartile":1.81,"thirdQuartile":1.80,"median":1.80,"max":1.80,"min":1.80},{"firstQuartile":1.64,"thirdQuartile":1.75,"median":1.75,"max":1.75,"min":1.75},{"firstQuartile":1.62,"thirdQuartile":1.73,"median":1.69,"max":1.77,"min":1.69},{"firstQuartile":1.62,"thirdQuartile":1.62,"median":1.62,"max":1.62,"min":1.62},{"firstQuartile":1.67,"thirdQuartile":1.67,"median":1.67,"max":1.67,"min":1.67},{"firstQuartile":1.63,"thirdQuartile":1.63,"median":1.63,"max":1.63,"min":1.63},{"firstQuartile":1.62,"thirdQuartile":1.66,"median":1.64,"max":1.64,"min":1.64},{"firstQuartile":1.64,"thirdQuartile":1.66,"median":1.64,"max":1.64,"min":1.66},{"firstQuartile":1.66,"thirdQuartile":1.69,"median":1.66,"max":1.67,"min":1.59},{"firstQuartile":1.68,"thirdQuartile":1.70,"median":1.69,"max":1.69,"min":1.69},{"firstQuartile":1.69,"thirdQuartile":1.75,"median":1.71,"max":1.73,"min":1.73},{"firstQuartile":1.64,"thirdQuartile":1.80,"median":1.75,"max":1.75,"min":1.75},{"firstQuartile":1.80,"thirdQuartile":1.80,"median":1.80,"max":1.80,"min":1.80},{"firstQuartile":1.81,"thirdQuartile":1.80,"median":1.81,"max":1.81,"min":1.81},{"firstQuartile":1.81,"thirdQuartile":1.80,"median":1.80,"max":1.80,"min":1.80},{"firstQuartile":1.82,"thirdQuartile":1.80,"median":1.82,"max":1.82,"min":1.82},{"firstQuartile":1.81,"thirdQuartile":1.81,"median":1.81,"max":1.81,"min":1.81},{"firstQuartile":1.81,"thirdQuartile":1.80,"median":1.80,"max":1.80,"min":1.80},{"firstQuartile":1.64,"thirdQuartile":1.75,"median":1.75,"max":1.75,"min":1.75},{"firstQuartile":1.62,"thirdQuartile":1.73,"median":1.69,"max":1.77,"min":1.69},{"firstQuartile":1.62,"thirdQuartile":1.62,"median":1.62,"max":1.62,"min":1.62},{"firstQuartile":1.67,"thirdQuartile":1.67,"median":1.67,"max":1.67,"min":1.67},{"firstQuartile":1.63,"thirdQuartile":1.63,"median":1.63,"max":1.63,"min":1.63},{"firstQuartile":1.62,"thirdQuartile":1.66,"median":1.64,"max":1.64,"min":1.64},{"firstQuartile":1.64,"thirdQuartile":1.66,"median":1.64,"max":1.64,"min":1.66},{"firstQuartile":1.66,"thirdQuartile":1.69,"median":1.66,"max":1.67,"min":1.67},{"firstQuartile":1.68,"thirdQuartile":1.70,"median":1.69,"max":1.69,"min":1.69},{"firstQuartile":1.69,"thirdQuartile":1.75,"median":1.71,"max":1.73,"min":1.73},{"firstQuartile":1.64,"thirdQuartile":1.80,"median":1.75,"max":1.75,"min":1.75},{"firstQuartile":1.80,"thirdQuartile":1.80,"median":1.80,"max":1.80,"min":1.80},{"firstQuartile":1.81,"thirdQuartile":1.80,"median":1.81,"max":1.81,"min":1.81},{"firstQuartile":1.81,"thirdQuartile":1.80,"median":1.80,"max":1.80,"min":1.80},{"firstQuartile":1.82,"thirdQuartile":1.80,"median":1.82,"max":1.82,"min":1.82},{"firstQuartile":1.81,"thirdQuartile":1.81,"median":1.81,"max":1.81,"min":1.81},{"firstQuartile":1.81,"thirdQuartile":1.80,"median":1.80,"max":1.80,"min":1.80},{"firstQuartile":1.64,"thirdQuartile":1.75,"median":1.75,"max":1.75,"min":1.75},{"firstQuartile":1.62,"thirdQuartile":1.73,"median":1.69,"max":1.77,"min":1.69}]
}]
}
},
@ -320,55 +596,80 @@
"name": "Nginx",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"instances": 1,
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}]
}, {
"uuid": "be227788-74f1-4e5b-a85f-b5c71cbae8d8",
"id": "wordpress",
"name": "Wordpress",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"instances": 1,
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}]
}, {
"uuid": "6a0eee76-c019-413b-9d5f-44712b55b993",
"id": "nfs",
"name": "NFS",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"instances": 1,
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}]
}, {
"uuid": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
"id": "memcached",
"name": "Memcached",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"instances": 5,
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}]
}, {
"uuid": "4ee4103e-1a52-4099-a48e-01588f597c70",
"id": "percona",
"name": "Percona",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"instances": 5,
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}]
}, {
"uuid": "9572d367-c4ae-4fb1-8ad5-f5e3830e7034",
"id": "primary",
@ -376,11 +677,16 @@
"parent": "4ee4103e-1a52-4099-a48e-01588f597c70",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"instances": 1,
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}]
}, {
"uuid": "c8411ef0-ab39-42cb-a704-d20b170eff31",
"id": "secondaries",
@ -388,22 +694,32 @@
"parent": "4ee4103e-1a52-4099-a48e-01588f597c70",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"instances": 4,
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}]
}, {
"uuid": "97c68055-db88-45c9-ad49-f26da4264777",
"id": "consul",
"name": "Consul",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"instances": 1,
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}]
}]
},
"instances": {
@ -416,121 +732,176 @@
"datacenter": "f018da03-41c8-4619-a36a-ab8b706160cb",
"service": "be227788-74f1-4e5b-a85f-b5c71cbae8d8",
"project": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "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",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "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",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "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",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "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",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "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",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "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",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "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",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "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",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "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",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "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",
"metrics": [
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec",
"3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
]
"metrics": [{
"type": "cpu-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}, {
"type": "zfs-used",
"dataset": "4e6ee79a-7453-4fc6-b9da-7ae1e41138ed"
}, {
"type": "mem-agg-usage",
"dataset": "3e6ee79a-7453-4fc6-b9da-7ae1e41138ec"
}]
}]
}
}

View File

@ -36,7 +36,10 @@ const Instance = React.PropTypes.shape({
});
const Metric = React.PropTypes.shape({
...BaseObject
...BaseObject,
min: React.PropTypes.number,
max: React.PropTypes.number,
measurement: React.PropTypes.string
});
const MetricType = React.PropTypes.shape({
@ -45,7 +48,7 @@ const MetricType = React.PropTypes.shape({
const Dataset = React.PropTypes.shape({
uuid: React.PropTypes.string,
type: React.PropTypes.string,
type: MetricType,
data: React.PropTypes.arrayOf(
React.PropTypes.shape({
firstQuartile: React.PropTypes.number,

View File

@ -13,6 +13,7 @@ module.exports = {
toggleHeaderTooltip: createAction(`${APP}/TOGGLE_HEADER_TOOLTIP`),
toggleServiceCollapsed: createAction(`${APP}/TOGGLE_SERVICE_COLLAPSED`),
addMetric: createAction(`${APP}/ADD_METRIC`),
metricDurationChange: createAction(`${APP}/METRIC_DURATION_CHANGE`),
toggleInstanceCollapsed: createAction(`${APP}/TOGGLE_INSTANCE_COLLAPSED`),
toggleMonitorView: createAction(`${APP}/TOGGLE_MONITOR_VIEW`),
switchMonitorViewPage: createAction(`${APP}/SWITCH_MONITOR_VIEW_PAGE`),

View File

@ -7,23 +7,19 @@ const {
} = ReduxActions;
const {
addMetric
metricDurationChange
} = actions;
// This will need to be handled by an async action
// to update on the server too
module.exports = handleActions({
[addMetric.toString()]: (state, action) => {
[metricDurationChange.toString()]: (state, action) => {
return ({
...state,
data: {
types: [
...state.data.types,
action.payload
],
datasets: [
...state.data.datasets
]
ui: {
...state.ui,
[action.payload.dataset]: {
...state.ui[action.payload.dataset],
duration: action.payload.duration
}
}
});
}

View File

@ -8,6 +8,7 @@ const {
} = ReduxActions;
const {
addMetric,
toggleServiceCollapsed
} = actions;
@ -15,6 +16,45 @@ const {
toggleCollapsed
} = common;
const getMetrics = (stateMetrics, addMetric, metric) => {
const metrics = stateMetrics.map((m) => {
return ({
...m
});
});
if(addMetric) {
metrics.push({
type: metric
});
}
return metrics;
};
const getServices = (stateServices, service, metric) => {
return stateServices.map((s) => {
return ({
...s,
metrics: getMetrics(
s.metrics,
s.uuid === service,
metric
)
});
});
};
module.exports = handleActions({
[toggleServiceCollapsed.toString()]: toggleCollapsed
[toggleServiceCollapsed.toString()]: toggleCollapsed,
// This will need to be handled by an async action
// to update on the server too
[addMetric.toString()]: (state, action) => {
return ({
...state,
data: getServices(
state.data,
action.payload.service,
action.payload.metric
)
});
}
}, {});

View File

@ -20,8 +20,10 @@ const collapsedServices = (state) => get(state, 'services.ui.collapsed', []);
const collapsedInstances = (state) => get(state, 'instances.ui.collapsed', []);
const instances = (state) => get(state, 'instances.data', []);
const members = (state) => get(state, 'members.data', []);
const metricsUI = (state) => get(state, 'metrics.ui', {});
const metricTypes = (state) => get(state, 'metrics.data.types', []);
const metricDatasets = (state) => get(state, 'metrics.data.datasets', []);
// const metricDatasets = (state) => get(state, 'metrics.data.datasets', []);
const metricsData = (state) => get(state, 'metrics.data', {});
const projectById = (projectId) => createSelector(
projects,
@ -52,14 +54,16 @@ const orgSections = (orgId) => createSelector(
const isCollapsed = (collapsed, uuid) => collapsed.indexOf(uuid) >= 0;
const datasets = (metrics, uuids) => uuids.map((uuid) => find(metrics, [
'uuid',
uuid
]));
const datasets = (metricsData, serviceOrInstanceMetrics, metricsUI) =>
serviceOrInstanceMetrics.map((soim) => ({
...find(metricsData.datasets, [ 'uuid', soim.dataset ]),
type: find(metricsData.types, [ 'id', soim.type ]),
...metricsUI[soim.dataset]
}));
const servicesByProjectId = (projectId) => createSelector(
[services, projectById(projectId), collapsedServices, metricDatasets],
(services, project, collapsed, metrics) =>
[services, projectById(projectId), collapsedServices, metricsData, metricsUI],
(services, project, collapsed, metrics, metricsUI) =>
services.filter((s) => s.project === project.uuid)
.map((service) => ({
...service,
@ -68,33 +72,36 @@ const servicesByProjectId = (projectId) => createSelector(
.filter((s) => !s.parent)
.map((service) => ({
...service,
metrics: datasets(metrics, service.metrics),
metrics: datasets(metrics, service.metrics, metricsUI),
collapsed: isCollapsed(collapsed, service.uuid),
services: service.services.map((service) => ({
...service,
metrics: datasets(metrics, service.metrics),
metrics: datasets(metrics, service.metrics, metricsUI),
collapsed: isCollapsed(collapsed, service.uuid)
}))
}))
);
const instancesByServiceId = (serviceId) => createSelector(
[instances, serviceById(serviceId), collapsedInstances, metricDatasets],
(instances, service, collapsed, metrics) =>
[
instances,
serviceById(serviceId),
collapsedInstances,
metricsData,
metricsUI
],
(instances, service, collapsed, metrics, metricsUI) =>
instances.filter((i) => i.service === service.uuid)
.map((instance) => ({
...instance,
metrics: datasets(metrics, instance.metrics),
metrics: datasets(metrics, instance.metrics, metricsUI),
collapsed: isCollapsed(collapsed, instance.uuid)
}))
);
const metricsByServiceId = (serviceId) => createSelector(
[metricTypes, serviceById(serviceId), metricDatasets],
(metricTypes, service, metrics) => ({
types: metricTypes.filter((i) => i.service === service.uuid),
datasets: datasets(metrics, service.metrics)
})
[serviceById(serviceId), metricsData, metricsUI],
(service, metrics, metricsUI) => datasets(metrics, service.metrics, metricsUI)
);
const metricTypeByUuid = (metricTypeUuid) => createSelector(
@ -104,7 +111,7 @@ const metricTypeByUuid = (metricTypeUuid) => createSelector(
const instancesByProjectId = (projectId) => createSelector(
[instances, projectById(projectId), collapsedInstances, metricDatasets],
[instances, projectById(projectId), collapsedInstances, metricsData],
(instances, project, collapsed, metrics) =>
instances.filter((i) => i.project === project.uuid)
.map((instance) => ({
@ -143,6 +150,7 @@ module.exports = {
servicesByProjectIdSelector: servicesByProjectId,
instancesByServiceIdSelector: instancesByServiceId,
metricsByServiceIdSelector: metricsByServiceId,
metricTypesSelector: metricTypes,
instancesByProjectIdSelector: instancesByProjectId,
metricTypeByUuidSelector: metricTypeByUuid,
peopleByOrgIdSelector: peopleByOrgId

View File

@ -59,9 +59,9 @@ class Graph extends React.Component {
unitStepSize: xUnitStepSize,
max: xMax,
min: xMin,
/*displayFormats: {
displayFormats: {
hour: 'MMM D, hA'
}*/
}
},
}],
yAxes: [{
@ -70,7 +70,9 @@ class Graph extends React.Component {
min: yMin,
max: yMax,
callback: (value, index, values) => {
return `${value.toFixed(2)}${yMeasurement}`;
return value < 10 && value !== 0 ?
`${value.toFixed(2)}${yMeasurement}` :
`${value}${yMeasurement}`;
}
}
}]
@ -100,11 +102,15 @@ class Graph extends React.Component {
this._chart.update(0);
}
shouldComponentUpdate(nextProps, nextState) {
return false;
}
processData(props) {
const {
data = [],
duration = 360
} = this.props;
} = props;
// I'm going to assume that data will be structured in 10min intervals...
// And that newest data will be at the end...
// Let's rock and roll!
@ -113,21 +119,24 @@ class Graph extends React.Component {
// first time on scale x
const before = moment().subtract(duration, 'minutes');
// remove leading data before first time on scale x
const totalData = data.slice(data.length - 1 - duration/10);
// adjust time of first data, if there's less data than would fill the chart
const start = moment(before)
.add(duration - (totalData.length-1)*10, 'minutes');
// add times to data
const dataWithTime = totalData.map((d, i) => {
const add = i*10;
return Object.assign(
{},
d,
{
x: moment(start).add(add, 'minutes').toDate()
}
);
});
let dataWithTime = [];
if(data && data.length) {
const totalData = data.slice(data.length - 1 - duration/10);
// adjust time of first data, if there's less data than would fill the chart
const start = moment(before)
.add(duration - (totalData.length-1)*10, 'minutes');
// add times to data
dataWithTime = totalData.map((d, i) => {
const add = i*10;
return Object.assign(
{},
d,
{
x: moment(start).add(add, 'minutes').toDate()
}
);
});
}
// set min and max
const xMax = now.toDate();
@ -152,6 +161,7 @@ class Graph extends React.Component {
}
render() {
return (
<Container name='metric-body'>
<Canvas
@ -165,8 +175,8 @@ class Graph extends React.Component {
}
Graph.propTypes = {
data: React.PropTypes.array,
duration: React.PropTypes.number,
data: React.PropTypes.array, // eslint-disable-line react/no-unused-prop-types
duration: React.PropTypes.number, // eslint-disable-line react/no-unused-prop-types
yMax: React.PropTypes.number,
yMeasurement: React.PropTypes.string,
yMin: React.PropTypes.number