From 944224b1a452ba90913bac6bee842f5a6bd61770 Mon Sep 17 00:00:00 2001 From: "Maharajh, Robby (rx2202)" Date: Mon, 4 Dec 2017 16:43:59 -0500 Subject: Update the aai-resources to verify bulk limits Issue-ID: AAI-529 Change-Id: Ia87e3298ce9c46f3b409587df6e9bd6e2065ec8a Signed-off-by: Maharajh, Robby (rx2202) --- .../etc/appprops/aaiconfig.properties | 4 + .../etc/appprops/error.properties | 1 + .../main/java/org/onap/aai/rest/BulkConsumer.java | 55 ++++- .../org/onap/aai/rest/BulkAddConsumerTest.java | 24 +++ .../org/onap/aai/rest/BulkProcessConsumerTest.java | 24 +++ .../etc/appprops/aaiconfig.properties | 2 + .../etc/appprops/error.properties | 1 + .../payloads/bulk/pserver-bulk-limit-exceed.json | 229 +++++++++++++++++++++ .../payloads/bulk/pserver-bulk-limit-meet.json | 222 ++++++++++++++++++++ 9 files changed, 556 insertions(+), 6 deletions(-) create mode 100644 aai-resources/src/test/resources/payloads/bulk/pserver-bulk-limit-exceed.json create mode 100644 aai-resources/src/test/resources/payloads/bulk/pserver-bulk-limit-meet.json diff --git a/aai-resources/bundleconfig-local/etc/appprops/aaiconfig.properties b/aai-resources/bundleconfig-local/etc/appprops/aaiconfig.properties index 98d4c57..27a744c 100644 --- a/aai-resources/bundleconfig-local/etc/appprops/aaiconfig.properties +++ b/aai-resources/bundleconfig-local/etc/appprops/aaiconfig.properties @@ -147,3 +147,7 @@ aai.server.rebind=g # This is a fake one just for test so please ignore ecm.auth.password.x=OBF:1igd1i9a1jnb1yte1vv11vu91yt81jk71i6o1idt aai.run.migrations=false +aai.jms.enable=false + +#limit set for bulk consumer APIS +aai.bulkconsumer.payloadlimit=30 diff --git a/aai-resources/bundleconfig-local/etc/appprops/error.properties b/aai-resources/bundleconfig-local/etc/appprops/error.properties index 9ad86ec..a20dce5 100644 --- a/aai-resources/bundleconfig-local/etc/appprops/error.properties +++ b/aai-resources/bundleconfig-local/etc/appprops/error.properties @@ -114,6 +114,7 @@ AAI_6143=5:4:INFO:6143:400:3000:Ghost vertex found AAI_6144=5:4:WARN:6144:400:3000:Cycle found in graph AAI_6145=5:4:ERROR:6145:400:3000:Cannot create a nested/containment edge via relationship AAI_6146=5:4:ERROR:6146:400:3000:Ambiguous identity map found, use a URI instead +AAI_6147=5:4:ERROR:6147:400:3000:Payload Limit Reached, reduce payload #--- aaicsvp: 7101-7199 AAI_7101=5:4:ERROR:7101:500:3002:Unexpected error in CSV file processing diff --git a/aai-resources/src/main/java/org/onap/aai/rest/BulkConsumer.java b/aai-resources/src/main/java/org/onap/aai/rest/BulkConsumer.java index 5591ae3..5ff0e35 100644 --- a/aai-resources/src/main/java/org/onap/aai/rest/BulkConsumer.java +++ b/aai-resources/src/main/java/org/onap/aai/rest/BulkConsumer.java @@ -24,8 +24,11 @@ package org.onap.aai.rest; import java.io.UnsupportedEncodingException; import java.net.URI; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Map.Entry; +import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; @@ -60,6 +63,8 @@ import org.onap.aai.restcore.HttpMethod; import org.onap.aai.restcore.RESTAPI; import org.onap.aai.serialization.engines.QueryStyle; import org.onap.aai.serialization.engines.TransactionalGraphEngine; +import org.onap.aai.util.AAIConfig; +import org.onap.aai.util.AAIConstants; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; @@ -250,12 +255,7 @@ public abstract class BulkConsumer extends RESTAPI { throw new AAIException("AAI_6111", String.format("input payload does not follow %s interface", module)); } JsonArray transactions = transactionsObj.getAsJsonArray(); - if (transactions.size() == 0) { - //case where user sends a validly formatted transactions object but - //which has no actual things in it for A&AI to do anything with - //assuming we should count this as a user error - throw new AAIException("AAI_6118", "payload had no objects to operate on"); - } + validateRequest(transactions); return transactions; } @@ -531,6 +531,49 @@ public abstract class BulkConsumer extends RESTAPI { } } + /** + * Pulls the config value for the limit of operations allowed in a bulk add/process request + * + * @throws AAIException + */ + private int getPayLoadLimit() throws AAIException{ + return Integer.parseInt(AAIConfig.get(AAIConstants.AAI_BULKCONSUMER_LIMIT)); + } + + /** + * Validates the amount of operations in a request payload is allowed + * + * @param transactions - a JsonArray of all the transactions in the request payload + * @throws AAIException + */ + private void validateRequest(JsonArray transactions) throws AAIException{ + if (transactions.size() == 0) { + //case where user sends a validly formatted transactions object but + //which has no actual things in it for A&AI to do anything with + //assuming we should count this as a user error + throw new AAIException("AAI_6118", "payload had no objects to operate on"); + }else if(transactions.size() > getPayLoadLimit()) { + throw new AAIException("AAI_6147", String.format("Payload limit of %s reached, please reduce payload.", getPayLoadLimit())); + } + int operationCount = 0; + int payLoadLimit = getPayLoadLimit(); + for(int i = 0; i < transactions.size(); i++){ + Set> entrySet = transactions.get(i).getAsJsonObject().entrySet(); + Iterator> it = entrySet.iterator(); + while(it.hasNext()){ + Map.Entry element = it.next(); + if(element.getValue() instanceof JsonArray) { + operationCount += ((JsonArray) element.getValue()).size(); + }else{ + operationCount++; + } + } + if(operationCount > payLoadLimit){ + throw new AAIException("AAI_6147", String.format("Payload limit of %s reached, please reduce payload.", payLoadLimit)); + } + } + } + protected abstract String getModule(); protected abstract boolean functionAllowed(HttpMethod method); diff --git a/aai-resources/src/test/java/org/onap/aai/rest/BulkAddConsumerTest.java b/aai-resources/src/test/java/org/onap/aai/rest/BulkAddConsumerTest.java index 66afeeb..e734d98 100644 --- a/aai-resources/src/test/java/org/onap/aai/rest/BulkAddConsumerTest.java +++ b/aai-resources/src/test/java/org/onap/aai/rest/BulkAddConsumerTest.java @@ -107,6 +107,30 @@ public class BulkAddConsumerTest extends BulkProcessorTestAbstraction { assertEquals("Bad Request", Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); assertEquals("Contains error code", true, response.getEntity().toString().contains("ERR.5.4.6111")); } + + @Test + public void bulkAddCheckMeetsLimit() throws IOException{ + when(uriInfo.getPath()).thenReturn(uri); + when(uriInfo.getPath(false)).thenReturn(uri); + + String payload = getBulkPayload("pserver-bulk-limit-meet"); + Response response = executeRequest(payload); + + assertEquals("Created", Response.Status.CREATED.getStatusCode(), response.getStatus()); + assertEquals("Contains 30 {\"201\":null}", 30, StringUtils.countMatches(response.getEntity().toString(), "{\"201\":null}")); + } + + @Test + public void bulkAddCheckExceedsLimit() throws IOException{ + when(uriInfo.getPath()).thenReturn(uri); + when(uriInfo.getPath(false)).thenReturn(uri); + + String payload = getBulkPayload("pserver-bulk-limit-exceed"); + Response response = executeRequest(payload); + + assertEquals("Bad Request", Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + assertEquals("Contains error code", true, response.getEntity().toString().contains("ERR.5.4.6147")); + } @Override protected BulkConsumer getConsumer(){ diff --git a/aai-resources/src/test/java/org/onap/aai/rest/BulkProcessConsumerTest.java b/aai-resources/src/test/java/org/onap/aai/rest/BulkProcessConsumerTest.java index 8a75a96..1e3a4e4 100644 --- a/aai-resources/src/test/java/org/onap/aai/rest/BulkProcessConsumerTest.java +++ b/aai-resources/src/test/java/org/onap/aai/rest/BulkProcessConsumerTest.java @@ -246,6 +246,30 @@ public class BulkProcessConsumerTest extends BulkProcessorTestAbstraction { assertEquals("Bad Request", Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); assertEquals("Contains error code", true, response.getEntity().toString().contains("ERR.5.4.6111")); } + @Test + public void bulkProcessCheckMeetsLimit() throws IOException{ + when(uriInfo.getPath()).thenReturn(uri); + when(uriInfo.getPath(false)).thenReturn(uri); + + String payload = getBulkPayload("pserver-bulk-limit-meet"); + Response response = executeRequest(payload); + + assertEquals("Created", Response.Status.CREATED.getStatusCode(), response.getStatus()); + //assertEquals("Contains error code", true, response.getEntity().toString().contains("ERR.5.4.6147")); + assertEquals("Contains 30 {\"201\":null}", 30, StringUtils.countMatches(response.getEntity().toString(), "{\"201\":null}")); + } + + @Test + public void bulkProcessCheckExceedsLimit() throws IOException{ + when(uriInfo.getPath()).thenReturn(uri); + when(uriInfo.getPath(false)).thenReturn(uri); + + String payload = getBulkPayload("pserver-bulk-limit-exceed"); + Response response = executeRequest(payload); + + assertEquals("Bad Request", Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus()); + assertEquals("Contains error code", true, response.getEntity().toString().contains("ERR.5.4.6147")); + } @Override protected BulkConsumer getConsumer(){ diff --git a/aai-resources/src/test/resources/bundleconfig-local/etc/appprops/aaiconfig.properties b/aai-resources/src/test/resources/bundleconfig-local/etc/appprops/aaiconfig.properties index 5ba1e0f..d406c04 100644 --- a/aai-resources/src/test/resources/bundleconfig-local/etc/appprops/aaiconfig.properties +++ b/aai-resources/src/test/resources/bundleconfig-local/etc/appprops/aaiconfig.properties @@ -144,3 +144,5 @@ aai.run.migrations=false ecm.auth.password.x=OBF:1igd1i9a1jnb1yte1vv11vu91yt81jk71i6o1idt aai.jms.enable=false +#limit set for bulk consumer APIS +aai.bulkconsumer.payloadlimit=30 diff --git a/aai-resources/src/test/resources/bundleconfig-local/etc/appprops/error.properties b/aai-resources/src/test/resources/bundleconfig-local/etc/appprops/error.properties index 11416ca..10f1177 100644 --- a/aai-resources/src/test/resources/bundleconfig-local/etc/appprops/error.properties +++ b/aai-resources/src/test/resources/bundleconfig-local/etc/appprops/error.properties @@ -111,6 +111,7 @@ AAI_6143=5:4:INFO:6143:400:3000:Ghost vertex found AAI_6144=5:4:WARN:6144:400:3000:Cycle found in graph AAI_6145=5:4:ERROR:6145:400:3000:Cannot create a nested/containment edge via relationship AAI_6146=5:4:ERROR:6146:400:3000:Ambiguous identity map found, use a URI instead +AAI_6147=5:4:ERROR:6147:400:3000:Payload Limit Reached, reduce payload #--- aaicsvp: 7101-7199 AAI_7101=5:4:ERROR:7101:500:3002:Unexpected error in CSV file processing diff --git a/aai-resources/src/test/resources/payloads/bulk/pserver-bulk-limit-exceed.json b/aai-resources/src/test/resources/payloads/bulk/pserver-bulk-limit-exceed.json new file mode 100644 index 0000000..60a96dd --- /dev/null +++ b/aai-resources/src/test/resources/payloads/bulk/pserver-bulk-limit-exceed.json @@ -0,0 +1,229 @@ +{ + "transactions": [ + { + "put": [ + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-1-", + "body": { + "hostname": "pserver-transactions-1-", + "fqdn": "fake-fqdn-1" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-2-", + "body": { + "hostname": "pserver-transactions-2-", + "fqdn": "fake-fqdn-2" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-3-", + "body": { + "hostname": "pserver-transactions-3-", + "fqdn": "fake-fqdn-3" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-4-", + "body": { + "hostname": "pserver-transactions-4-", + "fqdn": "fake-fqdn-4" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-5-", + "body": { + "hostname": "pserver-transactions-5-", + "fqdn": "fake-fqdn-5" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-6-", + "body": { + "hostname": "pserver-transactions-6-", + "fqdn": "fake-fqdn-6" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-7-", + "body": { + "hostname": "pserver-transactions-7-", + "fqdn": "fake-fqdn-7" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-8-", + "body": { + "hostname": "pserver-transactions-8-", + "fqdn": "fake-fqdn-8" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-9-", + "body": { + "hostname": "pserver-transactions-9-", + "fqdn": "fake-fqdn-9" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-10-", + "body": { + "hostname": "pserver-transactions-10-", + "fqdn": "fake-fqdn-10" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-11-", + "body": { + "hostname": "pserver-transactions-11-", + "fqdn": "fake-fqdn-11" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-12-", + "body": { + "hostname": "pserver-transactions-12-", + "fqdn": "fake-fqdn-12" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-13-", + "body": { + "hostname": "pserver-transactions-13-", + "fqdn": "fake-fqdn-13" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-14-", + "body": { + "hostname": "pserver-transactions-14-", + "fqdn": "fake-fqdn-14" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-15-", + "body": { + "hostname": "pserver-transactions-15-", + "fqdn": "fake-fqdn-15" + } + } + ] + }, + { + "put": [ + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-16-", + "body": { + "hostname": "pserver-transactions-16-", + "fqdn": "fake-fqdn-16" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-17-", + "body": { + "hostname": "pserver-transactions-17-", + "fqdn": "fake-fqdn-17" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-18-", + "body": { + "hostname": "pserver-transactions-18-", + "fqdn": "fake-fqdn-18" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-19-", + "body": { + "hostname": "pserver-transactions-19-", + "fqdn": "fake-fqdn-19" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-20-", + "body": { + "hostname": "pserver-transactions-20-", + "fqdn": "fake-fqdn-20" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-21-", + "body": { + "hostname": "pserver-transactions-21-", + "fqdn": "fake-fqdn-21" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-22-", + "body": { + "hostname": "pserver-transactions-22-", + "fqdn": "fake-fqdn-22" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-23-", + "body": { + "hostname": "pserver-transactions-23-", + "fqdn": "fake-fqdn-23" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-24-", + "body": { + "hostname": "pserver-transactions-24-", + "fqdn": "fake-fqdn-24" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-25-", + "body": { + "hostname": "pserver-transactions-25-", + "fqdn": "fake-fqdn-25" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-26-", + "body": { + "hostname": "pserver-transactions-26-", + "fqdn": "fake-fqdn-26" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-27-", + "body": { + "hostname": "pserver-transactions-27-", + "fqdn": "fake-fqdn-27" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-28-", + "body": { + "hostname": "pserver-transactions-28-", + "fqdn": "fake-fqdn-28" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-29-", + "body": { + "hostname": "pserver-transactions-29-", + "fqdn": "fake-fqdn-29" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-30-", + "body": { + "hostname": "pserver-transactions-30-", + "fqdn": "fake-fqdn-30" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-31-", + "body": { + "hostname": "pserver-transactions-31-", + "fqdn": "fake-fqdn-31" + } + } + ] + } + ] +} diff --git a/aai-resources/src/test/resources/payloads/bulk/pserver-bulk-limit-meet.json b/aai-resources/src/test/resources/payloads/bulk/pserver-bulk-limit-meet.json new file mode 100644 index 0000000..b06a846 --- /dev/null +++ b/aai-resources/src/test/resources/payloads/bulk/pserver-bulk-limit-meet.json @@ -0,0 +1,222 @@ +{ + "transactions": [ + { + "put": [ + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-1-", + "body": { + "hostname": "pserver-transactions-1-", + "fqdn": "fake-fqdn-1" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-2-", + "body": { + "hostname": "pserver-transactions-2-", + "fqdn": "fake-fqdn-2" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-3-", + "body": { + "hostname": "pserver-transactions-3-", + "fqdn": "fake-fqdn-3" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-4-", + "body": { + "hostname": "pserver-transactions-4-", + "fqdn": "fake-fqdn-4" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-5-", + "body": { + "hostname": "pserver-transactions-5-", + "fqdn": "fake-fqdn-5" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-6-", + "body": { + "hostname": "pserver-transactions-6-", + "fqdn": "fake-fqdn-6" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-7-", + "body": { + "hostname": "pserver-transactions-7-", + "fqdn": "fake-fqdn-7" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-8-", + "body": { + "hostname": "pserver-transactions-8-", + "fqdn": "fake-fqdn-8" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-9-", + "body": { + "hostname": "pserver-transactions-9-", + "fqdn": "fake-fqdn-9" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-10-", + "body": { + "hostname": "pserver-transactions-10-", + "fqdn": "fake-fqdn-10" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-11-", + "body": { + "hostname": "pserver-transactions-11-", + "fqdn": "fake-fqdn-11" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-12-", + "body": { + "hostname": "pserver-transactions-12-", + "fqdn": "fake-fqdn-12" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-13-", + "body": { + "hostname": "pserver-transactions-13-", + "fqdn": "fake-fqdn-13" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-14-", + "body": { + "hostname": "pserver-transactions-14-", + "fqdn": "fake-fqdn-14" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-15-", + "body": { + "hostname": "pserver-transactions-15-", + "fqdn": "fake-fqdn-15" + } + } + ] + }, + { + "put": [ + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-16-", + "body": { + "hostname": "pserver-transactions-16-", + "fqdn": "fake-fqdn-16" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-17-", + "body": { + "hostname": "pserver-transactions-17-", + "fqdn": "fake-fqdn-17" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-18-", + "body": { + "hostname": "pserver-transactions-18-", + "fqdn": "fake-fqdn-18" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-19-", + "body": { + "hostname": "pserver-transactions-19-", + "fqdn": "fake-fqdn-19" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-20-", + "body": { + "hostname": "pserver-transactions-20-", + "fqdn": "fake-fqdn-20" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-21-", + "body": { + "hostname": "pserver-transactions-21-", + "fqdn": "fake-fqdn-21" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-22-", + "body": { + "hostname": "pserver-transactions-22-", + "fqdn": "fake-fqdn-22" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-23-", + "body": { + "hostname": "pserver-transactions-23-", + "fqdn": "fake-fqdn-23" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-24-", + "body": { + "hostname": "pserver-transactions-24-", + "fqdn": "fake-fqdn-24" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-25-", + "body": { + "hostname": "pserver-transactions-25-", + "fqdn": "fake-fqdn-25" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-26-", + "body": { + "hostname": "pserver-transactions-26-", + "fqdn": "fake-fqdn-26" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-27-", + "body": { + "hostname": "pserver-transactions-27-", + "fqdn": "fake-fqdn-27" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-28-", + "body": { + "hostname": "pserver-transactions-28-", + "fqdn": "fake-fqdn-28" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-29-", + "body": { + "hostname": "pserver-transactions-29-", + "fqdn": "fake-fqdn-29" + } + }, + { + "uri": "/cloud-infrastructure/pservers/pserver/pserver-transactions-30-", + "body": { + "hostname": "pserver-transactions-30-", + "fqdn": "fake-fqdn-30" + } + } + ] + } + ] +} -- cgit 1.2.3-korg