From a7c754a0ad5b3b74861430c6fc115de010d55f6d Mon Sep 17 00:00:00 2001 From: Ryan Morgan Date: Thu, 26 Mar 2009 20:33:39 +0000 Subject: [PATCH 1/9] Add command line group sync ala mass [HHQ-2870] --- .../hyperic/hq/hqapi1/tools/GroupCommand.java | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/src/org/hyperic/hq/hqapi1/tools/GroupCommand.java b/src/org/hyperic/hq/hqapi1/tools/GroupCommand.java index 17e2ced1..03ecdbe2 100644 --- a/src/org/hyperic/hq/hqapi1/tools/GroupCommand.java +++ b/src/org/hyperic/hq/hqapi1/tools/GroupCommand.java @@ -8,10 +8,19 @@ import org.hyperic.hq.hqapi1.types.Group; import org.hyperic.hq.hqapi1.types.GroupsResponse; import org.hyperic.hq.hqapi1.types.StatusResponse; +import org.hyperic.hq.hqapi1.types.GroupResponse; +import org.hyperic.hq.hqapi1.types.ResponseStatus; +import org.hyperic.hq.hqapi1.types.ResourcesResponse; +import org.hyperic.hq.hqapi1.types.ResourcePrototypeResponse; +import org.hyperic.hq.hqapi1.types.Resource; import java.io.InputStream; import java.util.Arrays; import java.util.List; +import java.util.Iterator; +import java.util.ArrayList; +import java.util.regex.Pattern; +import java.util.regex.Matcher; public class GroupCommand extends Command { @@ -25,6 +34,12 @@ public class GroupCommand extends Command { private static String OPT_COMPAT = "compatible"; private static String OPT_MIXED = "mixed"; + // Additional sync commands when syncing via command line options. + private static String OPT_NAME = "name"; + private static String OPT_PROTOTYPE = "prototype"; + private static String OPT_REGEX = "regex"; + private static String OPT_DELETEMISSING = "deleteMissing"; + private void printUsage() { System.err.println("One of " + Arrays.toString(COMMANDS) + " required"); } @@ -81,8 +96,24 @@ private void list(String[] args) throws Exception { private void sync(String[] args) throws Exception { OptionParser p = getOptionParser(); + + p.accepts(OPT_NAME, "The group name to sync"). + withRequiredArg().ofType(String.class); + p.accepts(OPT_PROTOTYPE, "The resource type to query for group membership"). + withRequiredArg().ofType(String.class); + p.accepts(OPT_REGEX, "The regular expression to apply to the " + OPT_PROTOTYPE + + " flag").withRequiredArg().ofType(String.class); + p.accepts(OPT_DELETEMISSING, "Remove resources in the group not included in " + + "the " + OPT_PROTOTYPE + " and " + OPT_REGEX); + p.accepts(OPT_COMPAT, "If specified, attempt to make the group compatible"); + OptionSet options = getOptions(p, args); + if (options.hasArgument(OPT_NAME)) { + syncViaCommandLineArgs(options); + return; + } + HQApi api = getApi(options); GroupApi groupApi = api.getGroupApi(); @@ -98,6 +129,76 @@ private void sync(String[] args) throws Exception { System.out.println("Successfully synced " + groups.size() + " groups."); } + private void syncViaCommandLineArgs(OptionSet s) throws Exception + { + // Required args + String name = (String)getRequired(s, OPT_NAME); + String prototype = (String)getRequired(s, OPT_PROTOTYPE); + + // Optional + String regex = (String)s.valueOf(OPT_REGEX); + boolean deleteMissing = s.has(OPT_DELETEMISSING); + boolean compatible = s.has(OPT_COMPAT); + + HQApi api = getApi(s); + + // Get prototype + ResourcePrototypeResponse protoResponse = + api.getResourceApi().getResourcePrototype(prototype); + checkSuccess(protoResponse); + + // Query resources + ResourcesResponse resourceResponse = api.getResourceApi(). + getResources(protoResponse.getResourcePrototype(), false, false); + checkSuccess(resourceResponse); + + List resources = resourceResponse.getResource(); + + // Filter based on regex, if given. + if (regex != null) { + Pattern pattern = Pattern.compile(regex); + for (Iterator i = resources.iterator(); i.hasNext(); ) { + Resource r = i.next(); + Matcher m = pattern.matcher(r.getName()); + if (!m.matches()) { + i.remove(); + } + } + } + + System.out.println(name + ": Found " + resources.size() + " matching resources"); + + // Check for existing group + Group group; + GroupResponse groupResponse = api.getGroupApi().getGroup(name); + if (groupResponse.getStatus().equals(ResponseStatus.SUCCESS)) { + group = groupResponse.getGroup(); + System.out.println(name + ": Syncing existing group (" + + group.getResource().size() + " members)"); + + if (deleteMissing) { + System.out.println(name + ": Clearing existing members"); + group.getResource().clear(); + } + } else { + group = new Group(); + group.setName(name); + if (compatible) { + group.setResourcePrototype(protoResponse.getResourcePrototype()); + } + System.out.println(name + ": Creating new group"); + } + + group.getResource().addAll(resources); + List groups = new ArrayList(); + groups.add(group); + GroupsResponse syncResponse = api.getGroupApi().syncGroups(groups); + checkSuccess(syncResponse); + + System.out.println(name + ": Success (" + + syncResponse.getGroup().get(0).getResource().size() + " members)"); + } + private void delete(String[] args) throws Exception { OptionParser p = getOptionParser(); From 8ea3e3957c8657938d0eb40b76bda146414c027a Mon Sep 17 00:00:00 2001 From: Ryan Morgan Date: Thu, 26 Mar 2009 20:51:03 +0000 Subject: [PATCH 2/9] Add optional --description flag for group sync. --- src/org/hyperic/hq/hqapi1/tools/GroupCommand.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/org/hyperic/hq/hqapi1/tools/GroupCommand.java b/src/org/hyperic/hq/hqapi1/tools/GroupCommand.java index 03ecdbe2..45f3ed97 100644 --- a/src/org/hyperic/hq/hqapi1/tools/GroupCommand.java +++ b/src/org/hyperic/hq/hqapi1/tools/GroupCommand.java @@ -39,6 +39,7 @@ public class GroupCommand extends Command { private static String OPT_PROTOTYPE = "prototype"; private static String OPT_REGEX = "regex"; private static String OPT_DELETEMISSING = "deleteMissing"; + private static String OPT_DESC = "description"; private void printUsage() { System.err.println("One of " + Arrays.toString(COMMANDS) + " required"); @@ -106,6 +107,8 @@ private void sync(String[] args) throws Exception { p.accepts(OPT_DELETEMISSING, "Remove resources in the group not included in " + "the " + OPT_PROTOTYPE + " and " + OPT_REGEX); p.accepts(OPT_COMPAT, "If specified, attempt to make the group compatible"); + p.accepts(OPT_DESC, "If specified, set the description for the group"). + withRequiredArg().ofType(String.class); OptionSet options = getOptions(p, args); @@ -137,6 +140,7 @@ private void syncViaCommandLineArgs(OptionSet s) throws Exception // Optional String regex = (String)s.valueOf(OPT_REGEX); + String description = (String)s.valueOf(OPT_DESC); boolean deleteMissing = s.has(OPT_DELETEMISSING); boolean compatible = s.has(OPT_COMPAT); @@ -189,6 +193,10 @@ private void syncViaCommandLineArgs(OptionSet s) throws Exception System.out.println(name + ": Creating new group"); } + if (s.hasArgument(OPT_DESC)) { + group.setDescription((String)s.valueOf(OPT_DESC)); + } + group.getResource().addAll(resources); List groups = new ArrayList(); groups.add(group); From b4cbd704a9b4b1ee8886cceb2d9888664b19556a Mon Sep 17 00:00:00 2001 From: Ryan Morgan Date: Fri, 17 Apr 2009 19:07:42 +0000 Subject: [PATCH 3/9] Look up existing type alert defintions during sync. [HHQ-3039] --- .../app/AlertdefinitionController.groovy | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/hqu/hqapi1/app/AlertdefinitionController.groovy b/hqu/hqapi1/app/AlertdefinitionController.groovy index f7a073ba..27f0df26 100644 --- a/hqu/hqapi1/app/AlertdefinitionController.groovy +++ b/hqu/hqapi1/app/AlertdefinitionController.groovy @@ -341,6 +341,30 @@ public class AlertdefinitionController extends ApiController { name + " for definition " + xmlDef.'@name') } + + // For type based alerts - attempt to look up by name + // if no id was given. + try { + def allTypeAlerts = alertHelper.findTypeBasedDefinitions() + def existingTypeAlerts = allTypeAlerts.grep { + it.name == xmlDef.'@name' && + it.resource.name == name + } + + if (existingTypeAlerts.size() > 1) { + failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, + "Found multiple (" + + existingTypeAlerts.size() + + ") matches for alert " + + "definition " + + xmlDef.'@name') + } else if (existingTypeAlerts.size() == 1) { + existing = existingTypeAlerts[0] + log.debug("Found existing type alert=" + existing) + } + } catch (PermissionException e) { + failureXml = getFailureXML(ErrorCode.PERMISSION_DENIED) + } } else { failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, "A single Resource or " + From c00ce09ae4e453ac264891c4e676e20e166b7c0f Mon Sep 17 00:00:00 2001 From: Ryan Morgan Date: Tue, 31 Mar 2009 17:22:40 +0000 Subject: [PATCH 4/9] Remove internal alert definition by name cache used for recovery alert lookups. Instead query the resource in question for the definition by that name. This fixes problems where a sync file may have many problem/recoveries and may not be in order. [HHQ-2973] --- .../app/AlertdefinitionController.groovy | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/hqu/hqapi1/app/AlertdefinitionController.groovy b/hqu/hqapi1/app/AlertdefinitionController.groovy index 27f0df26..3d41abdb 100644 --- a/hqu/hqapi1/app/AlertdefinitionController.groovy +++ b/hqu/hqapi1/app/AlertdefinitionController.groovy @@ -297,7 +297,7 @@ public class AlertdefinitionController extends ApiController { def sync(params) { def syncRequest = new XmlParser().parseText(getUpload('postdata')) - def definitionsByName = [:] + def definitions = [] for (xmlDef in syncRequest['AlertDefinition']) { def failureXml = null @@ -610,33 +610,23 @@ public class AlertdefinitionController extends ApiController { break } - def recoveryDef = definitionsByName[xmlCond.'@recover'] - if (recoveryDef) { - if (aeid.type == recoveryDef.appdefType && - aeid.id == recoveryDef.appdefId) { - acv.measurementId = recoveryDef.id - } - } else { - // Attempt to look for the name of alert on the - // given resource. - if (resource) { - log.warn("Recovery alert " + - xmlCond.'@recover' + " not found " + - "in sync XML, checking resource.") - def resourceDefs = resource.getAlertDefinitions(user) - def recovery = resourceDefs.find { it.name == xmlCond.'@recover' } - if (recovery) { - log.info("Found definition " + recovery.id) - acv.measurementId = recovery.id - break - } + // If a resource alert, look up alert by name + if (resource) { + log.debug("Looking up alerts for resource=" + resource.id) + def resourceDefs = resource.getAlertDefinitions(user) + def recovery = resourceDefs.find { it.name == xmlCond.'@recover' } + if (recovery) { + log.info("Found recovery definition " + recovery.id) + acv.measurementId = recovery.id + break } + } + if (!acv.measurementId) { failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, "Unable to find recovery " + "with name '" + xmlCond.'@recover' + "'") - break } break @@ -738,14 +728,14 @@ public class AlertdefinitionController extends ApiController { pojo.unsetEscalation(user) } - // Keep defs around so we don't need to look up recovery alerts - definitionsByName[pojo.name] = pojo + // Keep synced defintions for sync return XML + definitions << pojo } renderXml() { out << AlertDefinitionsResponse() { out << getSuccessXML() - for (alertdef in definitionsByName.values()) { + for (alertdef in definitions) { out << getAlertDefinitionXML(alertdef, false) } } From eca576ef1bdb6f0dcb0ca8002e953379f9fcae4a Mon Sep 17 00:00:00 2001 From: Ryan Morgan Date: Fri, 12 Jun 2009 14:10:14 -0700 Subject: [PATCH 5/9] Do not allow escalations to be set for recovery alerts. [HHQ-3188] --- hqu/hqapi1/app/AlertdefinitionController.groovy | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/hqu/hqapi1/app/AlertdefinitionController.groovy b/hqu/hqapi1/app/AlertdefinitionController.groovy index 3d41abdb..371e1a04 100644 --- a/hqu/hqapi1/app/AlertdefinitionController.groovy +++ b/hqu/hqapi1/app/AlertdefinitionController.groovy @@ -477,6 +477,8 @@ public class AlertdefinitionController extends ApiController { templs = resource.metrics } + def isRecovery = false + for (xmlCond in xmlDef['AlertCondition']) { AlertConditionValue acv = new AlertConditionValue() def acError @@ -610,6 +612,8 @@ public class AlertdefinitionController extends ApiController { break } + isRecovery = true + // If a resource alert, look up alert by name if (resource) { log.debug("Looking up alerts for resource=" + resource.id) @@ -723,7 +727,13 @@ public class AlertdefinitionController extends ApiController { // Deal with Escalations if (escalation) { - pojo.setEscalation(user, escalation) + // TODO: Backend should handle escalations on recovery alerts + if (isRecovery) { + log.warn("Skipping escalation for definition '" + pojo.name + + "'. Escalations not allowed for recovery alerts.") + } else { + pojo.setEscalation(user, escalation) + } } else { pojo.unsetEscalation(user) } From 4ca79f891feb455aa8be84ed3b1b1c8779c6c999 Mon Sep 17 00:00:00 2001 From: Ryan Morgan Date: Mon, 15 Jun 2009 13:24:59 -0700 Subject: [PATCH 6/9] Fix argument passing for windows shell. [HHQ-2812] --- bin/hqapi.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/hqapi.bat b/bin/hqapi.bat index 955194c4..e4254525 100644 --- a/bin/hqapi.bat +++ b/bin/hqapi.bat @@ -16,4 +16,4 @@ if "%_JAVACMD%" == "" set _JAVACMD=%JAVA_HOME%\bin\java.exe :noJavaHome if "%_JAVACMD%" == "" set _JAVACMD=java.exe -"%_JAVACMD%" -cp %CLASSPATH% org.hyperic.hq.hqapi1.tools.Shell "%1" +"%_JAVACMD%" -cp %CLASSPATH% org.hyperic.hq.hqapi1.tools.Shell %* From 018ee97f63a145a1f034165324794a585ae03706 Mon Sep 17 00:00:00 2001 From: Ryan Morgan Date: Mon, 15 Jun 2009 15:15:58 -0700 Subject: [PATCH 7/9] Allow alert definition filtering by associated escalation. [HHQ-3192] --- .../app/AlertdefinitionController.groovy | 17 +++++++++++++- .../hyperic/hq/hqapi1/AlertDefinitionApi.java | 23 +++++++++++++++++++ .../hqapi1/tools/AlertDefinitionCommand.java | 16 +++++++++++-- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/hqu/hqapi1/app/AlertdefinitionController.groovy b/hqu/hqapi1/app/AlertdefinitionController.groovy index 371e1a04..237892f9 100644 --- a/hqu/hqapi1/app/AlertdefinitionController.groovy +++ b/hqu/hqapi1/app/AlertdefinitionController.groovy @@ -10,12 +10,15 @@ import org.hyperic.hq.events.AlertSeverity import org.hyperic.hq.events.EventConstants import org.hyperic.hq.events.shared.AlertConditionValue import org.hyperic.hq.events.shared.AlertDefinitionValue +import org.hyperic.hq.events.server.session.AlertDefinitionManagerEJBImpl as AMan import org.hyperic.hq.measurement.shared.ResourceLogEvent import org.hyperic.hq.product.LogTrackPlugin import ApiController public class AlertdefinitionController extends ApiController { private eventBoss = EventsBoss.one + private aMan = AMan.one + private EVENT_LEVEL_TO_NUM = [ ANY: -1, ERR : LogTrackPlugin.LOGLEVEL_ERROR, @@ -161,6 +164,7 @@ public class AlertdefinitionController extends ApiController { def alertNameFilter = params.getOne('alertNameFilter') def resourceNameFilter = params.getOne('resourceNameFilter') def groupName = params.getOne('groupName') + def escalationId = params.getOne('escalationId')?.toInteger() def excludeTypeBased = params.getOne('excludeTypeBased')?.toBoolean() if (excludeTypeBased == null) { @@ -170,7 +174,7 @@ public class AlertdefinitionController extends ApiController { def parentId = params.getOne('parentId')?.toInteger() def failureXml - def definitions + def definitions = [] if (parentId) { def typeAlert = alertHelper.getById(parentId) @@ -186,6 +190,16 @@ public class AlertdefinitionController extends ApiController { } else { definitions = typeAlert.children } + } else if (escalationId != null) { + def escalation = escalationHelper.getEscalation(escalationId, null) + if (!escalation) { + failureXml = getFailureXML(ErrorCode.OBJECT_NOT_FOUND, + "Escalation with id = " + escalationId + + " not found") + } else { + // TODO: Add to alert helper + definitions = aMan.getUsing(escalation) + } } else { definitions = alertHelper.findDefinitions(AlertSeverity.LOW, null, excludeTypeBased) @@ -203,6 +217,7 @@ public class AlertdefinitionController extends ApiController { failureXml = getFailureXML(ErrorCode.INVALID_PARAMETERS, "Invalid syntax: " + e.getMessage()) } + if (groupName) { def group = getGroup(null, groupName) if (!group) { diff --git a/src/org/hyperic/hq/hqapi1/AlertDefinitionApi.java b/src/org/hyperic/hq/hqapi1/AlertDefinitionApi.java index 06f50569..f84dcb96 100644 --- a/src/org/hyperic/hq/hqapi1/AlertDefinitionApi.java +++ b/src/org/hyperic/hq/hqapi1/AlertDefinitionApi.java @@ -4,6 +4,7 @@ import org.hyperic.hq.hqapi1.types.StatusResponse; import org.hyperic.hq.hqapi1.types.AlertDefinition; import org.hyperic.hq.hqapi1.types.AlertDefinitionsRequest; +import org.hyperic.hq.hqapi1.types.Escalation; import java.io.IOException; import java.util.HashMap; @@ -105,6 +106,28 @@ public AlertDefinitionsResponse getAlertDefinitions(AlertDefinition parent) AlertDefinitionsResponse.class); } + + /** + * Find all {@link org.hyperic.hq.hqapi1.types.AlertDefinition}s that use + * the given escalation. + * + * @param e The {@link Escalation} to filter by + * + * @return On {@link org.hyperic.hq.hqapi1.types.ResponseStatus#SUCCESS}, + * a list of AlertDefinitions are returned. + * + * @throws java.io.IOException If a network error occurs while making the request. + */ + public AlertDefinitionsResponse getAlertDefinitions(Escalation e) + throws IOException { + + Map params = new HashMap(); + params.put("escalationId", new String[] { Integer.toString(e.getId()) }); + + return doGet("alertdefinition/listDefinitions.hqu", params, + AlertDefinitionsResponse.class); + } + /** * Find all type based {@link org.hyperic.hq.hqapi1.types.AlertDefinition}s in the system. * diff --git a/src/org/hyperic/hq/hqapi1/tools/AlertDefinitionCommand.java b/src/org/hyperic/hq/hqapi1/tools/AlertDefinitionCommand.java index 41242512..7b0e7bd0 100644 --- a/src/org/hyperic/hq/hqapi1/tools/AlertDefinitionCommand.java +++ b/src/org/hyperic/hq/hqapi1/tools/AlertDefinitionCommand.java @@ -5,9 +5,11 @@ import org.hyperic.hq.hqapi1.AlertDefinitionApi; import org.hyperic.hq.hqapi1.HQApi; import org.hyperic.hq.hqapi1.XmlUtil; +import org.hyperic.hq.hqapi1.EscalationApi; import org.hyperic.hq.hqapi1.types.AlertDefinition; import org.hyperic.hq.hqapi1.types.AlertDefinitionsResponse; import org.hyperic.hq.hqapi1.types.StatusResponse; +import org.hyperic.hq.hqapi1.types.EscalationResponse; import java.io.InputStream; import java.util.Arrays; @@ -29,6 +31,7 @@ public class AlertDefinitionCommand extends Command { private static String OPT_ALERT_NAME = "alertName"; private static String OPT_ID = "id"; private static String OPT_BATCH_SIZE = "batchSize"; + private static String OPT_ESCLATION = "escalation"; private void printUsage() { System.err.println("One of " + Arrays.toString(COMMANDS) + " required"); @@ -70,14 +73,18 @@ private void list(String[] args) throws Exception { "belonging to a resource with the given " + "resource name regex."). withRequiredArg().ofType(String.class); - p.accepts(OPT_ALERT_NAME, "If specified, only show alert definitions " + - "with names that match the given regex. "). + p.accepts(OPT_ALERT_NAME, "If specified, only show alert definitions " + + "with names that match the given regex."). + withRequiredArg().ofType(String.class); + p.accepts(OPT_ESCLATION, "If specified, only show alert definitions " + + "that are tied to the named escalation."). withRequiredArg().ofType(String.class); OptionSet options = getOptions(p, args); HQApi api = getApi(options); AlertDefinitionApi definitionApi = api.getAlertDefinitionApi(); + EscalationApi escalationApi = api.getEscalationApi(); AlertDefinitionsResponse alertDefs; @@ -88,6 +95,11 @@ private void list(String[] args) throws Exception { } alertDefs = definitionApi.getTypeAlertDefinitions(excludeIds); + } else if (options.has(OPT_ESCLATION)) { + EscalationResponse escalationResponse = escalationApi. + getEscalation((String)getRequired(options, OPT_ESCLATION)); + checkSuccess(escalationResponse); + alertDefs = definitionApi.getAlertDefinitions(escalationResponse.getEscalation()); } else { boolean excludeTypeAlerts = false; if (options.has(OPT_EXCLUDE_TYPEALERTS)) { From 7edaaa81f520e4d3d0ce4689c6a848045cf0e170 Mon Sep 17 00:00:00 2001 From: Ryan Morgan Date: Wed, 13 May 2009 17:18:45 +0000 Subject: [PATCH 8/9] Default children & verbose to false if not specified. --- hqu/hqapi1/app/ResourceController.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hqu/hqapi1/app/ResourceController.groovy b/hqu/hqapi1/app/ResourceController.groovy index 2d5a0eb7..65149b76 100644 --- a/hqu/hqapi1/app/ResourceController.groovy +++ b/hqu/hqapi1/app/ResourceController.groovy @@ -214,8 +214,8 @@ class ResourceController extends ApiController { def get(params) { def id = params.getOne("id")?.toInteger() def platformName = params.getOne("platformName") - def children = params.getOne("children")?.toBoolean() - def verbose = params.getOne("verbose")?.toBoolean() + boolean children = params.getOne("children", "false")?.toBoolean() + boolean verbose = params.getOne("verbose", "false")?.toBoolean() def resource = null def failureXml From f45409d35865ac2c4c49f3361f4de2a10bef182f Mon Sep 17 00:00:00 2001 From: Ryan Morgan Date: Mon, 18 May 2009 23:21:47 +0000 Subject: [PATCH 9/9] Defaults provided. Elvis has left the building. --- hqu/hqapi1/app/ResourceController.groovy | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hqu/hqapi1/app/ResourceController.groovy b/hqu/hqapi1/app/ResourceController.groovy index 65149b76..9affabe2 100644 --- a/hqu/hqapi1/app/ResourceController.groovy +++ b/hqu/hqapi1/app/ResourceController.groovy @@ -214,8 +214,8 @@ class ResourceController extends ApiController { def get(params) { def id = params.getOne("id")?.toInteger() def platformName = params.getOne("platformName") - boolean children = params.getOne("children", "false")?.toBoolean() - boolean verbose = params.getOne("verbose", "false")?.toBoolean() + boolean children = params.getOne("children", "false").toBoolean() + boolean verbose = params.getOne("verbose", "false").toBoolean() def resource = null def failureXml @@ -248,8 +248,8 @@ class ResourceController extends ApiController { def find(params) { def agentId = params.getOne("agentId")?.toInteger() def prototype = params.getOne("prototype") - def children = params.getOne("children")?.toBoolean() - def verbose = params.getOne("verbose")?.toBoolean() + def children = params.getOne("children", "false").toBoolean() + def verbose = params.getOne("verbose", "false").toBoolean() def resources = [] def failureXml