From eef8eb06c5cce316d0d482462e992553e4627822 Mon Sep 17 00:00:00 2001 From: Ryan Morgan Date: Mon, 24 May 2010 15:15:56 -0700 Subject: [PATCH 1/2] Fix URL generation error when first parameter is specified multiple times. --- src/org/hyperic/hq/hqapi1/HQConnection.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/org/hyperic/hq/hqapi1/HQConnection.java b/src/org/hyperic/hq/hqapi1/HQConnection.java index b5b3de84..ba5306a0 100644 --- a/src/org/hyperic/hq/hqapi1/HQConnection.java +++ b/src/org/hyperic/hq/hqapi1/HQConnection.java @@ -149,12 +149,15 @@ T doGet(String path, Map params, Class resultClass) for (Iterator i = params.keySet().iterator(); i.hasNext(); idx++) { String key = (String)i.next(); String[] vals = params.get(key); + boolean append = false; + for (String val : vals) { if (val != null) { - if (idx > 0) { + if (append) { uri.append("&"); } uri.append(key).append("=").append(urlEncode(val)); + append = true; } } } From beee14a60aa03b04fca181f0f0f652ca15432fd8 Mon Sep 17 00:00:00 2001 From: Ryan Morgan Date: Mon, 24 May 2010 16:30:19 -0700 Subject: [PATCH 2/2] Back port AlertApi to 2.x branch. --- ChangeLog | 3 + hqu/hqapi1/app/AlertController.groovy | 398 ++++++++++++++++++ hqu/hqapi1/app/MetricdataController.groovy | 67 +++ src/org/hyperic/hq/hqapi1/AlertApi.java | 335 +++++++++++++++ src/org/hyperic/hq/hqapi1/HQApi.java | 22 + src/org/hyperic/hq/hqapi1/HQConnection.java | 2 +- src/org/hyperic/hq/hqapi1/MetricDataApi.java | 49 +++ ...AlertDefinitionSyncControlAction_test.java | 28 -- .../hqapi1/test/AlertDefinitionTestBase.java | 26 ++ .../hq/hqapi1/test/AlertDelete_test.java | 64 +++ .../hqapi1/test/AlertFindByResource_test.java | 130 ++++++ .../hq/hqapi1/test/AlertFind_test.java | 109 +++++ .../hyperic/hq/hqapi1/test/AlertFix_test.java | 169 ++++++++ .../hyperic/hq/hqapi1/test/AlertTestBase.java | 192 +++++++++ .../hyperic/hq/hqapi1/test/HQApiTestBase.java | 327 +++++++++++++- .../hq/hqapi1/test/MaintenanceTestBase.java | 6 - .../hq/hqapi1/test/MetricData_test.java | 6 - .../hyperic/hq/hqapi1/tools/AlertCommand.java | 247 +++++++++++ src/org/hyperic/hq/hqapi1/tools/Shell.java | 1 + xsd/HQApi1.xsd | 76 ++++ 20 files changed, 2210 insertions(+), 47 deletions(-) create mode 100644 hqu/hqapi1/app/AlertController.groovy create mode 100644 hqu/hqapi1/app/MetricdataController.groovy create mode 100644 src/org/hyperic/hq/hqapi1/AlertApi.java create mode 100644 src/org/hyperic/hq/hqapi1/MetricDataApi.java create mode 100644 src/org/hyperic/hq/hqapi1/test/AlertDelete_test.java create mode 100644 src/org/hyperic/hq/hqapi1/test/AlertFindByResource_test.java create mode 100644 src/org/hyperic/hq/hqapi1/test/AlertFind_test.java create mode 100644 src/org/hyperic/hq/hqapi1/test/AlertFix_test.java create mode 100644 src/org/hyperic/hq/hqapi1/test/AlertTestBase.java create mode 100644 src/org/hyperic/hq/hqapi1/tools/AlertCommand.java diff --git a/ChangeLog b/ChangeLog index e133203e..37e51940 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,9 @@ Changes in HQApi 2.5 + *) Back port AlertApi to 2.x branch. As a part of this change portions of + the new MetricDataApi were also backported to aid in testing. + Changes in HQApi 2.4 *) [HHQ-3960] Allow filtering of alert definitions by priority. diff --git a/hqu/hqapi1/app/AlertController.groovy b/hqu/hqapi1/app/AlertController.groovy new file mode 100644 index 00000000..ecdad38a --- /dev/null +++ b/hqu/hqapi1/app/AlertController.groovy @@ -0,0 +1,398 @@ + +import org.hyperic.hq.hqapi1.ErrorCode + +import org.hyperic.hq.events.AlertSeverity +import org.hyperic.hibernate.PageInfo +import org.hyperic.hq.events.server.session.AlertSortField +import org.hyperic.hq.events.server.session.AlertManagerEJBImpl as AlertMan +import org.hyperic.hq.escalation.server.session.EscalationManagerEJBImpl as EscMan +import org.hyperic.hq.events.server.session.ClassicEscalationAlertType +import org.hyperic.hq.authz.shared.PermissionException + +public class AlertController extends ApiController { + + private aMan = AlertMan.one + private escMan = EscMan.one + + private static final int ROUNDING_VOODOO = 60000 + + private getEscalationState(alert) { + // TODO: Move to AlertCategory + if (alert.stateId) { + return escMan.findEscalationState(alert.alertDefinition) + } + return null + } + + private Closure getAlertXML(a) { + { doc -> + Alert(id : a.id, + name : a.alertDefinition.name, + alertDefinitionId : a.alertDefinition.id, + resourceId : a.alertDefinition.resource.id, + ctime : a.ctime, + fixed : a.fixed, + reason : aMan.getLongReason(a).trim()) { + def e = getEscalationState(a) + if (e) { + EscalationState(ackedBy: e.acknowledgedBy?.name, + escalationId: e.escalation.id, + nextActionTime: e.nextActionTime) + } + for (l in a.actionLog) { + if (l.subject + || (l.action && !l.action.alertDefinition) + || (l.action && l.action.className == "com.hyperic.hq.bizapp.server.action.alert.SnmpAction")) { + // Ignore 'internal' logs, but include escalation and snmp logs + AlertActionLog(timestamp: l.timeStamp, + detail: l.detail, + user: l.subject?.name) + } + } + } + } + } + + def get(params) { + def id = params.getOne("id")?.toInteger() + + def failureXml = null + + if (id == null) { + failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, + "Alert id not given") + } + + def alert + if (!failureXml) { + alert = getAlertById(id) + + if (!alert) { + failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Alert with id " + id + + " not found") + } + } + + renderXml() { + AlertResponse() { + if (failureXml) { + out << failureXml + } else { + out << getSuccessXML() + out << getAlertXML(alert) + } + } + } + } + + def find(params) { + Long begin = params.getOne("begin")?.toLong() + Long end = params.getOne("end")?.toLong() + Integer count = params.getOne("count")?.toInteger() + Integer sev = params.getOne("severity")?.toInteger() + Boolean inEsc = params.getOne("inEscalation", "false").toBoolean() + Boolean notFixed = params.getOne("notFixed", "false").toBoolean() + + def failureXml = null + def alerts = [] + if (begin == null || end == null || count == null || sev == null) { + failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, + "All required attributes not given") + } else if (begin >= end) { + failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, + "Begin must be < end") + } else if (count <= 0) { + failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, + "Count must ben > 0") + } else { + try { + Integer groupId = null + AlertSeverity severity = AlertSeverity.findByCode(sev) + PageInfo pInfo = PageInfo.create(0, count, AlertSortField.DATE, + false); + + // TODO: Work around incorrect TimingVoodoo in AlertManagerEJBImpl + long roundedEnd = end + (ROUNDING_VOODOO - (end % ROUNDING_VOODOO)) + long timerange = roundedEnd - begin + + alerts = alertHelper.findAlerts(severity, timerange, roundedEnd, + inEsc, notFixed, groupId, pInfo) + } catch (IllegalStateException e) { + failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, + "Invalid severity " + sev) + + } catch (Throwable t) { + failureXml = getFailureXML(ErrorCode.UNEXPECTED_ERROR, + t.getMessage()) + } + } + + renderXml() { + AlertsResponse() { + if (failureXml) { + out << failureXml + } else { + out << getSuccessXML() + // TODO: See above, re-apply the actual end time + for (a in alerts.findAll { it.ctime <= end }) { + out << getAlertXML(a) + } + } + } + } + } + + def findByResource(params) { + Integer rid = params.getOne("resourceId")?.toInteger() + Long begin = params.getOne("begin")?.toLong() + Long end = params.getOne("end")?.toLong() + Integer count = params.getOne("count")?.toInteger() + Integer sev = params.getOne("severity")?.toInteger() + Boolean inEsc = params.getOne("inEscalation", "false").toBoolean() + Boolean notFixed = params.getOne("notFixed", "false").toBoolean() + + def failureXml = null + def alerts = [] + if (rid == null || begin == null || end == null || sev == null) { + failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, + "All required attributes not given") + } else if (begin >= end) { + failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, + "Begin must be < end") + } else if (count <= 0) { + failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, + "Count must ben > 0") + } else { + try { + AlertSeverity severity = AlertSeverity.findByCode(sev) + def resource = getResource(rid) + if (!resource) { + failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Unable to find resource with " + + "id=" + rid) + } else { + // TODO: Work around incorrect TimingVoodoo in AlertManagerEJBImpl + long roundedEnd = end + (ROUNDING_VOODOO - (end % ROUNDING_VOODOO)) + alerts = resource.getAlerts(user, begin, roundedEnd, + count, severity) + //TODO: Move these to ResourceCategory or AlertManager + if (inEsc) { + alerts = alerts.findAll { it.stateId != null && it.stateId > 0 } + } + + if (notFixed) { + alerts = alerts.findAll { !it.fixed } + } + } + } catch (IllegalStateException e) { + failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, + "Invalid severity " + sev) + + } catch (Throwable t) { + failureXml = getFailureXML(ErrorCode.UNEXPECTED_ERROR, + t.getMessage()) + } + } + + renderXml() { + AlertsResponse() { + if (failureXml) { + out << failureXml + } else { + out << getSuccessXML() + // TODO: See above, re-apply the actual end time + for (a in alerts.findAll { it.ctime <= end }) { + out << getAlertXML(a) + } + } + } + } + } + + private getAlertById(id) { + // TODO: Add get method in AlertHelper + try { + return aMan.findAlertById(id) + } catch (Throwable t) { + return null + } + } + + def ack(params) { + def ids = params.get("id")*.toInteger() + def reason = params.getOne("reason") + def pause = params.getOne("pause", "0").toLong() + + def failureXml = null + def alerts = [] + + if (ids == null) { + failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, + "Required parameter id not given") + } else { + for (id in ids) { + def alert = getAlertById(id) + if (!alert) { + failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Unable to find alert with id = " + id) + } + } + + if (!failureXml) { + try { + // TODO: Add to EscalationHelper + for (id in ids) { + def success = escMan.acknowledgeAlert(user, ClassicEscalationAlertType.CLASSIC, + id, reason, pause) + if (!success) { + // TODO: Should re-evaluate this in the future, should we return an error? + log.warn("Alert id " + id + " was not in an " + + "acknowledgable state") + } + // Re-lookup alert so we have the correct escalation state + alerts << getAlertById(id) + } + } catch (PermissionException e) { + failureXml = getFailureXML(ErrorCode.PERMISSION_DENIED) + } catch (Throwable t) { + failureXml = getFailureXML(ErrorCode.UNEXPECTED_ERROR, + t.getMessage()) + } + } + } + + renderXml() { + AlertsResponse() { + if (failureXml) { + out << failureXml + } else { + out << getSuccessXML() + for (alert in alerts) { + out << getAlertXML(alert) + } + } + } + } + } + + private canManageAlerts(resource) { + // TODO: Fix AlertManager to handle permissions + def appdefResource + // TODO: Platform/Server/Service have different modifyAlert operations + def operation + if (resource.isPlatform()) { + operation = "modifyAlerts" + appdefResource = resource.toPlatform() + } else if (resource.isServer()) { + operation = "modifyAlerts" + appdefResource = resource.toServer() + } else if (resource.isService()) { + operation = "manageAlerts" + appdefResource = resource.toService() + } else { + log.warn("Unhandled resource to canManageAlerts: " + resource.name) + return false + } + + try { + appdefResource.checkPerms(operation:operation, user:user) + return true + } catch (Exception e) { + return false + } + } + + def fix(params) { + def ids = params.get("id")*.toInteger() + def reason = params.getOne("reason") + def failureXml = null + def alerts = [] + + if (ids == null) { + failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, + "Required parameter id not given") + + } else { + def alertsToFix = [] + for (id in ids) { + def alert = getAlertById(id) + if (!alert) { + failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Unable to find alert with id = " + id) + } else { + alertsToFix << alert + } + } + + if (!failureXml) { + try { + for (alert in alertsToFix) { + escMan.fixAlert(user, + ClassicEscalationAlertType.CLASSIC, + alert.id, + reason) + alerts << getAlertById(alert.id) + } + } catch (PermissionException p) { + failureXml = getFailureXML(ErrorCode.PERMISSION_DENIED) + } catch (Exception e) { + failureXml = getFailureXML(ErrorCode.UNEXPECTED_ERROR, + e.getMessage()) + } + } + } + + renderXml() { + AlertsResponse() { + if (failureXml) { + out << failureXml + } else { + out << getSuccessXML() + for (alert in alerts) { + out << getAlertXML(alert) + } + } + } + } + } + + def delete(params) { + def ids = params.get("id")*.toInteger() + def failureXml = null + if (ids == null) { + failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, + "Required parameter id not given") + } else { + for (id in ids) { + def alert = getAlertById(id) + if (!alert) { + failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Unable to find alert with id = " + id) + } else if (!canManageAlerts(alert.definition.resource)) { + failureXml = getFailureXML(ErrorCode.PERMISSION_DENIED) + } + } + + if (!failureXml) { + try { + // TODO: Add to AlertCategory + aMan.deleteAlerts(ids as Integer[]) + } catch (Exception e) { + failureXml = getFailureXML(ErrorCode.UNEXPECTED_ERROR, + e.getMessage()) + } + } + } + + renderXml() { + StatusResponse() { + if (failureXml) { + out << failureXml + } else { + out << getSuccessXML() + } + } + } + } +} \ No newline at end of file diff --git a/hqu/hqapi1/app/MetricdataController.groovy b/hqu/hqapi1/app/MetricdataController.groovy new file mode 100644 index 00000000..2e7cc63a --- /dev/null +++ b/hqu/hqapi1/app/MetricdataController.groovy @@ -0,0 +1,67 @@ +import org.hyperic.hq.hqapi1.ErrorCode; + +import org.hyperic.hq.measurement.server.session.MeasurementStartupListener as MListener +import org.hyperic.hq.measurement.server.session.DataPoint as DP + +class MetricdataController extends ApiController { + + def put(params) { + def inserter = null + def failureXml = null + + def dataRequest = new XmlParser().parseText(getPostData()) + def metricId = dataRequest.'@metricId'?.toInteger() + + def metric = metricHelper.findMeasurementById(metricId) + if (!metric) { + failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Unable to find metric with id = " + + metricId) + } else { + try { + def points = [] + for (dp in dataRequest["DataPoint"]) { + long ts = dp.'@timestamp'?.toLong() + double val = dp.'@value'?.toDouble() + points << createDataPoint(metric, val, ts) + } + log.info("Inserting " + points.size() + " metrics for " + metric.template.name) + + if (metric.getTemplate().isAvailability()) { + inserter = MListener.availDataInserter + } else { + inserter = MListener.dataInserter + } + inserter.insertMetrics(points) + } catch (IllegalArgumentException ia) { + failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, + ia.getMessage()) + } catch (Exception e) { + failureXml = getFailureXML(ErrorCode.UNEXPECTED_ERROR, + "Error inserting metrics: " + + e.getMessage()) + log.warn("Error inserting metrics", e) + } + } + + renderXml() { + StatusResponse() { + if (failureXml) { + out << failureXml + } else { + out << getSuccessXML() + } + } + } + } + + private createDataPoint(metric, value, timestamp) { + if (metric.getTemplate().isAvailability()) { + if (value != 0.0 && value != 1.0 && value != -0.01) { + throw new IllegalArgumentException("Invalid availability data point: " + value) + } + } + + return new DP(metric.id, value, timestamp) + } +} \ No newline at end of file diff --git a/src/org/hyperic/hq/hqapi1/AlertApi.java b/src/org/hyperic/hq/hqapi1/AlertApi.java new file mode 100644 index 00000000..6e5bd8a1 --- /dev/null +++ b/src/org/hyperic/hq/hqapi1/AlertApi.java @@ -0,0 +1,335 @@ +/* + * + * NOTE: This copyright does *not* cover user programs that use HQ + * program services by normal system calls through the application + * program interfaces provided as part of the Hyperic Plug-in Development + * Kit or the Hyperic Client Development Kit - this is merely considered + * normal use of the program, and does *not* fall under the heading of + * "derived work". + * + * Copyright (C) [2008, 2009], Hyperic, Inc. + * This file is part of HQ. + * + * HQ is free software; you can redistribute it and/or modify + * it under the terms version 2 of the GNU General Public License as + * published by the Free Software Foundation. This program is distributed + * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA. + * + */ + +package org.hyperic.hq.hqapi1; + +import org.hyperic.hq.hqapi1.types.AgentResponse; +import org.hyperic.hq.hqapi1.types.Resource; +import org.hyperic.hq.hqapi1.types.StatusResponse; +import org.hyperic.hq.hqapi1.types.AlertsResponse; +import org.hyperic.hq.hqapi1.types.AlertResponse; + +import java.io.IOException; +import java.util.Map; +import java.util.HashMap; + +/** + * The Hyperic HQ Alert API. + *

+ * This class provides access to the alerts within the HQ system. Each of the + * methods in this class return {@link org.hyperic.hq.hqapi1.types.Response} + * objects that wrap the result of the method with a + * {@link org.hyperic.hq.hqapi1.types.ResponseStatus} and a + * {@link org.hyperic.hq.hqapi1.types.ServiceError} that indicates the error + * if the response status is {@link org.hyperic.hq.hqapi1.types.ResponseStatus#FAILURE}. + */ +public class AlertApi extends BaseApi { + + AlertApi(HQConnection conn) { + super(conn); + } + + /** + * Get an Alert + * + * @param alertId The id of the Alert to get. + * + * @return On {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS}, + * the Alert requested + * + * @throws IOException If a network error occurs while making the request. + */ + public AlertResponse getAlert(Integer alertId) + throws IOException + { + Map params = new HashMap(); + + params.put("id", new String[] { alertId.toString() }); + + return doGet("alert/get.hqu", params, + AlertResponse.class); + } + + /** + * Find Alerts in the system. + * + * @param begin The beginning of the time window in epoch-millis. + * @param end The end of the time window in epoch-millis. + * @param count The maximum number of Alert instances to return. + * @param severity The minimum severity to query. 1 = LOW, 2 = MEDIUM, 3 = HIGH + * @param inEscalation If true, only return Alerts which are in Escalation + * @param notFixed If true, only return Alerts which are not fixed. + * + * @return On {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS}, + * a list of Alerts are returned via + * {@link org.hyperic.hq.hqapi1.types.AlertsResponse#getAlert()}. + * + * @throws IOException If a network error occurs while making the request. + */ + public AlertsResponse findAlerts(long begin, long end, int count, + int severity, Boolean inEscalation, + Boolean notFixed) + throws IOException + { + Map params = new HashMap(); + params.put("begin", new String[] { Long.toString(begin)}); + params.put("end", new String[] { Long.toString(end)}); + params.put("count", new String[] { Integer.toString(count)}); + params.put("severity", new String[] { Integer.toString(severity)}); + + if (inEscalation != null) { + params.put("inEscalation", new String[] { Boolean.toString(inEscalation)}); + } + if (notFixed != null) { + params.put("notFixed", new String[] { Boolean.toString(notFixed)}); + } + + return doGet("alert/find.hqu", params, + AlertsResponse.class); + } + + /** + * Find Alerts for the given Resource. + * + * @param r The Resource to query for alerts. + * @param begin The beginning of the time window in epoch-millis. + * @param end The end of the time window in epoch-millis. + * @param count The maximum number of Alert instances to return. + * @param severity The minimum severity to query. 1 = LOW, 2 = MEDIUM, 3 = HIGH + * @param inEscalation If true, only return Alerts which are in Escalation + * @param notFixed If true, only return Alerts which are not fixed. + * + * @return On {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS}, + * a list of Alerts are returned via + * {@link org.hyperic.hq.hqapi1.types.AlertsResponse#getAlert()}. + * + * @throws IOException If a network error occurs while making the request. + */ + public AlertsResponse findAlerts(Resource r, long begin, long end, + int count, int severity, + Boolean inEscalation, + Boolean notFixed) + throws IOException + { + Map params = new HashMap(); + params.put("resourceId", new String[] { Integer.toString(r.getId())}); + params.put("begin", new String[] { Long.toString(begin)}); + params.put("end", new String[] { Long.toString(end)}); + params.put("count", new String[] { Integer.toString(count)}); + params.put("severity", new String[] {Integer.toString(severity)}); + + if (inEscalation != null) { + params.put("inEscalation", new String[] { Boolean.toString(inEscalation)}); + } + if (notFixed != null) { + params.put("notFixed", new String[] { Boolean.toString(notFixed)}); + } + + return doGet("alert/findByResource.hqu", params, + AlertsResponse.class); + } + + /** + * Fix an Alert + * + * @param alertId The id of the Alert to fix. + * + * @return {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS}, + * if the Alert was successfully fixed. + * + * @throws IOException If a network error occurs while making the request. + */ + public AlertResponse fixAlert(Integer alertId) + throws IOException + { + return fixAlert(alertId, ""); + } + + /** + * Fix an Alert + * + * @param alertId The id of the Alert to fix. + * @param reason The reason for the fix. + * + * @return {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS}, + * if the Alert was successfully fixed. + * + * @throws IOException If a network error occurs while making the request. + */ + public AlertResponse fixAlert(Integer alertId, String reason) + throws IOException + { + AlertsResponse response = fixAlerts(new Integer[] { alertId }, reason); + + AlertResponse res = new AlertResponse(); + res.setStatus(response.getStatus()); + res.setError(response.getError()); + if (response.getAlert().size() == 1) { + res.setAlert(response.getAlert().get(0)); + } + return res; + } + + /** + * Fix multiple Alerts + * + * @param alertIds An array of Alert id's to fix. + * + * @return {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS}, + * if the Alert was successfully fixed. + * + * @throws IOException If a network error occurs while making the request. + */ + public AlertsResponse fixAlerts(Integer[] alertIds) + throws IOException + { + return fixAlerts(alertIds, ""); + } + + /** + * Fix multiple Alerts + * + * @param alertIds An array of Alert id's to fix. + * @param reason The reason for the fix. + * + * @return {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS}, + * if the Alert was successfully fixed. + * + * @throws IOException If a network error occurs while making the request. + */ + public AlertsResponse fixAlerts(Integer[] alertIds, String reason) + throws IOException + { + Map params = new HashMap(); + String[] ids = new String[alertIds.length]; + for (int i = 0; i < alertIds.length; i++) { + ids[i] = Integer.toString(alertIds[i]); + } + + params.put("id", ids); + params.put("reason", new String[] { reason }); + + return doGet("alert/fix.hqu", params, + AlertsResponse.class); + } + + /** + * Acknowledge an Alert + * + * @param alertId The id of the Alert to acknowledge. + * @param reason The reason for acknowledgement. + * @param pause If not null, pause the Escalation for the specified number + * of milliseconds. + * + * @return {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS}, + * if the Alert was successfully acknowledged. + * + * @throws IOException If a network error occurs while making the request. + */ + public AlertResponse ackAlert(Integer alertId, String reason, Long pause) + throws IOException + { + AlertsResponse response = ackAlerts(new Integer[] { alertId }, reason, pause); + + AlertResponse res = new AlertResponse(); + res.setStatus(response.getStatus()); + res.setError(response.getError()); + if (response.getAlert().size() == 1) { + res.setAlert(response.getAlert().get(0)); + } + return res; + } + + /** + * Acknowledge multiple Alerts + * + * @param alertIds An array of Alert id's to acknowledge. + * @param reason The reason for acknowledgement. + * @param pause If not null, pause the Escalation for the specified number + * of milliseconds. + * + * @return {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS}, + * if the Alert was successfully acknowledged. + * + * @throws IOException If a network error occurs while making the request. + */ + public AlertsResponse ackAlerts(Integer[] alertIds, String reason, Long pause) + throws IOException + { + Map params = new HashMap(); + String[] ids = new String[alertIds.length]; + for (int i = 0; i < alertIds.length; i++) { + ids[i] = Integer.toString(alertIds[i]); + } + params.put("id", ids); + params.put("reason", new String[] { reason }); + params.put("pause", new String[] { Long.toString(pause)}); + + return doGet("alert/ack.hqu", params, + AlertsResponse.class); + } + + /** + * Delete an Alert + * + * @param alertId An array of Alert id's to delete. + * + * @return {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS}, + * if the Alert was successfully deleted. + * + * @throws IOException If a network error occurs while making the request. + */ + public StatusResponse delete(Integer alertId) + throws IOException + { + return delete(new Integer[] { alertId}); + } + + /** + * Delete multiple Alerts + * + * @param alertIds An array of Alert id's to delete. + * + * @return {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS}, + * if the Alert was successfully deleted. + * + * @throws IOException If a network error occurs while making the request. + */ + public StatusResponse delete(Integer[] alertIds) + throws IOException + { + Map params = new HashMap(); + String[] ids = new String[alertIds.length]; + for (int i = 0; i < alertIds.length; i++) { + ids[i] = Integer.toString(alertIds[i]); + } + params.put("id", ids); + + return doGet("alert/delete.hqu", params, + StatusResponse.class); + } +} diff --git a/src/org/hyperic/hq/hqapi1/HQApi.java b/src/org/hyperic/hq/hqapi1/HQApi.java index ac5f70d4..8147dd64 100644 --- a/src/org/hyperic/hq/hqapi1/HQApi.java +++ b/src/org/hyperic/hq/hqapi1/HQApi.java @@ -45,6 +45,8 @@ public class HQApi { private final AlertDefinitionApi _alertDefinitionApi; private final MaintenanceApi _maintenanceApi; private final ResourceEdgeApi _resourceEdgeApi; + private final AlertApi _alertApi; + private final MetricDataApi _metricDataApi; private final ApplicationApi _applApi; /** @@ -69,6 +71,8 @@ public HQApi(String host, int port, boolean isSecure, String user, _alertDefinitionApi = new AlertDefinitionApi(connection); _maintenanceApi = new MaintenanceApi(connection); _resourceEdgeApi = new ResourceEdgeApi(connection); + _alertApi = new AlertApi(connection); + _metricDataApi = new MetricDataApi(connection); _applApi = new ApplicationApi(connection); } @@ -117,6 +121,15 @@ public MetricApi getMetricApi() { return _metricApi; } + /** + * Import Metric data + * + * @return The API for querying or reporting Metric information. + */ + public MetricDataApi getMetricDataApi() { + return _metricDataApi; + } + /** * Add, remove and assign escalations. * @@ -171,6 +184,15 @@ public ResourceEdgeApi getResourceEdgeApi() { return _resourceEdgeApi; } + /** + * Manage Alerts + * + * @return The API for finding and managing Alerts. + */ + public AlertApi getAlertApi() { + return _alertApi; + } + /** * List, create and update Applications. * diff --git a/src/org/hyperic/hq/hqapi1/HQConnection.java b/src/org/hyperic/hq/hqapi1/HQConnection.java index ba5306a0..da59658c 100644 --- a/src/org/hyperic/hq/hqapi1/HQConnection.java +++ b/src/org/hyperic/hq/hqapi1/HQConnection.java @@ -153,7 +153,7 @@ T doGet(String path, Map params, Class resultClass) for (String val : vals) { if (val != null) { - if (append) { + if (idx > 0 || append) { uri.append("&"); } uri.append(key).append("=").append(urlEncode(val)); diff --git a/src/org/hyperic/hq/hqapi1/MetricDataApi.java b/src/org/hyperic/hq/hqapi1/MetricDataApi.java new file mode 100644 index 00000000..86a43a49 --- /dev/null +++ b/src/org/hyperic/hq/hqapi1/MetricDataApi.java @@ -0,0 +1,49 @@ +package org.hyperic.hq.hqapi1; + +import org.hyperic.hq.hqapi1.types.DataPoint; +import org.hyperic.hq.hqapi1.types.DataPointsRequest; +import org.hyperic.hq.hqapi1.types.StatusResponse; +import org.hyperic.hq.hqapi1.types.Metric; + +import java.io.IOException; +import java.util.List; + +/** + * The Hyperic HQ MetricData API. + *

+ * This class provides access to the @{link MetricData} within the HQ system. Each of the + * methods in this class return {@link org.hyperic.hq.hqapi1.types.Response} + * objects that wrap the result of the method with a + * {@link org.hyperic.hq.hqapi1.types.ResponseStatus} and a + * {@link org.hyperic.hq.hqapi1.types.ServiceError} that indicates the error + * if the response status is {@link org.hyperic.hq.hqapi1.types.ResponseStatus#FAILURE}. + */ +public class MetricDataApi extends BaseApi { + + MetricDataApi(HQConnection conn) { + super(conn); + } + + /** + * Insert {@link org.hyperic.hq.hqapi1.types.DataPoint}s for the specified + * Metric. + * + * @param metric The Metric to insert data for. + * @param data A List of {@link org.hyperic.hq.hqapi1.types.DataPoint}s to insert. + * + * @return {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS} if the + * data was sucessfully inserted. + * + * @throws IOException If a network error occurs while making the request. + */ + public StatusResponse addData(Metric metric, List data) + throws IOException + { + DataPointsRequest request = new DataPointsRequest(); + request.setMetricId(metric.getId()); + request.getDataPoint().addAll(data); + + return doPost("metricData/put.hqu", request, + StatusResponse.class); + } +} diff --git a/src/org/hyperic/hq/hqapi1/test/AlertDefinitionSyncControlAction_test.java b/src/org/hyperic/hq/hqapi1/test/AlertDefinitionSyncControlAction_test.java index 59e88262..29a93275 100644 --- a/src/org/hyperic/hq/hqapi1/test/AlertDefinitionSyncControlAction_test.java +++ b/src/org/hyperic/hq/hqapi1/test/AlertDefinitionSyncControlAction_test.java @@ -27,34 +27,6 @@ public AlertDefinitionSyncControlAction_test(String name) { super(name); } - // TODO: Copied from ControlTestBase in master branch. - private Resource createControllableResource(HQApi api) - throws Exception - { - ResourceApi rApi = api.getResourceApi(); - - ResourcePrototypeResponse protoResponse = - rApi.getResourcePrototype("FileServer File"); - hqAssertSuccess(protoResponse); - - Resource localPlatform = getLocalPlatformResource(false, false); - - Map config = new HashMap(); - // TODO: Fix for windows - config.put("path", "/usr/bin/true"); - - Random r = new Random(); - String name = "Controllable-Resource-" + r.nextInt(); - - ResourceResponse resourceCreateResponse = - rApi.createService(protoResponse.getResourcePrototype(), - localPlatform, name, config); - - hqAssertSuccess(resourceCreateResponse); - - return resourceCreateResponse.getResource(); - } - public void testAddRemoveControlAction() throws Exception { HQApi api = getApi(); AlertDefinitionApi defApi = api.getAlertDefinitionApi(); diff --git a/src/org/hyperic/hq/hqapi1/test/AlertDefinitionTestBase.java b/src/org/hyperic/hq/hqapi1/test/AlertDefinitionTestBase.java index 0e1b2631..af76a967 100644 --- a/src/org/hyperic/hq/hqapi1/test/AlertDefinitionTestBase.java +++ b/src/org/hyperic/hq/hqapi1/test/AlertDefinitionTestBase.java @@ -30,9 +30,11 @@ import org.hyperic.hq.hqapi1.AlertDefinitionApi; import org.hyperic.hq.hqapi1.AlertDefinitionBuilder.AlertPriority; import org.hyperic.hq.hqapi1.types.AlertDefinition; +import org.hyperic.hq.hqapi1.types.AlertDefinitionsResponse; import org.hyperic.hq.hqapi1.types.StatusResponse; import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.Random; @@ -53,6 +55,30 @@ protected AlertDefinition generateTestDefinition() { return d; } + protected AlertDefinition syncAlertDefinition(AlertDefinition d) + throws IOException { + + return syncAlertDefinitions(Collections.singletonList(d)).get(0); + } + + protected List syncAlertDefinitions(List definitions) + throws IOException { + + AlertDefinitionApi defApi = getApi().getAlertDefinitionApi(); + + AlertDefinitionsResponse response = defApi.syncAlertDefinitions(definitions); + hqAssertSuccess(response); + int expectedSize = definitions.size(); + assertEquals("Should have found " + expectedSize + " alert definitions from sync", + expectedSize, response.getAlertDefinition().size()); + + for (AlertDefinition d : response.getAlertDefinition()) { + validateDefinition(d); + } + + return response.getAlertDefinition(); + } + protected void validateDefinition(AlertDefinition d) { assertNotNull(d.getName()); assertTrue("Invalid frequency " + d.getFrequency(), diff --git a/src/org/hyperic/hq/hqapi1/test/AlertDelete_test.java b/src/org/hyperic/hq/hqapi1/test/AlertDelete_test.java new file mode 100644 index 00000000..3762d2dd --- /dev/null +++ b/src/org/hyperic/hq/hqapi1/test/AlertDelete_test.java @@ -0,0 +1,64 @@ +package org.hyperic.hq.hqapi1.test; + +import org.hyperic.hq.hqapi1.types.Alert; +import org.hyperic.hq.hqapi1.types.StatusResponse; +import org.hyperic.hq.hqapi1.types.Resource; +import org.hyperic.hq.hqapi1.types.User; +import org.hyperic.hq.hqapi1.AlertApi; + +import java.util.List; + +public class AlertDelete_test extends AlertTestBase { + + public AlertDelete_test(String name) { + super(name); + } + + public void testDeleteAlert() throws Exception { + AlertApi api = getAlertApi(); + Resource platform = getLocalPlatformResource(false, false); + Alert a = generateAlerts(platform); + + validateAlert(a); + + // Test delete + StatusResponse deleteResponse = api.delete(a.getId()); + hqAssertSuccess(deleteResponse); + + // TODO: Valididate alert was deleted? Will require a getById API. + + // Cleanup + deleteAlertDefinitionByAlert(a); + } + + public void testDeleteAlertNoPermission() throws Exception { + AlertApi api = getAlertApi(); + Resource platform = getLocalPlatformResource(false, false); + Alert a = generateAlerts(platform); + + validateAlert(a); + + // Test delete with an unprivledged user + + List users = createTestUsers(1); + User unprivUser = users.get(0); + AlertApi apiUnpriv = getApi(unprivUser.getName(), TESTUSER_PASSWORD).getAlertApi(); + + StatusResponse deleteResponse = apiUnpriv.delete(a.getId()); + hqAssertFailurePermissionDenied(deleteResponse); + + // TODO: Valididate alert was deleted? Will require a getById API. + + // Cleanup + deleteAlertDefinitionByAlert(a); + deleteTestUsers(users); + } + + public void testDeleteInvalidAlert() throws Exception { + + AlertApi api = getAlertApi(); + + StatusResponse response = api.delete(Integer.MAX_VALUE); + hqAssertFailureObjectNotFound(response); + } +} diff --git a/src/org/hyperic/hq/hqapi1/test/AlertFindByResource_test.java b/src/org/hyperic/hq/hqapi1/test/AlertFindByResource_test.java new file mode 100644 index 00000000..a1d76da3 --- /dev/null +++ b/src/org/hyperic/hq/hqapi1/test/AlertFindByResource_test.java @@ -0,0 +1,130 @@ +package org.hyperic.hq.hqapi1.test; + +import org.hyperic.hq.hqapi1.types.AlertsResponse; +import org.hyperic.hq.hqapi1.types.Resource; +import org.hyperic.hq.hqapi1.types.Alert; +import org.hyperic.hq.hqapi1.types.User; +import org.hyperic.hq.hqapi1.AlertApi; + +import java.util.List; + +public class AlertFindByResource_test extends AlertTestBase { + + public AlertFindByResource_test(String name) { + super(name); + } + + public void testFindValid() throws Exception { + AlertApi api = getAlertApi(); + Resource r = getLocalPlatformResource(false, false); + Alert a = generateAlerts(r); + + AlertsResponse response = api.findAlerts(r, 0, System.currentTimeMillis(), + 10, 1, false, false); + hqAssertSuccess(response); + assertTrue(response.getAlert().size() <= 10); + assertTrue(response.getAlert().size() > 0); + + for (Alert alerts : response.getAlert()) { + validateAlert(alerts); + } + + // Cleanup + deleteAlertDefinitionByAlert(a); + } + + public void testFindValidNoPermission() throws Exception { + Resource r = getLocalPlatformResource(false, false); + Alert a = generateAlerts(r); + + List users = createTestUsers(1); + User unprivUser = users.get(0); + AlertApi apiUnpriv = getApi(unprivUser.getName(), TESTUSER_PASSWORD).getAlertApi(); + + AlertsResponse response = apiUnpriv.findAlerts(r, 0, System.currentTimeMillis(), + 10, 1, false, false); + hqAssertSuccess(response); + assertTrue(response.getAlert().size() == 0); + + // Cleanup + deleteAlertDefinitionByAlert(a); + deleteTestUsers(users); + } + + public void testFindValidSmallRange() throws Exception { + AlertApi api = getAlertApi(); + Resource r = getLocalPlatformResource(false, false); + Alert a = generateAlerts(r); + + // Issue find 1 ms < a.ctime and 1 ms > a.ctime + long start = a.getCtime() - 1; + long end = a.getCtime() + 1; + + AlertsResponse response = api.findAlerts(r, start, end, + 10, 1, false, false); + hqAssertSuccess(response); + assertTrue(response.getAlert().size() <= 10); + assertTrue(response.getAlert().size() > 0); + + for (Alert alerts : response.getAlert()) { + validateAlert(alerts); + } + + // Cleanup + deleteAlertDefinitionByAlert(a); + } + + public void testFindSmallRangeNoAlerts() throws Exception { + AlertApi api = getAlertApi(); + Resource r = getLocalPlatformResource(false, false); + Alert a = generateAlerts(r); + + // Issue find 1 ms > a.ctime and 2 ms > a.ctime + long start = a.getCtime() + 1; + long end = a.getCtime() + 2; + + AlertsResponse response = api.findAlerts(r, start, end, + 10, 1, false, false); + hqAssertSuccess(response); + assertTrue(response.getAlert().size() == 0); + + // Cleanup + deleteAlertDefinitionByAlert(a); + } + + public void testFindInvalidSeverity() throws Exception { + AlertApi api = getAlertApi(); + Resource r = getLocalPlatformResource(false, false); + AlertsResponse response = api.findAlerts(r, 0, System.currentTimeMillis(), + 10, 4, false, false); + hqAssertFailureInvalidParameters(response); + } + + public void testFindInvalidCount() throws Exception { + AlertApi api = getAlertApi(); + Resource r = getLocalPlatformResource(false, false); + AlertsResponse response = api.findAlerts(r, 0, System.currentTimeMillis(), + -5, 2, false, false); + hqAssertFailureInvalidParameters(response); + } + + public void testFindInvalidRange() throws Exception { + AlertApi api = getAlertApi(); + Resource r = getLocalPlatformResource(false, false); + + AlertsResponse response = api.findAlerts(r, System.currentTimeMillis(), 0, + 10, 2, false, false); + hqAssertFailureInvalidParameters(response); + } + + public void testFindInvalidResource() throws Exception { + AlertApi api = getAlertApi(); + + Resource r = new Resource(); + r.setId(Integer.MAX_VALUE); + r.setName("Invalid resource"); + AlertsResponse response = api.findAlerts(r, 0, System.currentTimeMillis(), + 10, 1, false, false); + hqAssertFailureObjectNotFound(response); + } +} diff --git a/src/org/hyperic/hq/hqapi1/test/AlertFind_test.java b/src/org/hyperic/hq/hqapi1/test/AlertFind_test.java new file mode 100644 index 00000000..c74c0f6e --- /dev/null +++ b/src/org/hyperic/hq/hqapi1/test/AlertFind_test.java @@ -0,0 +1,109 @@ +package org.hyperic.hq.hqapi1.test; + +import org.hyperic.hq.hqapi1.AlertApi; +import org.hyperic.hq.hqapi1.types.AlertsResponse; +import org.hyperic.hq.hqapi1.types.Alert; +import org.hyperic.hq.hqapi1.types.Resource; +import org.hyperic.hq.hqapi1.types.User; + +import java.util.List; + +public class AlertFind_test extends AlertTestBase { + + public AlertFind_test(String name) { + super(name); + } + + public void testFindValid() throws Exception { + AlertApi api = getAlertApi(); + Resource platform = getLocalPlatformResource(false, false); + Alert a = generateAlerts(platform); + + AlertsResponse response = api.findAlerts(0, System.currentTimeMillis(), + 10, 1, false, false); + hqAssertSuccess(response); + assertTrue(response.getAlert().size() <= 10); + assertTrue(response.getAlert().size() > 0); + + for (Alert alerts : response.getAlert()) { + validateAlert(alerts); + } + + // Cleanup + deleteAlertDefinitionByAlert(a); + } + + public void testFindValidNoPermission() throws Exception { + Resource platform = getLocalPlatformResource(false, false); + Alert a = generateAlerts(platform); + + List users = createTestUsers(1); + User unprivUser = users.get(0); + AlertApi apiUnpriv = getApi(unprivUser.getName(), TESTUSER_PASSWORD).getAlertApi(); + + AlertsResponse response = apiUnpriv.findAlerts(0, System.currentTimeMillis(), + 10, 1, false, false); + hqAssertSuccess(response); + assertTrue(response.getAlert().size() == 0); + + // Cleanup + deleteAlertDefinitionByAlert(a); + deleteTestUsers(users); + } + + public void testFindValidSmallRange() throws Exception { + AlertApi api = getAlertApi(); + Resource platform = getLocalPlatformResource(false, false); + Alert a = generateAlerts(platform); + + long start = a.getCtime() - 1; + long end = a.getCtime() + 1; + + AlertsResponse response = api.findAlerts(start, end, + 10, 1, false, false); + hqAssertSuccess(response); + assertTrue(response.getAlert().size() <= 10); + assertTrue(response.getAlert().size() > 0); + + for (Alert alerts : response.getAlert()) { + validateAlert(alerts); + } + + // Cleanup + deleteAlertDefinitionByAlert(a); + } + + public void testFindValidSmallRangeNoAlerts() throws Exception { + AlertApi api = getAlertApi(); + + // Issue a find slightly into the future, should return 0 alerts + long start = System.currentTimeMillis() + 100; + long end = start + 1; + + AlertsResponse response = api.findAlerts(start, end, + 10, 1, false, false); + hqAssertSuccess(response); + assertTrue(response.getAlert().size() == 0); + } + + public void testFindInvalidSeverity() throws Exception { + AlertApi api = getAlertApi(); + AlertsResponse response = api.findAlerts(0, System.currentTimeMillis(), + 10, 4, false, false); + hqAssertFailureInvalidParameters(response); + } + + public void testFindInvalidCount() throws Exception { + AlertApi api = getAlertApi(); + AlertsResponse response = api.findAlerts(0, System.currentTimeMillis(), + -5, 2, false, false); + hqAssertFailureInvalidParameters(response); + } + + public void testFindInvalidRange() throws Exception { + AlertApi api = getAlertApi(); + AlertsResponse response = api.findAlerts(System.currentTimeMillis(), 0, + 10, 2, false, false); + hqAssertFailureInvalidParameters(response); + } +} diff --git a/src/org/hyperic/hq/hqapi1/test/AlertFix_test.java b/src/org/hyperic/hq/hqapi1/test/AlertFix_test.java new file mode 100644 index 00000000..ecd5fce1 --- /dev/null +++ b/src/org/hyperic/hq/hqapi1/test/AlertFix_test.java @@ -0,0 +1,169 @@ +package org.hyperic.hq.hqapi1.test; + +import org.hyperic.hq.hqapi1.types.Alert; +import org.hyperic.hq.hqapi1.types.AlertActionLog; +import org.hyperic.hq.hqapi1.types.AlertResponse; +import org.hyperic.hq.hqapi1.types.Group; +import org.hyperic.hq.hqapi1.types.Operation; +import org.hyperic.hq.hqapi1.types.Resource; +import org.hyperic.hq.hqapi1.types.ResourcePrototypeResponse; +import org.hyperic.hq.hqapi1.types.ResourcesResponse; +import org.hyperic.hq.hqapi1.types.Role; +import org.hyperic.hq.hqapi1.types.User; +import org.hyperic.hq.hqapi1.AlertApi; +import org.hyperic.hq.hqapi1.ResourceApi; + +import java.util.Collections; +import java.util.List; + +public class AlertFix_test extends AlertTestBase { + + public AlertFix_test(String name) { + super(name); + } + + public void testFixAlert() throws Exception { + AlertApi api = getAlertApi(); + Resource platform = getLocalPlatformResource(false, false); + Alert a = generateAlerts(platform); + + validateAlert(a); + + // Test marking fixed + AlertResponse fixResponse = api.fixAlert(a.getId()); + hqAssertSuccess(fixResponse); + a = fixResponse.getAlert(); + List logs = a.getAlertActionLog(); + + assertTrue("Alert was not fixed!", a.isFixed()); + assertTrue("Alert action log for the fix is missing", + logs.size() > 0); + assertTrue("Expecting an alert action log containing 'Fixed by'", + logs.get(0).getDetail().indexOf("Fixed by") > -1 ); + + // Cleanup + deleteAlertDefinitionByAlert(a); + } + + public void testFixAlertWithReason() throws Exception { + AlertApi api = getAlertApi(); + Resource platform = getLocalPlatformResource(false, false); + Alert a = generateAlerts(platform); + + validateAlert(a); + + // Test marking fixed + String REASON = "HQApi Alert Fix Test"; + AlertResponse fixResponse = api.fixAlert(a.getId(), REASON); + hqAssertSuccess(fixResponse); + a = fixResponse.getAlert(); + List logs = a.getAlertActionLog(); + + assertTrue("Alert was not fixed!", a.isFixed()); + assertTrue("Alert action log for the fix is missing", + logs.size() > 0); + assertEquals("Wrong reason for the alert fix", + REASON, logs.get(0).getDetail()); + + // Cleanup + deleteAlertDefinitionByAlert(a); + } + + public void testFixPlatformAlertNoPermission() throws Exception { + Resource platform = getLocalPlatformResource(false, false); + Alert a = generateAlerts(platform); + + validateAlert(a); + + List users = createTestUsers(1); + User unprivUser = users.get(0); + AlertApi apiUnpriv = getApi(unprivUser.getName(), TESTUSER_PASSWORD).getAlertApi(); + + // Test marking fixed with an unprivlidged user + AlertResponse fixResponse = apiUnpriv.fixAlert(a.getId()); + hqAssertFailurePermissionDenied(fixResponse); + + + // Cleanup + deleteAlertDefinitionByAlert(a); + deleteTestUsers(users); + } + + public void testFixServerAlertNoPermission() throws Exception { + ResourceApi rApi = getApi().getResourceApi(); + + ResourcePrototypeResponse protoResponse = + rApi.getResourcePrototype("HQ Agent"); + hqAssertSuccess(protoResponse); + + ResourcesResponse resourcesResponse = + rApi.getResources(protoResponse.getResourcePrototype(), + false, false); + hqAssertSuccess(resourcesResponse); + + assertTrue("No resources of type " + + protoResponse.getResourcePrototype().getName() + + " could be found!", resourcesResponse.getResource().size() > 0); + + Resource server = resourcesResponse.getResource().get(0); + + Alert a = generateAlerts(server); + + validateAlert(a); + + List users = createTestUsers(1); + User unprivUser = users.get(0); + AlertApi apiUnpriv = getApi(unprivUser.getName(), TESTUSER_PASSWORD).getAlertApi(); + + // Test marking fixed with an unprivlidged user + AlertResponse fixResponse = apiUnpriv.fixAlert(a.getId()); + hqAssertFailurePermissionDenied(fixResponse); + + // Cleanup + deleteAlertDefinitionByAlert(a); + deleteTestUsers(users); + } + + public void testFixServiceAlertNoPermission() throws Exception { + AlertApi api = getAlertApi(); + ResourceApi rApi = getApi().getResourceApi(); + + ResourcePrototypeResponse protoResponse = + rApi.getResourcePrototype("FileServer Mount"); + hqAssertSuccess(protoResponse); + + ResourcesResponse resourcesResponse = + rApi.getResources(protoResponse.getResourcePrototype(), + false, false); + hqAssertSuccess(resourcesResponse); + + assertTrue("No resources of type " + + protoResponse.getResourcePrototype().getName() + + " could be found!", resourcesResponse.getResource().size() > 0); + + Resource service = resourcesResponse.getResource().get(0); + + Alert a = generateAlerts(service); + validateAlert(a); + + List users = createTestUsers(1); + User unprivUser = users.get(0); + AlertApi apiUnpriv = getApi(unprivUser.getName(), TESTUSER_PASSWORD).getAlertApi(); + + // Test marking fixed with an unprivlidged user + AlertResponse fixResponse = apiUnpriv.fixAlert(a.getId()); + hqAssertFailurePermissionDenied(fixResponse); + + // Cleanup + deleteAlertDefinitionByAlert(a); + deleteTestUsers(users); + } + + public void testFixInvalidAlert() throws Exception { + + AlertApi api = getAlertApi(); + + AlertResponse response = api.fixAlert(Integer.MAX_VALUE); + hqAssertFailureObjectNotFound(response); + } +} diff --git a/src/org/hyperic/hq/hqapi1/test/AlertTestBase.java b/src/org/hyperic/hq/hqapi1/test/AlertTestBase.java new file mode 100644 index 00000000..bba5f780 --- /dev/null +++ b/src/org/hyperic/hq/hqapi1/test/AlertTestBase.java @@ -0,0 +1,192 @@ +package org.hyperic.hq.hqapi1.test; + +import org.hyperic.hq.hqapi1.AlertApi; +import org.hyperic.hq.hqapi1.AlertDefinitionBuilder; +import org.hyperic.hq.hqapi1.AlertDefinitionApi; +import org.hyperic.hq.hqapi1.HQApi; +import org.hyperic.hq.hqapi1.EscalationApi; +import org.hyperic.hq.hqapi1.EscalationActionBuilder; +import org.hyperic.hq.hqapi1.MetricDataApi; +import org.hyperic.hq.hqapi1.types.AlertDefinition; +import org.hyperic.hq.hqapi1.types.Resource; +import org.hyperic.hq.hqapi1.types.StatusResponse; +import org.hyperic.hq.hqapi1.types.AlertDefinitionsResponse; +import org.hyperic.hq.hqapi1.types.AlertResponse; +import org.hyperic.hq.hqapi1.types.AlertsResponse; +import org.hyperic.hq.hqapi1.types.Alert; +import org.hyperic.hq.hqapi1.types.Metric; +import org.hyperic.hq.hqapi1.types.DataPoint; +import org.hyperic.hq.hqapi1.types.Escalation; +import org.hyperic.hq.hqapi1.types.EscalationAction; +import org.hyperic.hq.hqapi1.types.EscalationResponse; + +import java.io.IOException; +import java.util.Random; +import java.util.List; +import java.util.ArrayList; + +public abstract class AlertTestBase extends AlertDefinitionTestBase { + + public AlertTestBase(String name) { + super(name); + } + + protected AlertApi getAlertApi() { + return getApi().getAlertApi(); + } + + protected Alert getAlert(Integer alertId) throws IOException { + + AlertResponse response = getAlertApi().getAlert(alertId); + hqAssertSuccess(response); + Alert a = response.getAlert(); + validateAlert(a); + + return a; + } + + protected void validateAlert(Alert a) { + assertNotNull("fixed was NULL", a.isFixed()); + assertTrue("ctime is incorrect", a.getCtime() > 0); + assertTrue("resourceId is invalid", a.getResourceId() > 0); + assertTrue("alertDefinitionId is invalid", a.getAlertDefinitionId() > 0); + assertNotNull("Alert name was null", a.getName()); + assertTrue("Alert id is incorrect", a.getId() > 0); + assertTrue("Empty long reason", a.getReason().length() > 0); + } + + protected Escalation createEscalation() throws Exception { + EscalationAction action = EscalationActionBuilder.createNoOpAction(60000); + return createEscalation(action); + } + + protected Escalation createEscalation(EscalationAction action) throws Exception { + EscalationApi escalationApi = getApi().getEscalationApi(); + + Escalation e = new Escalation(); + Random r = new Random(); + e.setName("Test Escalation" + r.nextInt()); + e.setMaxPauseTime(24 * 60 * 60 * 1000); + e.setRepeat(true); + e.setDescription("Test escalation for Alert tests"); + e.setPauseAllowed(true); + e.getAction().add(action); + + EscalationResponse response = escalationApi.createEscalation(e); + hqAssertSuccess(response); + return response.getEscalation(); + } + + protected void deleteEscalation(Escalation e) throws Exception { + EscalationApi api = getApi().getEscalationApi(); + StatusResponse response = api.deleteEscalation(e.getId()); + hqAssertSuccess(response); + } + + protected Alert generateAlerts(Resource resource) throws Exception { + return generateAlerts(resource, null); + } + + /** + * Setup an AlertDefinition that will fire Alert instances waiting for + * at least 1 alert to be generated. + * + * @param resource The resource to generate Alerts on + * @param e Optional escalation to assign to the AlertDefiniton that is created + * + * @return The Alert that was created. + * @throws Exception If an error occurs generating the alerts + */ + protected Alert generateAlerts(Resource resource, + Escalation e) throws Exception { + + long start = System.currentTimeMillis(); + + // Find availability metric for the passed in resource + Metric availMetric = findAvailabilityMetric(resource); + + // Create alert definition + AlertDefinition d = generateTestDefinition(); + d.setDescription("Definition that will always fire, allowing for testing of Alerts"); + d.setResource(resource); + if (e != null) { + d.setEscalation(e); + } + d.getAlertCondition().add(AlertDefinitionBuilder. + createThresholdCondition(true, availMetric.getName(), + AlertDefinitionBuilder.AlertComparator.GREATER_THAN, -1)); + AlertDefinition def = syncAlertDefinition(d); + + // Insert a fake 'up' measurement + sendAvailabilityDataPoint(resource, 1.0); + + return findAlert(def, start); + } + + protected void sendAvailabilityDataPoint(Resource resource, double availability) + throws IOException { + + MetricDataApi dataApi = getApi().getMetricDataApi(); + + // Find availability metric for the resource + Metric availMetric = findAvailabilityMetric(resource); + + List dataPoints = new ArrayList(); + DataPoint dp = new DataPoint(); + dp.setTimestamp(System.currentTimeMillis()); + dp.setValue(availability); + dataPoints.add(dp); + StatusResponse dataResponse = dataApi.addData(availMetric, dataPoints); + hqAssertSuccess(dataResponse); + } + + protected Alert findAlert(AlertDefinition def, long start) + throws Exception { + + return findAlert(def, false, start); + } + + protected Alert findAlert(AlertDefinition def, + boolean withActionLog, + long start) + throws Exception { + + final int TIMEOUT = 90; + for (int i = 0; i < TIMEOUT; i++) { + // Wait for alerts + AlertsResponse alerts = getAlertApi().findAlerts(def.getResource(), start, + System.currentTimeMillis(), + 10, 1, + (def.getEscalation() != null), + false); + hqAssertSuccess(alerts); + + for (Alert a : alerts.getAlert()) { + // Verify this alert comes from the definition we just created + if (a.getAlertDefinitionId() == def.getId()) { + if (withActionLog + && a.getAlertActionLog().isEmpty()) { + // wait for the next loop iteration so that the + // escalation action has time to be executed + continue; + } + validateAlert(a); + return a; + } + } + + pauseTest(1000); + } + + throw new Exception("Unable to find generated alerts for " + + def.getResource().getName() + " under alert definition " + + def.getName()); + } + + protected void deleteAlertDefinitionByAlert(Alert a) throws Exception { + AlertDefinitionApi api = getApi().getAlertDefinitionApi(); + + StatusResponse response = api.deleteAlertDefinition(a.getAlertDefinitionId()); + hqAssertSuccess(response); + } +} diff --git a/src/org/hyperic/hq/hqapi1/test/HQApiTestBase.java b/src/org/hyperic/hq/hqapi1/test/HQApiTestBase.java index 010efd8b..82769ef1 100644 --- a/src/org/hyperic/hq/hqapi1/test/HQApiTestBase.java +++ b/src/org/hyperic/hq/hqapi1/test/HQApiTestBase.java @@ -7,7 +7,7 @@ * normal use of the program, and does *not* fall under the heading of * "derived work". * - * Copyright (C) [2008, 2009], Hyperic, Inc. + * Copyright (C) [2008-2010], Hyperic, Inc. * This file is part of HQ. * * HQ is free software; you can redistribute it and/or modify @@ -33,20 +33,43 @@ import org.apache.log4j.PropertyConfigurator; import org.hyperic.hq.hqapi1.AgentApi; import org.hyperic.hq.hqapi1.ErrorCode; +import org.hyperic.hq.hqapi1.GroupApi; import org.hyperic.hq.hqapi1.HQApi; +import org.hyperic.hq.hqapi1.MetricApi; import org.hyperic.hq.hqapi1.ResourceApi; +import org.hyperic.hq.hqapi1.RoleApi; +import org.hyperic.hq.hqapi1.UserApi; import org.hyperic.hq.hqapi1.types.Agent; import org.hyperic.hq.hqapi1.types.AgentsResponse; +import org.hyperic.hq.hqapi1.types.Group; +import org.hyperic.hq.hqapi1.types.GroupResponse; +import org.hyperic.hq.hqapi1.types.Metric; +import org.hyperic.hq.hqapi1.types.MetricsResponse; +import org.hyperic.hq.hqapi1.types.Operation; import org.hyperic.hq.hqapi1.types.PingAgentResponse; import org.hyperic.hq.hqapi1.types.Resource; +import org.hyperic.hq.hqapi1.types.ResourcePrototype; import org.hyperic.hq.hqapi1.types.ResourcesResponse; import org.hyperic.hq.hqapi1.types.Response; import org.hyperic.hq.hqapi1.types.ResponseStatus; - +import org.hyperic.hq.hqapi1.types.Role; +import org.hyperic.hq.hqapi1.types.RoleResponse; +import org.hyperic.hq.hqapi1.types.RolesResponse; +import org.hyperic.hq.hqapi1.types.User; +import org.hyperic.hq.hqapi1.types.UserResponse; +import org.hyperic.hq.hqapi1.types.StatusResponse; +import org.hyperic.hq.hqapi1.types.ResourcePrototypeResponse; +import org.hyperic.hq.hqapi1.types.ResourceResponse; + +import java.io.IOException; import java.util.List; import java.util.Properties; +import java.util.Random; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; -public class HQApiTestBase extends TestCase { +public abstract class HQApiTestBase extends TestCase { private static boolean _logConfigured = false; @@ -57,6 +80,16 @@ public class HQApiTestBase extends TestCase { private static final String USER = "hqadmin"; private static final String PASSWORD = "hqadmin"; + static final String TESTUSER_PASSWORD = "apitest"; + static final String TESTUSER_NAME_PREFIX = "apitest"; + static final String TESTUSER_FIRSTNAME = "API"; + static final String TESTUSER_LASTNAME = "Test"; + static final String TESTUSER_EMAIL = "apitest@hyperic.com"; + static final boolean TESTUSER_ACTIVE = true; + + static final String TESTROLE_NAME_PREFIX = "API Test Role "; + static final String TESTROLE_DESCRIPTION = "API Test Role Description"; + private Log _log = LogFactory.getLog(HQApiTestBase.class); private static Agent _localAgent = null; @@ -164,17 +197,299 @@ protected Resource getLocalPlatformResource(boolean verbose, children); hqAssertSuccess(resourceResponse); - List localPlatforms = resourceResponse.getResource(); - if (localPlatforms.size() == 0) { + Resource localPlatform = null; + List invalidPlatforms = new ArrayList(); + invalidPlatforms.add("Network Device"); + invalidPlatforms.add("Network Host"); + + for (Resource r : resourceResponse.getResource()) { + if (!invalidPlatforms.contains(r.getResourcePrototype().getName())) { + localPlatform = r; + break; + } + } + + if (localPlatform == null) { String err = "Unable to find platform associated with agent " + a.getAddress() + ":" + a.getPort(); getLog().error(err); throw new Exception(err); } - return localPlatforms.get(0); + return localPlatform; + } + + public Resource createControllableResource(HQApi api) + throws Exception + { + ResourceApi rApi = api.getResourceApi(); + + ResourcePrototypeResponse protoResponse = + rApi.getResourcePrototype("FileServer File"); + hqAssertSuccess(protoResponse); + + Resource localPlatform = getLocalPlatformResource(false, false); + + Map config = new HashMap(); + if (localPlatform.getResourcePrototype().getName().equals("Win32")) { + config.put("path", "C:\\windows\\system32\\cmd.exe"); + } else { + config.put("path", "/usr/bin/true"); + } + + Random r = new Random(); + String name = "Controllable-Resource-" + r.nextInt(); + + ResourceResponse resourceCreateResponse = + rApi.createService(protoResponse.getResourcePrototype(), + localPlatform, name, config); + + hqAssertSuccess(resourceCreateResponse); + + pauseTest(); + + return resourceCreateResponse.getResource(); + } + + public void cleanupResource(HQApi api, Resource r) + throws Exception + { + pauseTest(); + + ResourceApi rApi = api.getResourceApi(); + + StatusResponse response = rApi.deleteResource(r.getId()); + hqAssertSuccess(response); + } + + protected Group createGroup(List resources) + throws Exception { + + return createGroup(resources, null); + } + + protected Group createGroup(List resources, List roles) + throws Exception { + + // determine whether to create a mixed or compatible group + ResourcePrototype prototype = null; + for (Resource r : resources) { + if (prototype == null) { + prototype = r.getResourcePrototype(); + } else { + if (!prototype.getName().equals(r.getResourcePrototype().getName())) { + prototype = null; + break; + } + } + } + + // create group + Random r = new Random(); + Group g = new Group(); + String name = (prototype == null ? "Mixed" : "Compatible") + + " Group for Tests" + r.nextInt(); + g.setName(name); + if (prototype != null) { + g.setResourcePrototype(prototype); + } + g.getResource().addAll(resources); + GroupResponse groupResponse = getApi().getGroupApi().createGroup(g); + hqAssertSuccess(groupResponse); + Group createdGroup = groupResponse.getGroup(); + assertEquals(resources.size(), createdGroup.getResource().size()); + if (prototype == null) { + assertNull("This should be a mixed group", + createdGroup.getResourcePrototype()); + } else { + assertNotNull("This should be a compatible group", + createdGroup.getResourcePrototype()); + assertEquals(prototype.getName(), + createdGroup.getResourcePrototype().getName()); + } + + if (roles != null) { + createdGroup.getRole().addAll(roles); + + groupResponse = getApi().getGroupApi().updateGroup(createdGroup); + hqAssertSuccess(groupResponse); + + createdGroup = groupResponse.getGroup(); + + assertEquals(roles.size(), createdGroup.getRole().size()); + } + + return createdGroup; + } + + /** + * Generate a valid User object that's guaranteed to have a unique Name + * @return A valid User object. + */ + public User generateTestUser() { + + Random r = new Random(); + + User user = new User(); + user.setName(TESTUSER_NAME_PREFIX + r.nextInt()); + user.setFirstName(TESTUSER_FIRSTNAME); + user.setLastName(TESTUSER_LASTNAME); + user.setEmailAddress(TESTUSER_EMAIL); + user.setActive(TESTUSER_ACTIVE); + return user; + } + + /** + * Create a List of Users. + * + * @param num The number of users to generate + * @return The list of create Users + * @exception Exception If an error occurs while creating the users. + */ + public List createTestUsers(int num) throws Exception { + ArrayList users = new ArrayList(); + for (int i = 0; i < num; i++) { + User u = generateTestUser(); + UserResponse createResponse = getApi().getUserApi().createUser(u, TESTUSER_PASSWORD); + hqAssertSuccess(createResponse); + users.add(createResponse.getUser()); + } + return users; + } + + public void deleteTestUsers(List users) throws Exception { + UserApi api = getApi().getUserApi(); + + for (User u : users) { + StatusResponse response = api.deleteUser(u.getId()); + hqAssertSuccess(response); + } + } + + /** + * Generate a valid Role object that's guaranteed to have a unique Name + * @return A valid Role object. + */ + protected Role generateTestRole() { + + Random r = new Random(); + + Role role = new Role(); + role.setName(TESTROLE_NAME_PREFIX + r.nextInt()); + role.setDescription(TESTROLE_DESCRIPTION); + + return role; + } + + protected Role createRole(List users, List operations) + throws Exception { + + Role r = generateTestRole(); + + r.getOperation().addAll(operations); + r.getUser().addAll(users); + + RoleResponse roleResponse = getApi().getRoleApi().createRole(r); + hqAssertSuccess(roleResponse); + Role createdRole = roleResponse.getRole(); + + assertEquals("The role should have " + users.size() + " users", + users.size(), createdRole.getUser().size()); + + for (Operation o : operations) { + assertTrue("Created role does not contain operation " + o.value(), + createdRole.getOperation().contains(o)); + } + + return createdRole; + } + + protected void cleanupRole(Role r) throws Exception { + RoleApi api = getApi().getRoleApi(); + StatusResponse response = api.deleteRole(r.getId()); + hqAssertSuccess(response); } + + protected void cleanupRoles() throws Exception { + RoleApi api = getApi().getRoleApi(); + RolesResponse response = api.getRoles(); + for (Role r : response.getRole()) { + if (r.getName().startsWith(TESTROLE_NAME_PREFIX)) { + api.deleteRole(r.getId()); + } + } + } + + protected void cleanupGroup(Group g) throws Exception { + cleanupGroup(g, false); + } + + protected void cleanupGroup(Group g, boolean deleteMembers) throws Exception { + + if (deleteMembers) { + ResourceApi api = getApi().getResourceApi(); + for (Resource r : g.getResource()) { + StatusResponse response = api.deleteResource(r.getId()); + hqAssertSuccess(response); + } + } + + GroupApi api = getApi().getGroupApi(); + StatusResponse response = api.deleteGroup(g.getId()); + hqAssertSuccess(response); + } + + protected Metric findAvailabilityMetric(Resource resource) + throws IOException { + + return findAvailabilityMetric(resource, true); + } + + protected Metric findAvailabilityMetric(Resource resource, boolean enabled) + throws IOException { + + MetricApi metricApi = getApi().getMetricApi(); + + // Find availability metric for the resource + MetricsResponse metricsResponse = metricApi.getEnabledMetrics(resource); + hqAssertSuccess(metricsResponse); + Metric availMetric = null; + for (Metric m : metricsResponse.getMetric()) { + if (m.getName().equals("Availability")) { + availMetric = m; + break; + } + } + + assertNotNull("Unable to find " + + (enabled ? "an enabled" : "a disabled") + + " availability metric for " + resource.getName(), + availMetric); + + return availMetric; + } + + /** + * Need to pause test because HQ does not like it when resources + * are modified or deleted so quickly after being created. + * + * TODO: This issue needs to be fixed in the HQ Core code + * @param timeMillis Time in milliseconds to pause. + */ + void pauseTest(long timeMillis) { + try { + Thread.sleep(timeMillis); + } catch (InterruptedException e) { + // Ignore + } + } + + void pauseTest() { + // default pause time + pauseTest(2500); + } + // Assert SUCCESS void hqAssertSuccess(Response response) { diff --git a/src/org/hyperic/hq/hqapi1/test/MaintenanceTestBase.java b/src/org/hyperic/hq/hqapi1/test/MaintenanceTestBase.java index 7e40c592..ece2b6ab 100644 --- a/src/org/hyperic/hq/hqapi1/test/MaintenanceTestBase.java +++ b/src/org/hyperic/hq/hqapi1/test/MaintenanceTestBase.java @@ -45,12 +45,6 @@ public MaintenanceTestBase(String name) { super(name); } - void cleanupGroup(Group g) throws Exception { - GroupApi api = getApi().getGroupApi(); - StatusResponse response = api.deleteGroup(g.getId()); - hqAssertSuccess(response); - } - Group getFileServerMountCompatibleGroup() throws Exception { HQApi api = getApi(); diff --git a/src/org/hyperic/hq/hqapi1/test/MetricData_test.java b/src/org/hyperic/hq/hqapi1/test/MetricData_test.java index e927ea87..e80c847d 100644 --- a/src/org/hyperic/hq/hqapi1/test/MetricData_test.java +++ b/src/org/hyperic/hq/hqapi1/test/MetricData_test.java @@ -254,12 +254,6 @@ private Group getMixedGroup() throws Exception { return groupResponse.getGroup(); } - private void cleanupGroup(Group g) throws Exception { - GroupApi api = getApi().getGroupApi(); - StatusResponse response = api.deleteGroup(g.getId()); - hqAssertSuccess(response); - } - public void testGetMetricGroupData() throws Exception { HQApi api = getApi(); diff --git a/src/org/hyperic/hq/hqapi1/tools/AlertCommand.java b/src/org/hyperic/hq/hqapi1/tools/AlertCommand.java new file mode 100644 index 00000000..09a212b3 --- /dev/null +++ b/src/org/hyperic/hq/hqapi1/tools/AlertCommand.java @@ -0,0 +1,247 @@ +/* + * + * NOTE: This copyright does *not* cover user programs that use HQ + * program services by normal system calls through the application + * program interfaces provided as part of the Hyperic Plug-in Development + * Kit or the Hyperic Client Development Kit - this is merely considered + * normal use of the program, and does *not* fall under the heading of + * "derived work". + * + * Copyright (C) [2008, 2009], Hyperic, Inc. + * This file is part of HQ. + * + * HQ is free software; you can redistribute it and/or modify + * it under the terms version 2 of the GNU General Public License as + * published by the Free Software Foundation. This program is distributed + * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA. + * + */ + +package org.hyperic.hq.hqapi1.tools; + +import joptsimple.OptionParser; +import joptsimple.OptionSet; + +import java.io.InputStream; +import java.util.Arrays; +import java.util.List; + +import org.hyperic.hq.hqapi1.HQApi; +import org.hyperic.hq.hqapi1.XmlUtil; +import org.hyperic.hq.hqapi1.AlertApi; +import org.hyperic.hq.hqapi1.types.Alert; +import org.hyperic.hq.hqapi1.types.AlertsResponse; +import org.hyperic.hq.hqapi1.types.ResourceResponse; +import org.hyperic.hq.hqapi1.types.StatusResponse; +import org.hyperic.hq.hqapi1.types.AlertResponse; + +public class AlertCommand extends Command { + + private static String CMD_LIST = "list"; + private static String CMD_ACK = "ack"; + private static String CMD_FIX = "fix"; + private static String CMD_DELETE = "delete"; + + private static String[] COMMANDS = { CMD_LIST, CMD_ACK, CMD_FIX, CMD_DELETE }; + + private static String OPT_ID = "id"; + private static String OPT_RESOURCE_ID = "resourceId"; + private static String OPT_COUNT = "count"; + private static String OPT_BEGIN = "begin"; + private static String OPT_END = "end"; + private static String OPT_INESC[] = { "inEsc" }; + private static String OPT_NOTFIXED[] = { "notFixed" }; + private static String OPT_SEVERITY = "severity"; + private static String OPT_REASON = "reason"; + private static String OPT_PAUSE = "pause"; + + private void printUsage() { + System.err.println("One of " + Arrays.toString(COMMANDS) + " required"); + } + + public String getName() { + return "alert"; + } + + public void handleCommand(String[] args) throws Exception { + if (args.length == 0) { + printUsage(); + System.exit(-1); + } + if (args[0].equals(CMD_LIST)) { + list(trim(args)); + } else if (args[0].equals(CMD_FIX)) { + fix(trim(args)); + } else if (args[0].equals(CMD_ACK)) { + ack(trim(args)); + } else if (args[0].equals(CMD_DELETE)) { + delete(trim(args)); + } else { + printUsage(); + System.exit(-1); + } + } + + private void list(String[] args) throws Exception { + + OptionParser p = getOptionParser(); + + p.accepts(OPT_RESOURCE_ID, "If specified, only return alerts for the " + + "given resource."). + withRequiredArg().ofType(Integer.class); + p.accepts(OPT_SEVERITY, "If specified, the minimum severity of alerts " + + "to include. (LOW=1, HIGH=3) Default = 1."). + withRequiredArg().ofType(Integer.class); + p.accepts(OPT_BEGIN, "If specified, the begin time in epoch-millis."). + withRequiredArg().ofType(Long.class); + p.accepts(OPT_END, "If specified, the end time in epoch-millis."). + withRequiredArg().ofType(Long.class); + p.accepts(OPT_COUNT, "The maximum number of alerts to return.") + .withRequiredArg().ofType(Integer.class); + p.acceptsAll(Arrays.asList(OPT_INESC), + "If specified, only return alerts which are in " + + "escalation."); + p.acceptsAll(Arrays.asList(OPT_NOTFIXED), + "If specified, only return alerts which are not fixed."); + + OptionSet options = getOptions(p, args); + + HQApi api = getApi(options); + AlertApi alertApi = api.getAlertApi(); + + Integer severity = (Integer)options.valueOf(OPT_SEVERITY); + if (severity == null) { + severity = 1; + } + + Long begin = (Long)options.valueOf(OPT_BEGIN); + if (begin == null) { + begin = 0l; + } + + Long end = (Long)options.valueOf(OPT_END); + if (end == null) { + end = System.currentTimeMillis(); + } + + // --count is required + Integer count = (Integer)getRequired(options, OPT_COUNT); + + Boolean inEsc = options.has(OPT_INESC[0]); + Boolean notFixed = options.has(OPT_NOTFIXED[0]); + + AlertsResponse alerts; + if (options.has(OPT_RESOURCE_ID)) { + Integer rid = (Integer)options.valueOf(OPT_RESOURCE_ID); + ResourceResponse resource = api.getResourceApi(). + getResource(rid, false, false); + checkSuccess(resource); + alerts = alertApi.findAlerts(resource.getResource(), begin, + end, count, severity, inEsc, notFixed); + } else { + alerts = alertApi.findAlerts(begin, end, count, + severity, inEsc, notFixed); + } + + checkSuccess(alerts); + XmlUtil.serialize(alerts, System.out, Boolean.TRUE); + } + + private void fix(String[] args) throws Exception { + + OptionParser p = getOptionParser(); + + p.accepts(OPT_ID, "The id of the alert to fix") + .withRequiredArg().ofType(Integer.class); + p.accepts(OPT_REASON, "The reason for fixing the alert") + .withRequiredArg().ofType(String.class); + + OptionSet options = getOptions(p, args); + + HQApi api = getApi(options); + AlertApi alertApi = api.getAlertApi(); + + Integer id = (Integer)getRequired(options, OPT_ID); + String reason = (String)options.valueOf(OPT_REASON); + + AlertResponse response = alertApi.fixAlert(id, reason); + checkSuccess(response); + + System.out.println("Successfully fixed alert id " + id); + } + + private void ack(String[] args) throws Exception { + + OptionParser p = getOptionParser(); + + p.accepts(OPT_ID, "The id of the Alert to acknowledge"). + withRequiredArg().ofType(Integer.class); + p.accepts(OPT_REASON, "The reason for acknowledging the alert") + .withRequiredArg().ofType(String.class); + p.accepts(OPT_PAUSE, "If specified, pause the Escalation for the " + + "given number of milliseconds") + .withRequiredArg().ofType(Long.class); + + OptionSet options = getOptions(p, args); + + HQApi api = getApi(options); + AlertApi alertApi = api.getAlertApi(); + + Integer id = (Integer)getRequired(options, OPT_ID); + String reason = (String)options.valueOf(OPT_REASON); + long pause; + if (options.has(OPT_PAUSE)) { + pause = (Long)options.valueOf(OPT_PAUSE); + } else { + pause = 0l; + } + + AlertResponse response = alertApi.ackAlert(id, reason, pause); + checkSuccess(response); + + System.out.println("Successfully acknowledged alert id " + id); + } + + private void delete(String[] args) throws Exception { + + OptionParser p = getOptionParser(); + + p.accepts(OPT_ID, "The id of the Alert to delete"). + withRequiredArg().ofType(Integer.class); + + OptionSet options = getOptions(p, args); + + HQApi api = getApi(options); + AlertApi alertApi = api.getAlertApi(); + + if (options.has(OPT_ID)) { + Integer id = (Integer)getRequired(options, OPT_ID); + StatusResponse response = alertApi.delete(id); + checkSuccess(response); + System.out.println("Successfully deleted alert id " + id); + } else { + // Delete via stdin + InputStream is = getInputStream(options); + + AlertsResponse resp = XmlUtil.deserialize(AlertsResponse.class, is); + List alerts = resp.getAlert(); + + Integer[] alertIds = new Integer[alerts.size()]; + for (int i = 0; i < alerts.size(); i++) { + alertIds[i] = alerts.get(i).getId(); + } + + StatusResponse response = alertApi.delete(alertIds); + checkSuccess(response); + System.out.println("Successfully deleted " + alertIds.length + " alerts"); + } + } +} diff --git a/src/org/hyperic/hq/hqapi1/tools/Shell.java b/src/org/hyperic/hq/hqapi1/tools/Shell.java index bb927f34..0b79ffc4 100644 --- a/src/org/hyperic/hq/hqapi1/tools/Shell.java +++ b/src/org/hyperic/hq/hqapi1/tools/Shell.java @@ -36,6 +36,7 @@ public class Shell { static { _commands.put("agent", new AgentCommand()); + _commands.put("alert", new AlertCommand()); _commands.put("alertdefinition", new AlertDefinitionCommand()); _commands.put("application", new ApplicationCommand()); _commands.put("autodiscovery", new AutoDiscoveryCommand()); diff --git a/xsd/HQApi1.xsd b/xsd/HQApi1.xsd index 755a4fa0..1103625a 100644 --- a/xsd/HQApi1.xsd +++ b/xsd/HQApi1.xsd @@ -504,6 +504,17 @@ + + + + + + + + + + + @@ -890,6 +901,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +