From fca49cde9dc53b9158f2340a7bcc9c10fbf84412 Mon Sep 17 00:00:00 2001 From: Ryan Morgan Date: Tue, 21 Jul 2009 15:12:44 -0700 Subject: [PATCH] [HHQ-3216] Split out metric data APIs into MetricDataApi. --- ChangeLog | 8 + hqu/hqapi1/app/MetricController.groovy | 12 + hqu/hqapi1/app/MetricdataController.groovy | 222 ++++++++++++++++++ src/org/hyperic/hq/hqapi1/HQApi.java | 12 + src/org/hyperic/hq/hqapi1/MetricApi.java | 3 + src/org/hyperic/hq/hqapi1/MetricDataApi.java | 131 +++++++++++ .../hqapi1/test/MetricDataAddData_test.java | 46 ++++ .../test/MetricDataGetLastMulti_test.java | 100 ++++++++ .../hqapi1/test/MetricDataGetLast_test.java | 67 ++++++ .../hqapi1/test/MetricDataGetMulti_test.java | 89 +++++++ .../hq/hqapi1/test/MetricDataGet_test.java | 66 ++++++ .../hq/hqapi1/test/MetricDataTestBase.java | 40 ++++ .../hq/hqapi1/test/MetricData_test.java | 3 + .../hq/hqapi1/test/WADLMetricData_test.java | 70 ++++++ .../hq/hqapi1/tools/MetricDataCommand.java | 78 +++++- xsd/HQApi1.wadl | 123 ++++++++++ xsd/HQApi1.xsd | 45 ++++ 17 files changed, 1106 insertions(+), 9 deletions(-) create mode 100644 hqu/hqapi1/app/MetricdataController.groovy create mode 100644 src/org/hyperic/hq/hqapi1/MetricDataApi.java create mode 100644 src/org/hyperic/hq/hqapi1/test/MetricDataAddData_test.java create mode 100644 src/org/hyperic/hq/hqapi1/test/MetricDataGetLastMulti_test.java create mode 100644 src/org/hyperic/hq/hqapi1/test/MetricDataGetLast_test.java create mode 100644 src/org/hyperic/hq/hqapi1/test/MetricDataGetMulti_test.java create mode 100644 src/org/hyperic/hq/hqapi1/test/MetricDataGet_test.java create mode 100644 src/org/hyperic/hq/hqapi1/test/MetricDataTestBase.java create mode 100644 src/org/hyperic/hq/hqapi1/test/WADLMetricData_test.java diff --git a/ChangeLog b/ChangeLog index a2568a8f..e92cd21b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,14 @@ Changes in HQApi 3.0 + *) [HHQ-3216] Revamp metricData CLI command to allow metrics to be pulled + for a single resource in addition to metrics. Allow different time + windows to be specified and format output in human readable form rather + than XML. + + *) [HHQ-3216] Split out APIs for pulling or adding Metric data into a new + MetricDataApi. Old methods for pulling metric data have been deprecated. + *) [HHQ-3244] Add better error handling to Resource create and updates. *) MetricApi.getMetrics(Resource r) and MetricApi.getEnabledMetrics(Resource r) diff --git a/hqu/hqapi1/app/MetricController.groovy b/hqu/hqapi1/app/MetricController.groovy index cf334720..b562566a 100644 --- a/hqu/hqapi1/app/MetricController.groovy +++ b/hqu/hqapi1/app/MetricController.groovy @@ -42,6 +42,9 @@ class MetricController extends ApiController { } } + /** + * @deprecated + */ private Closure getMetricDataXML(r) { { doc -> MetricData(resourceId: r.resource.id, @@ -334,6 +337,9 @@ class MetricController extends ApiController { } } + /** + * @deprecated + */ def getData(params) { def metricId = params.getOne("metricId")?.toInteger() def start = params.getOne("start")?.toLong() @@ -380,6 +386,9 @@ class MetricController extends ApiController { } } + /** + * @deprecated + */ def getGroupData(params) { def groupId = params.getOne("groupId")?.toInteger() def templateId = params.getOne("templateId")?.toInteger() @@ -459,6 +468,9 @@ class MetricController extends ApiController { } } + /** + * @deprecated + */ def getResourceData(params) { def ids = params["ids"] diff --git a/hqu/hqapi1/app/MetricdataController.groovy b/hqu/hqapi1/app/MetricdataController.groovy new file mode 100644 index 00000000..a655b575 --- /dev/null +++ b/hqu/hqapi1/app/MetricdataController.groovy @@ -0,0 +1,222 @@ +import org.hyperic.hq.hqapi1.ErrorCode; + +class MetricdataController extends ApiController { + + private Closure getMetricDataXML(r) { + { doc -> + MetricData(resourceId: r.resource.id, + resourceName: r.resource.name, + metricId: r.metric.id, + metricName: r.metric.template.name) { + // TODO: Backend does not always return data in asending order + for (dp in r.data.sort {a, b -> a.timestamp <=> b.timestamp}) { + DataPoint(timestamp : dp.timestamp, + value : dp.value) + } + } + } + } + + private Closure getLastMetricDataXML(r) { + { doc -> + LastMetricData(resourceId: r.resource.id, + resourceName: r.resource.name, + metricId: r.metric.id, + metricName: r.metric.template.name) { + if (!r.data) { + log.warn("No data found for metric id=" + r.metric.id) + } else { + DataPoint(timestamp : r.data.timestamp, + value : r.data.value) + } + } + } + } + + /** + * Validate metric parameters, returning a Closure representing the error + * or null if the parameters are valid + */ + private Closure validateParameters(metricIds, start, end) { + + if (!start) { + return getFailureXML(ErrorCode.INVALID_PARAMETERS, + "Start time not given") + } + if (!end) { + return getFailureXML(ErrorCode.INVALID_PARAMETERS, + "End time not given") + } + if (end < start) { + return getFailureXML(ErrorCode.INVALID_PARAMETERS, + "End time cannot be < start time") + } + return validateParameters(metricIds) + } + + /** + * Validate metric parameters, returning a Closure representing the error + * or null if the parameters are valid + */ + private Closure validateParameters(metricIds) { + if (metricIds == null || metricIds.size() == 0) { + return getFailureXML(ErrorCode.INVALID_PARAMETERS, + "Metric id not given") + } + + for (mid in metricIds) { + if (!mid) { + return getFailureXML(ErrorCode.INVALID_PARAMETERS, + "Metric id not given") + } + def metric = metricHelper.findMeasurementById(mid) + if (!metric) { + return getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Metric id " + mid + " not found") + } + } + return null + } + + def get(params) { + def metricId = params.getOne("id")?.toInteger() + def start = params.getOne("start")?.toLong() + def end = params.getOne("end")?.toLong() + + def failureXml = validateParameters([metricId], start, end) + def metric = metricHelper.findMeasurementById(metricId) + def data + if (!failureXml) { + try { + data = metric.getData(start, end) + } catch (Exception e) { + log.error("UnexpectedError: " + e.getMessage(), e); + failureXml = getFailureXML(ErrorCode.UNEXPECTED_ERROR) + } + } + + renderXml() { + MetricDataResponse() { + if (failureXml) { + out << failureXml + } else { + def result = [resource: metric.resource, metric: metric, + data: data] + out << getSuccessXML() + out << getMetricDataXML(result) + } + } + } + } + + def getLast(params) { + def metricId = params.getOne("id")?.toInteger() + + def failureXml = validateParameters([metricId]) + def metric = metricHelper.findMeasurementById(metricId) + def data + if (!failureXml) { + try { + data = metric.getLastDataPoint() + } catch (Exception e) { + log.error("UnexpectedError: " + e.getMessage(), e); + failureXml = getFailureXML(ErrorCode.UNEXPECTED_ERROR) + } + } + + renderXml() { + LastMetricDataResponse() { + if (failureXml) { + out << failureXml + } else { + def result = [resource: metric.resource, metric: metric, + data: data] + out << getSuccessXML() + out << getLastMetricDataXML(result) + } + } + } + } + + def getMulti(params) { + def metricIds = params.get("id")*.toInteger() + def start = params.getOne("start")?.toLong() + def end = params.getOne("end")?.toLong() + + def failureXml = validateParameters(metricIds, start, end) + + def results = [] + + if (!failureXml) { + for (m in metricIds) { + try { + // TODO: Switch to collections based API + def metric = metricHelper.findMeasurementById(m) + def data = metric.getData(start, end) + results << [resource: metric.resource, + metric: metric, data: data] + } catch (Exception e) { + log.error("UnexpectedError: " + e.getMessage(), e); + failureXml = getFailureXML(ErrorCode.UNEXPECTED_ERROR) + } + } + } + + renderXml() { + MetricsDataResponse() { + if (failureXml) { + out << failureXml + } else { + out << getSuccessXML() + for (result in results) { + out << getMetricDataXML(result) + } + } + } + } + } + + def getMultiLast(params) { + def metricIds = params.get("id")*.toInteger() + + def failureXml = validateParameters(metricIds) + + def results = [] + + if (!failureXml) { + for (m in metricIds) { + try { + def metric = metricHelper.findMeasurementById(m) + def data = metric.getLastDataPoint() + results << [resource: metric.resource, + metric: metric, data: data] + } catch (Exception e) { + log.error("UnexpectedError: " + e.getMessage(), e); + failureXml = getFailureXML(ErrorCode.UNEXPECTED_ERROR) + } + } + } + + renderXml() { + LastMetricsDataResponse() { + if (failureXml) { + out << failureXml + } else { + out << getSuccessXML() + for (result in results) { + out << getLastMetricDataXML(result) + } + } + } + } + } + + def put(params) { + + renderXml() { + StatusResponse() { + out << getFailureXML(ErrorCode.NOT_IMPLEMENTED); + } + } + } +} \ No newline at end of file diff --git a/src/org/hyperic/hq/hqapi1/HQApi.java b/src/org/hyperic/hq/hqapi1/HQApi.java index 466049fa..8dbee664 100644 --- a/src/org/hyperic/hq/hqapi1/HQApi.java +++ b/src/org/hyperic/hq/hqapi1/HQApi.java @@ -47,6 +47,7 @@ public class HQApi { private final ResourceEdgeApi _resourceEdgeApi; private final ServerConfigApi _serverConfigApi; private final AlertApi _alertApi; + private final MetricDataApi _metricDataApi; /** * @param host The hostname of the HQ Server to connect to. @@ -72,6 +73,7 @@ public HQApi(String host, int port, boolean isSecure, String user, _resourceEdgeApi = new ResourceEdgeApi(connection); _serverConfigApi = new ServerConfigApi(connection); _alertApi = new AlertApi(connection); + _metricDataApi = new MetricDataApi(connection); } /** @@ -119,6 +121,16 @@ public MetricApi getMetricApi() { return _metricApi; } + /** + * Import or Export Metric data + * + * @return The API for querying or reporting Metric information. + */ + public MetricDataApi getMetricDataApi() { + return _metricDataApi; + } + + /** * Add, remove and assign escalations. * diff --git a/src/org/hyperic/hq/hqapi1/MetricApi.java b/src/org/hyperic/hq/hqapi1/MetricApi.java index 18390360..d4d414dc 100644 --- a/src/org/hyperic/hq/hqapi1/MetricApi.java +++ b/src/org/hyperic/hq/hqapi1/MetricApi.java @@ -248,6 +248,7 @@ public StatusResponse syncMetricTemplates(List templates) * via {@link org.hyperic.hq.hqapi1.types.MetricDataResponse#getMetricData()}. * * @throws IOException If a network error occurs while making the request. + * @deprecated See {@link org.hyperic.hq.hqapi1.MetricDataApi}. */ public MetricDataResponse getMetricData(int metricId, long start, long end) throws IOException @@ -277,6 +278,7 @@ public MetricDataResponse getMetricData(int metricId, long start, long end) * via {@link org.hyperic.hq.hqapi1.types.MetricsDataResponse#getMetricData()}. * * @throws IOException If a network error occurs while making the request. + * @deprecated See {@link org.hyperic.hq.hqapi1.MetricDataApi}. */ public MetricsDataResponse getMetricData(int groupId, int templateId, long start, long end) @@ -311,6 +313,7 @@ public MetricsDataResponse getMetricData(int groupId, int templateId, * via {@link org.hyperic.hq.hqapi1.types.MetricsDataResponse#getMetricData()}. * * @throws IOException If a network error occurs while making the request. + * @deprecated See {@link org.hyperic.hq.hqapi1.MetricDataApi}. */ public MetricsDataResponse getMetricData(int[] resourceIds, int templateId, long start, long end) diff --git a/src/org/hyperic/hq/hqapi1/MetricDataApi.java b/src/org/hyperic/hq/hqapi1/MetricDataApi.java new file mode 100644 index 00000000..224f09ab --- /dev/null +++ b/src/org/hyperic/hq/hqapi1/MetricDataApi.java @@ -0,0 +1,131 @@ +package org.hyperic.hq.hqapi1; + +import org.hyperic.hq.hqapi1.types.DataPoint; +import org.hyperic.hq.hqapi1.types.MetricDataResponse; +import org.hyperic.hq.hqapi1.types.DataPointsRequest; +import org.hyperic.hq.hqapi1.types.StatusResponse; +import org.hyperic.hq.hqapi1.types.LastMetricsDataResponse; +import org.hyperic.hq.hqapi1.types.LastMetricDataResponse; +import org.hyperic.hq.hqapi1.types.MetricsDataResponse; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.HashMap; + +public class MetricDataApi extends BaseApi { + + MetricDataApi(HQConnection conn) { + super(conn); + } + + /** + * Get the {@link org.hyperic.hq.hqapi1.types.MetricData} for the + * given {@link org.hyperic.hq.hqapi1.types.Metric} id. + * + * @param metricId The id of the {@link org.hyperic.hq.hqapi1.types.Metric} to query. + * @param start The start time to query, in epoch-millis. + * @param end The end time to query, in epoch-millis. + * + * @return {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS} + * if the data was succesfully queried. The returned data can be retrieved + * via {@link org.hyperic.hq.hqapi1.types.MetricDataResponse#getMetricData()}. + * + * @throws IOException If a network error occurs while making the request. + */ + public MetricDataResponse getData(int metricId, long start, long end) + throws IOException + { + Map params = new HashMap(); + params.put("id", new String[] { Integer.toString(metricId) }); + params.put("start", new String[] { Long.toString(start)}); + params.put("end", new String[] { Long.toString(end)}); + return doGet("metricData/get.hqu", params, MetricDataResponse.class); + } + + /** + * Get the {@link org.hyperic.hq.hqapi1.types.LastMetricData} for the + * given {@link org.hyperic.hq.hqapi1.types.Metric} id. This object + * represents the last metric collection for the given metric id. + * + * @param metricId The id of the {@link org.hyperic.hq.hqapi1.types.Metric} to query. + * + * @return {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS} + * if the data was succesfully queried. The returned data can be retrieved + * via {@link org.hyperic.hq.hqapi1.types.LastMetricDataResponse#getLastMetricData()}. + * + * @throws IOException If a network error occurs while making the request. + */ + public LastMetricDataResponse getData(int metricId) + throws IOException + { + Map params = new HashMap(); + params.put("id", new String[] { Integer.toString(metricId) }); + return doGet("metricData/getLast.hqu", params, LastMetricDataResponse.class); + } + + /** + * Get the {@link org.hyperic.hq.hqapi1.types.MetricData} for the + * given list of {@link org.hyperic.hq.hqapi1.types.Metric} ids. + * + * @param metricIds The ids of the {@link org.hyperic.hq.hqapi1.types.Metric}s to query. + * @param start The start time to query, in epoch-millis. + * @param end The end time to query, in epoch-millis. + * + * @return {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS} + * if the data was succesfully queried. The returned data can be retrieved + * via {@link org.hyperic.hq.hqapi1.types.MetricsDataResponse#getMetricData()}. + * + * @throws IOException If a network error occurs while making the request. + */ + public MetricsDataResponse getData(int[] metricIds, long start, long end) + throws IOException + { + Map params = new HashMap(); + String[] ids = new String[metricIds.length]; + for (int i = 0; i < metricIds.length; i++) { + ids[i] = Integer.toString(metricIds[i]); + + } + params.put("id", ids); + params.put("start", new String[] { Long.toString(start)}); + params.put("end", new String[] { Long.toString(end)}); + return doGet("metricData/getMulti.hqu", params, MetricsDataResponse.class); + } + + /** + * Get the {@link org.hyperic.hq.hqapi1.types.LastMetricData} for the + * given list of {@link org.hyperic.hq.hqapi1.types.Metric} ids. This object + * represents the last metric collection for the given metric id. + * + * @param metricIds The ids of the {@link org.hyperic.hq.hqapi1.types.Metric}s to query. + * + * @return {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS} + * if the data was succesfully queried. The returned data can be retrieved + * via {@link org.hyperic.hq.hqapi1.types.LastMetricsDataResponse#getLastMetricData()}. + * + * @throws IOException If a network error occurs while making the request. + */ + public LastMetricsDataResponse getData(int[] metricIds) + throws IOException + { + Map params = new HashMap(); + String[] ids = new String[metricIds.length]; + for (int i = 0; i < metricIds.length; i++) { + ids[i] = Integer.toString(metricIds[i]); + + } + params.put("id", ids); + return doGet("metricData/getMultiLast.hqu", params, LastMetricsDataResponse.class); + } + + public StatusResponse addData(int metricId, List data) + throws IOException + { + DataPointsRequest request = new DataPointsRequest(); + request.setMetricId(metricId); + request.getDataPoint().addAll(data); + + return doPost("metricData/put.hqu", request, StatusResponse.class); + } +} diff --git a/src/org/hyperic/hq/hqapi1/test/MetricDataAddData_test.java b/src/org/hyperic/hq/hqapi1/test/MetricDataAddData_test.java new file mode 100644 index 00000000..2f4132b8 --- /dev/null +++ b/src/org/hyperic/hq/hqapi1/test/MetricDataAddData_test.java @@ -0,0 +1,46 @@ +package org.hyperic.hq.hqapi1.test; + +import org.hyperic.hq.hqapi1.MetricApi; +import org.hyperic.hq.hqapi1.MetricDataApi; +import org.hyperic.hq.hqapi1.types.Resource; +import org.hyperic.hq.hqapi1.types.MetricsResponse; +import org.hyperic.hq.hqapi1.types.Metric; +import org.hyperic.hq.hqapi1.types.DataPoint; +import org.hyperic.hq.hqapi1.types.StatusResponse; + +import java.util.List; +import java.util.ArrayList; +import java.util.Random; + +public class MetricDataAddData_test extends MetricDataTestBase { + + public MetricDataAddData_test(String name) { + super(name); + } + + public void testAddData() throws Exception { + + MetricApi api = getApi().getMetricApi(); + MetricDataApi dataApi = getApi().getMetricDataApi(); + + Resource platform = getLocalPlatformResource(false, false); + MetricsResponse metricsResponse = api.getMetrics(platform, true); + hqAssertSuccess(metricsResponse); + assertTrue("No metrics found for " + platform.getName(), + metricsResponse.getMetric().size() > 0); + + Metric m = metricsResponse.getMetric().get(0); + + List dps = new ArrayList(); + Random r = new Random(); + for (int i = 1; i < 100; i++) { + DataPoint dp = new DataPoint(); + dp.setTimestamp(i); + dp.setValue(r.nextDouble()); + dps.add(dp); + } + + StatusResponse response = dataApi.addData(m.getId(), dps); + hqAssertFailureNotImplemented(response); + } +} diff --git a/src/org/hyperic/hq/hqapi1/test/MetricDataGetLastMulti_test.java b/src/org/hyperic/hq/hqapi1/test/MetricDataGetLastMulti_test.java new file mode 100644 index 00000000..1f22cb6c --- /dev/null +++ b/src/org/hyperic/hq/hqapi1/test/MetricDataGetLastMulti_test.java @@ -0,0 +1,100 @@ +package org.hyperic.hq.hqapi1.test; + +import org.hyperic.hq.hqapi1.MetricApi; +import org.hyperic.hq.hqapi1.MetricDataApi; +import org.hyperic.hq.hqapi1.types.Resource; +import org.hyperic.hq.hqapi1.types.MetricsResponse; +import org.hyperic.hq.hqapi1.types.MetricsDataResponse; +import org.hyperic.hq.hqapi1.types.MetricData; +import org.hyperic.hq.hqapi1.types.LastMetricsDataResponse; +import org.hyperic.hq.hqapi1.types.LastMetricData; +import org.hyperic.hq.hqapi1.types.Metric; +import org.hyperic.hq.hqapi1.types.LastMetricDataResponse; + +import java.util.ArrayList; +import java.util.List; + +public class MetricDataGetLastMulti_test extends MetricDataTestBase { + + public MetricDataGetLastMulti_test(String name) { + super(name); + } + + public void testValidGet() throws Exception { + + MetricApi api = getApi().getMetricApi(); + MetricDataApi dataApi = getApi().getMetricDataApi(); + + Resource platform = getLocalPlatformResource(false, false); + MetricsResponse metricsResponse = api.getMetrics(platform, true); + hqAssertSuccess(metricsResponse); + assertTrue("No metrics found for " + platform.getName(), + metricsResponse.getMetric().size() > 0); + + int[] mids = new int[metricsResponse.getMetric().size()]; + for (int i = 0; i < metricsResponse.getMetric().size(); i++) { + mids[i] = metricsResponse.getMetric().get(i).getId(); + } + + LastMetricsDataResponse dataResponse = dataApi.getData(mids); + hqAssertSuccess(dataResponse); + + for (LastMetricData metricData : dataResponse.getLastMetricData()) { + validateLastMetricData(metricData); + } + } + + public void testGetInvalidMetricId() throws Exception { + + MetricDataApi dataApi = getApi().getMetricDataApi(); + + int[] mids = { Integer.MAX_VALUE }; + + LastMetricsDataResponse dataResponse = dataApi.getData(mids); + hqAssertFailureObjectNotFound(dataResponse); + } + + public void testGetEmptyMetricArray() throws Exception { + + MetricDataApi dataApi = getApi().getMetricDataApi(); + + int[] mids = {}; + + LastMetricsDataResponse dataResponse = dataApi.getData(mids); + hqAssertFailureInvalidParameters(dataResponse); + } + + public void testGetLastNoData() throws Exception { + + MetricApi api = getApi().getMetricApi(); + MetricDataApi dataApi = getApi().getMetricDataApi(); + + Resource platform = getLocalPlatformResource(false, false); + MetricsResponse metricsResponse = api.getMetrics(platform, false); + hqAssertSuccess(metricsResponse); + assertTrue("No metrics found for " + platform.getName(), + metricsResponse.getMetric().size() > 0); + + List disabledMetrics = new ArrayList(); + for (Metric m : metricsResponse.getMetric()) { + if (!m.isEnabled()) { + disabledMetrics.add(m); + } + } + + assertTrue("No disabled metrics could be found", disabledMetrics.size() > 0); + + int[] mids = new int[disabledMetrics.size()]; + for (int i = 0; i < disabledMetrics.size(); i++) { + mids[i] = disabledMetrics.get(i).getId(); + } + + LastMetricsDataResponse dataResponse = dataApi.getData(mids); + hqAssertSuccess(dataResponse); + // TODO: What is the correct behavior of the API if the last data point + // could not be found? Return an error for simply null? + for (LastMetricData d : dataResponse.getLastMetricData()) { + assertNull("Metric datapoint not null", d.getDataPoint()); + } + } +} diff --git a/src/org/hyperic/hq/hqapi1/test/MetricDataGetLast_test.java b/src/org/hyperic/hq/hqapi1/test/MetricDataGetLast_test.java new file mode 100644 index 00000000..8cf9b537 --- /dev/null +++ b/src/org/hyperic/hq/hqapi1/test/MetricDataGetLast_test.java @@ -0,0 +1,67 @@ +package org.hyperic.hq.hqapi1.test; + +import org.hyperic.hq.hqapi1.MetricApi; +import org.hyperic.hq.hqapi1.MetricDataApi; +import org.hyperic.hq.hqapi1.types.Resource; +import org.hyperic.hq.hqapi1.types.MetricsResponse; +import org.hyperic.hq.hqapi1.types.Metric; +import org.hyperic.hq.hqapi1.types.LastMetricDataResponse; + +public class MetricDataGetLast_test extends MetricDataTestBase { + + public MetricDataGetLast_test(String name) { + super(name); + } + + public void testValidGet() throws Exception { + MetricApi api = getApi().getMetricApi(); + MetricDataApi dataApi = getApi().getMetricDataApi(); + + Resource platform = getLocalPlatformResource(false, false); + MetricsResponse metricsResponse = api.getMetrics(platform, true); + hqAssertSuccess(metricsResponse); + assertTrue("No metrics found for " + platform.getName(), + metricsResponse.getMetric().size() > 0); + + Metric m = metricsResponse.getMetric().get(0); + + LastMetricDataResponse dataResponse = dataApi.getData(m.getId()); + hqAssertSuccess(dataResponse); + + validateLastMetricData(dataResponse.getLastMetricData()); + } + + public void testGetInvalidMetricId() throws Exception { + MetricDataApi dataApi = getApi().getMetricDataApi(); + LastMetricDataResponse dataResponse = dataApi.getData(Integer.MAX_VALUE); + hqAssertFailureObjectNotFound(dataResponse); + } + + public void testGetLastNoData() throws Exception { + + MetricApi api = getApi().getMetricApi(); + MetricDataApi dataApi = getApi().getMetricDataApi(); + + Resource platform = getLocalPlatformResource(false, false); + MetricsResponse metricsResponse = api.getMetrics(platform, false); + hqAssertSuccess(metricsResponse); + assertTrue("No metrics found for " + platform.getName(), + metricsResponse.getMetric().size() > 0); + + Metric metric = null; + for (Metric m : metricsResponse.getMetric()) { + if (!m.isEnabled()) { + metric = m; + } + } + + assertNotNull("No disabled metric could be found", metric); + + LastMetricDataResponse dataResponse = dataApi.getData(metric.getId()); + hqAssertSuccess(dataResponse); + // TODO: What is the correct behavior of the API if the last data point + // could not be found? Return an error for simply null? + assertNull("Metric datapoint not null", + dataResponse.getLastMetricData().getDataPoint()); + } +} diff --git a/src/org/hyperic/hq/hqapi1/test/MetricDataGetMulti_test.java b/src/org/hyperic/hq/hqapi1/test/MetricDataGetMulti_test.java new file mode 100644 index 00000000..dc4b54ce --- /dev/null +++ b/src/org/hyperic/hq/hqapi1/test/MetricDataGetMulti_test.java @@ -0,0 +1,89 @@ +package org.hyperic.hq.hqapi1.test; + +import org.hyperic.hq.hqapi1.MetricApi; +import org.hyperic.hq.hqapi1.MetricDataApi; +import org.hyperic.hq.hqapi1.types.Resource; +import org.hyperic.hq.hqapi1.types.MetricsResponse; +import org.hyperic.hq.hqapi1.types.MetricsDataResponse; +import org.hyperic.hq.hqapi1.types.MetricData; + +public class MetricDataGetMulti_test extends MetricDataTestBase { + + public MetricDataGetMulti_test(String name) { + super(name); + } + + public void testValidGet() throws Exception { + + MetricApi api = getApi().getMetricApi(); + MetricDataApi dataApi = getApi().getMetricDataApi(); + + Resource platform = getLocalPlatformResource(false, false); + MetricsResponse metricsResponse = api.getMetrics(platform, true); + hqAssertSuccess(metricsResponse); + assertTrue("No metrics found for " + platform.getName(), + metricsResponse.getMetric().size() > 0); + + int[] mids = new int[metricsResponse.getMetric().size()]; + for (int i = 0; i < metricsResponse.getMetric().size(); i++) { + mids[i] = metricsResponse.getMetric().get(i).getId(); + } + + long end = System.currentTimeMillis(); + long start = end - (8 * 60 * 60 * 1000); + MetricsDataResponse dataResponse = dataApi.getData(mids, start, end); + hqAssertSuccess(dataResponse); + + for (MetricData metricData : dataResponse.getMetricData()) { + validateMetricData(metricData); + } + } + + public void testGetInvalidMetricId() throws Exception { + + MetricDataApi dataApi = getApi().getMetricDataApi(); + + long end = System.currentTimeMillis(); + long start = end - (8 * 60 * 60 * 1000); + + int[] mids = { Integer.MAX_VALUE }; + + MetricsDataResponse dataResponse = dataApi.getData(mids, start, end); + hqAssertFailureObjectNotFound(dataResponse); + } + + public void testGetEmptyMetricArray() throws Exception { + + MetricDataApi dataApi = getApi().getMetricDataApi(); + + long end = System.currentTimeMillis(); + long start = end - (8 * 60 * 60 * 1000); + + int[] mids = {}; + + MetricsDataResponse dataResponse = dataApi.getData(mids, start, end); + hqAssertFailureInvalidParameters(dataResponse); + } + + public void testGetInvalidRange() throws Exception { + + MetricApi api = getApi().getMetricApi(); + MetricDataApi dataApi = getApi().getMetricDataApi(); + + Resource platform = getLocalPlatformResource(false, false); + MetricsResponse metricsResponse = api.getMetrics(platform, true); + hqAssertSuccess(metricsResponse); + assertTrue("No metrics found for " + platform.getName(), + metricsResponse.getMetric().size() > 0); + + int[] mids = new int[metricsResponse.getMetric().size()]; + for (int i = 0; i < metricsResponse.getMetric().size(); i++) { + mids[i] = metricsResponse.getMetric().get(i).getId(); + } + + long end = System.currentTimeMillis(); + long start = end - (8 * 60 * 60 * 1000); + MetricsDataResponse dataResponse = dataApi.getData(mids, end, start); + hqAssertFailureInvalidParameters(dataResponse); + } +} diff --git a/src/org/hyperic/hq/hqapi1/test/MetricDataGet_test.java b/src/org/hyperic/hq/hqapi1/test/MetricDataGet_test.java new file mode 100644 index 00000000..87efebc1 --- /dev/null +++ b/src/org/hyperic/hq/hqapi1/test/MetricDataGet_test.java @@ -0,0 +1,66 @@ +package org.hyperic.hq.hqapi1.test; + +import org.hyperic.hq.hqapi1.MetricDataApi; +import org.hyperic.hq.hqapi1.MetricApi; +import org.hyperic.hq.hqapi1.types.MetricsResponse; +import org.hyperic.hq.hqapi1.types.Resource; +import org.hyperic.hq.hqapi1.types.Metric; +import org.hyperic.hq.hqapi1.types.MetricDataResponse; + +public class MetricDataGet_test extends MetricDataTestBase { + + public MetricDataGet_test(String name) { + super(name); + } + + public void testValidGet() throws Exception { + + MetricApi api = getApi().getMetricApi(); + MetricDataApi dataApi = getApi().getMetricDataApi(); + + Resource platform = getLocalPlatformResource(false, false); + MetricsResponse metricsResponse = api.getMetrics(platform, true); + hqAssertSuccess(metricsResponse); + assertTrue("No metrics found for " + platform.getName(), + metricsResponse.getMetric().size() > 0); + + Metric m = metricsResponse.getMetric().get(0); + + long end = System.currentTimeMillis(); + long start = end - (8 * 60 * 60 * 1000); + MetricDataResponse dataResponse = dataApi.getData(m.getId(), start, end); + hqAssertSuccess(dataResponse); + + validateMetricData(dataResponse.getMetricData()); + } + + public void testGetInvalidMetricId() throws Exception { + + MetricDataApi dataApi = getApi().getMetricDataApi(); + + long end = System.currentTimeMillis(); + long start = end - (8 * 60 * 60 * 1000); + MetricDataResponse dataResponse = dataApi.getData(Integer.MAX_VALUE, + start, end); + hqAssertFailureObjectNotFound(dataResponse); + } + + public void testGetInvalidRange() throws Exception { + + MetricApi api = getApi().getMetricApi(); + MetricDataApi dataApi = getApi().getMetricDataApi(); + + Resource platform = getLocalPlatformResource(false, false); + MetricsResponse metricsResponse = api.getMetrics(platform, true); + hqAssertSuccess(metricsResponse); + assertTrue("No metrics found for " + platform.getName(), + metricsResponse.getMetric().size() > 0); + + Metric m = metricsResponse.getMetric().get(0); + + long end = System.currentTimeMillis(); + long start = end - (8 * 60 * 60 * 1000); + MetricDataResponse dataResponse = dataApi.getData(m.getId(), start, end); + hqAssertSuccess(dataResponse); + } +} diff --git a/src/org/hyperic/hq/hqapi1/test/MetricDataTestBase.java b/src/org/hyperic/hq/hqapi1/test/MetricDataTestBase.java new file mode 100644 index 00000000..5bdd447f --- /dev/null +++ b/src/org/hyperic/hq/hqapi1/test/MetricDataTestBase.java @@ -0,0 +1,40 @@ +package org.hyperic.hq.hqapi1.test; + +import org.hyperic.hq.hqapi1.types.MetricData; +import org.hyperic.hq.hqapi1.types.DataPoint; +import org.hyperic.hq.hqapi1.types.LastMetricData; + +public class MetricDataTestBase extends HQApiTestBase { + + public MetricDataTestBase(String name) { + super(name); + } + + protected void validateMetricData(MetricData data) { + assertTrue("Resource name is empty", data.getResourceName().length() > 0); + assertTrue("Invalid resource id", data.getResourceId() > 0); + assertTrue("Metric name is empty", data.getMetricName().length() > 0); + assertTrue("Invalid metric id", data.getMetricId() > 0); + + long lastTs = 0; + // Data points should be in ascending order + for (DataPoint dp : data.getDataPoint()) { + assertTrue("Timestamp out of order " + lastTs + " > " + dp.getTimestamp(), + lastTs < dp.getTimestamp()); + lastTs = dp.getTimestamp(); + } + } + + + protected void validateLastMetricData(LastMetricData data) { + assertTrue("Resource name is empty", data.getResourceName().length() > 0); + assertTrue("Invalid resource id", data.getResourceId() > 0); + assertTrue("Metric name is empty", data.getMetricName().length() > 0); + assertTrue("Invalid metric id", data.getMetricId() > 0); + + DataPoint dp = data.getDataPoint(); + assertNotNull(dp); + assertTrue("Timestamp incorrect", dp.getTimestamp() > 0); + assertTrue("Metric value incorrect", dp.getValue() >= 0); + } +} diff --git a/src/org/hyperic/hq/hqapi1/test/MetricData_test.java b/src/org/hyperic/hq/hqapi1/test/MetricData_test.java index ad754ee2..f2f341ca 100644 --- a/src/org/hyperic/hq/hqapi1/test/MetricData_test.java +++ b/src/org/hyperic/hq/hqapi1/test/MetricData_test.java @@ -52,6 +52,9 @@ import java.util.Random; import java.io.IOException; +/** + * These are tests for the old Metric API which as of 3.0 is deprecated. + */ public class MetricData_test extends MetricTestBase { public MetricData_test(String name) { diff --git a/src/org/hyperic/hq/hqapi1/test/WADLMetricData_test.java b/src/org/hyperic/hq/hqapi1/test/WADLMetricData_test.java new file mode 100644 index 00000000..71767494 --- /dev/null +++ b/src/org/hyperic/hq/hqapi1/test/WADLMetricData_test.java @@ -0,0 +1,70 @@ +package org.hyperic.hq.hqapi1.test; + +import org.hyperic.hq.hqapi1.wadl.*; + +import java.util.ArrayList; +import java.util.List; + +public class WADLMetricData_test extends WADLTestBase { + + public void testGet() throws Exception { + Endpoint.MetricDataGetHqu get = new Endpoint.MetricDataGetHqu(); + + MetricDataResponse response = + get.getAsMetricDataResponse(Integer.MAX_VALUE, + 0l, System.currentTimeMillis()); + hqAssertFailure(response); + } + + public void testGetLast() throws Exception { + Endpoint.MetricDataGetLastHqu last = new Endpoint.MetricDataGetLastHqu(); + + LastMetricDataResponse response = + last.getAsLastMetricDataResponse(Integer.MAX_VALUE); + hqAssertFailure(response); + } + + public void testGetMulti() throws Exception { + Endpoint.MetricDataGetMultiHqu multi = new Endpoint.MetricDataGetMultiHqu(); + + List ids = new ArrayList(); + ids.add(0); + ids.add(2); + MetricsDataResponse response = + multi.getAsMetricsDataResponse(ids, 0l, + System.currentTimeMillis()); + hqAssertFailure(response); + + } + + public void testGetMultiLast() throws Exception { + Endpoint.MetricDataGetMultiLastHqu multiLast = + new Endpoint.MetricDataGetMultiLastHqu(); + + List ids = new ArrayList(); + ids.add(0); + ids.add(2); + + LastMetricsDataResponse response = + multiLast.getAsLastMetricsDataResponse(ids); + hqAssertFailure(response); + } + + public void testPut() throws Exception { + Endpoint.MetricDataPutHqu put = new Endpoint.MetricDataPutHqu(); + + List dps = new ArrayList(); + DataPoint dp = new DataPoint(); + dp.setTimestamp(System.currentTimeMillis()); + dp.setValue(0); + dps.add(dp); + + DataPointsRequest request = new DataPointsRequest(); + request.setMetricId(Integer.MAX_VALUE); + request.getDataPoint().addAll(dps); + + StatusResponse response = put.postAsStatusResponse(request); + hqAssertFailure(response); + } +} + diff --git a/src/org/hyperic/hq/hqapi1/tools/MetricDataCommand.java b/src/org/hyperic/hq/hqapi1/tools/MetricDataCommand.java index b79334fd..9e9561bd 100644 --- a/src/org/hyperic/hq/hqapi1/tools/MetricDataCommand.java +++ b/src/org/hyperic/hq/hqapi1/tools/MetricDataCommand.java @@ -31,10 +31,17 @@ import joptsimple.OptionSet; import org.hyperic.hq.hqapi1.HQApi; import org.hyperic.hq.hqapi1.MetricApi; -import org.hyperic.hq.hqapi1.XmlUtil; +import org.hyperic.hq.hqapi1.MetricDataApi; +import org.hyperic.hq.hqapi1.ResourceApi; import org.hyperic.hq.hqapi1.types.MetricDataResponse; +import org.hyperic.hq.hqapi1.types.DataPoint; +import org.hyperic.hq.hqapi1.types.LastMetricsDataResponse; +import org.hyperic.hq.hqapi1.types.MetricsResponse; +import org.hyperic.hq.hqapi1.types.ResourceResponse; +import org.hyperic.hq.hqapi1.types.LastMetricData; import java.util.Arrays; +import java.util.Date; public class MetricDataCommand extends Command { @@ -42,7 +49,9 @@ public class MetricDataCommand extends Command { private static String[] COMMANDS = { CMD_LIST }; - private static final String OPT_METRIC_ID = "metricId"; + private static final String OPT_RESOURCE_ID = "resourceId"; + private static final String OPT_METRIC_ID = "metricId"; + private static final String OPT_HOURS = "hours"; private void printUsage() { System.err.println("One of " + Arrays.toString(COMMANDS) + " required"); @@ -67,20 +76,71 @@ private void list(String[] args) throws Exception { OptionParser p = getOptionParser(); - p.accepts(OPT_METRIC_ID, "The metric id to query for data"). - withRequiredArg().ofType(Integer.class); + p.accepts(OPT_RESOURCE_ID, "The resource id to query for metric data") + .withRequiredArg().ofType(Integer.class); + p.accepts(OPT_METRIC_ID, "The metric id to query for data") + .withRequiredArg().ofType(Integer.class); + p.accepts(OPT_HOURS, "The number of hours of data to query. Defaults to 8") + .withRequiredArg().ofType(Integer.class); OptionSet options = getOptions(p, args); HQApi api = getApi(options); + ResourceApi resourceApi = api.getResourceApi(); MetricApi metricApi = api.getMetricApi(); + MetricDataApi dataApi = api.getMetricDataApi(); long end = System.currentTimeMillis(); - long start = end - 8 * 60 * 60 * 1000; - MetricDataResponse data = - metricApi.getMetricData((Integer)getRequired(options, OPT_METRIC_ID), - start, end); + long start; + if (options.has(OPT_HOURS)) { + Integer hours = (Integer)options.valueOf(OPT_HOURS); + start = end - (hours * 60 * 60 * 1000); + } else { + start = end - 8 * 60 * 60 * 1000; + } - XmlUtil.serialize(data, System.out, Boolean.TRUE); + if (options.has(OPT_METRIC_ID)) { + + MetricDataResponse data = + dataApi.getData((Integer)getRequired(options, OPT_METRIC_ID), + start, end); + checkSuccess(data); + + System.out.println("Values for " + data.getMetricData().getMetricName() + + " on " + data.getMetricData().getResourceName()); + for (DataPoint dp : data.getMetricData().getDataPoint()) { + System.out.println(" " + new Date(dp.getTimestamp()) + + " = " + dp.getValue()); + } + } else if (options.has(OPT_RESOURCE_ID)){ + ResourceResponse resource = + resourceApi.getResource((Integer)getRequired(options, + OPT_RESOURCE_ID), + false, false); + checkSuccess(resource); + + MetricsResponse metrics = metricApi.getMetrics(resource.getResource(), true); + checkSuccess(metrics); + + int[] ids = new int[metrics.getMetric().size()]; + for (int i = 0; i < metrics.getMetric().size(); i++) { + ids[i] = metrics.getMetric().get(i).getId(); + } + + LastMetricsDataResponse data = dataApi.getData(ids); + checkSuccess(data); + + System.out.println("Last metric values for " + resource.getResource().getName()); + for (LastMetricData d : data.getLastMetricData()) { + String value; + if (d.getDataPoint() != null) { + value = d.getDataPoint().getValue() + " (at " + + new Date(d.getDataPoint().getTimestamp()) + ")"; + } else { + value = "No data"; + } + System.out.println(" " + d.getMetricName() + " = " + value); + } + } } } diff --git a/xsd/HQApi1.wadl b/xsd/HQApi1.wadl index 61740b8f..56700735 100644 --- a/xsd/HQApi1.wadl +++ b/xsd/HQApi1.wadl @@ -519,6 +519,7 @@ + Get MetricData for the specified time window @@ -551,6 +552,7 @@ + Get MetricData for a MetricTemplate across a compatible Group @@ -589,6 +591,7 @@ + Get MetricData for a list of Resource's @@ -1327,6 +1330,126 @@ + + + Get MetricData for the specified time window for the requested Metric + + + + + + The id of the Metric to query for MetricData. + + + + + The start time in milliseconds from the epoch. + + + + + The end time in milliseconds from the epoch. + + + + + + + + + + + + Get the LastMetricData for given Metric + + + + + + The id of the Metric to query for MetricData. + + + + + + + + + + + + Get MetricData for the specified time window for multiple Metrics + + + + + + The ids of the Metrics to query for MetricData. + + + + + The start time in milliseconds from the epoch. + + + + + The end time in milliseconds from the epoch. + + + + + + + + + + + + Get the LastMetricData for given Metric + + + + + + The ids of the Metrics to query for MetricData. + + + + + + + + + + + + Push DataPoints into the system for the specified Metric + + + + + + + + + + + diff --git a/xsd/HQApi1.xsd b/xsd/HQApi1.xsd index db2ed355..c5971bc1 100644 --- a/xsd/HQApi1.xsd +++ b/xsd/HQApi1.xsd @@ -443,6 +443,16 @@ + + + + + + + + + + @@ -491,6 +501,18 @@ + + + + + + + + + + + + @@ -503,6 +525,29 @@ + + + + + + + + + + + + + + + + + + + + + + +