2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.vid.controller.test;
23 import org.codehaus.jackson.map.ObjectMapper;
24 import org.onap.portalsdk.core.controller.RestrictedBaseController;
25 import org.onap.vid.model.ExceptionResponse;
26 import org.springframework.http.HttpStatus;
27 import org.springframework.http.ResponseEntity;
28 import org.springframework.web.bind.annotation.*;
30 import javax.servlet.http.HttpServletRequest;
31 import javax.servlet.http.HttpServletResponse;
32 import java.io.IOException;
33 import java.util.stream.Collectors;
36 * The "TestMsoController" class is primarily designed to help test "msoCommitController.js"
38 * This class expects and receives JSON data in the same format as expected
39 * in the "real" application version of this code. However, string versions of JSON are
40 * maintained internally here instead of marshalled / unmarshalled JSON objects.
41 * The primary reasons for this were to encapsulate all the test code in this single file and
42 * minimize the time required to support initial test cases.
44 * The non-test equivalent of this controller could alternatively incorporate POJO objects
45 * instead of strings. However, the same data format sent to / received from the browser
46 * JavaScript code would still be expected.
48 * Two specific mechanisms used in this test class may be useful to the application version:
50 * 1) The use of "{variable}" elements in @RequestMappings along with the corresponding
51 * @PathVariable declarations.
53 * 2) The use of @ExceptionHandler for general purpose exception handler.
54 * (See @ExceptionHandler comments)
56 * This class is intended to be used in either:
58 * A) Eclipse environments
60 * B) Linux environments with ONLY a single user running tests.
61 * The "quick and dirty" error simulation approach used here makes use of static states for some
62 * scenarios. Thus multiple users simultaneously testing in Linux environments
63 * may have contention issues.
67 * The Class TestMsoController.
70 @RequestMapping("testmso")
71 public class TestMsoController extends RestrictedBaseController {
74 * Artificial delay (in milliseconds) added before responding to create /
78 /** The Constant TEST_DELAY_SHORT_MSEC. */
79 private final static int TEST_DELAY_SHORT_MSEC = 1000;
82 * Long delay to simulate non-responsive server test
85 /** The Constant TEST_DELAY_LONG_MSEC. */
86 private final static int TEST_DELAY_LONG_MSEC = 15000;
89 * Default number of polls expected before transaction complete.
92 /** The Constant MAXIMUM_POLLS_DEFAULT. */
93 private final static int MAXIMUM_POLLS_DEFAULT = 4;
96 * Number of polls to simulate "maximum polls exceeded" test.
99 /** The Constant MAXIMUM_POLLS_LARGE. */
100 private final static int MAXIMUM_POLLS_LARGE = 10;
103 * Simulated error types. The GUI front end is expected to set these values
104 * in the "modelName" field of the "mso_create_svc_instance" request.
107 /** The Constant ERROR_POLICY_EXCEPTION. */
108 private final static String ERROR_POLICY_EXCEPTION = "ERROR_POLICY_EXCEPTION";
110 /** The Constant ERROR_SERVICE_EXCEPTION. */
111 private final static String ERROR_SERVICE_EXCEPTION = "ERROR_SERVICE_EXCEPTION";
113 /** The Constant ERROR_POLL_FAILURE. */
114 private final static String ERROR_POLL_FAILURE = "ERROR_POLL_FAILURE";
116 /** The Constant ERROR_INVALID_FIELD_INITIAL. */
117 private final static String ERROR_INVALID_FIELD_INITIAL = "ERROR_INVALID_FIELD_INITIAL";
119 /** The Constant ERROR_INVALID_FIELD_POLL. */
120 private final static String ERROR_INVALID_FIELD_POLL = "ERROR_INVALID_FIELD_POLL";
122 /** The Constant ERROR_GENERAL_SERVER_EXCEPTION. */
123 private final static String ERROR_GENERAL_SERVER_EXCEPTION = "ERROR_GENERAL_SERVER_EXCEPTION";
125 /** The Constant ERROR_MAX_POLLS. */
126 private final static String ERROR_MAX_POLLS = "ERROR_MAX_POLLS";
128 /** The Constant ERROR_SERVER_TIMEOUT_INITIAL. */
129 private final static String ERROR_SERVER_TIMEOUT_INITIAL = "ERROR_SERVER_TIMEOUT_INITIAL";
131 /** The Constant ERROR_SERVER_TIMEOUT_POLL. */
132 private final static String ERROR_SERVER_TIMEOUT_POLL = "ERROR_SERVER_TIMEOUT_POLL";
134 /** The simulated error. */
135 private String simulatedError = "";
137 /** The maximum polls. */
138 private int maximumPolls = 0;
140 /** The attempt count. */
141 private int attemptCount = 0;
144 * Creates the svc instance.
146 * @param request the request
147 * @return the response entity
148 * @throws Exception the exception
150 @RequestMapping(value = "/mso_create_svc_instance", method = RequestMethod.POST)
151 public ResponseEntity<String> createSvcInstance(HttpServletRequest request) throws Exception {
152 readAndLogRequest("CREATE SERVICE INSTANCE", request);
153 Thread.sleep(TEST_DELAY_SHORT_MSEC);
154 maximumPolls = MAXIMUM_POLLS_DEFAULT; // Simulates MSO polling behavior
158 * This block of code simulates various errors and would NOT be expected
159 * in a non-test method
161 System.err.println("simulatedError: " + simulatedError);
163 if (simulatedError.equals(ERROR_POLICY_EXCEPTION)) {
164 return new ResponseEntity<String>(policyExceptionResponse, HttpStatus.OK);
166 if (simulatedError.equals(ERROR_SERVICE_EXCEPTION)) {
167 return new ResponseEntity<String>(serviceExceptionResponse, HttpStatus.OK);
169 if (simulatedError.equals(ERROR_INVALID_FIELD_INITIAL)) {
171 * Force invalid response field name. Return
172 * "XXXXXrequestReferences" instead of "requestReferences"
174 return new ResponseEntity<String>(acceptResponse.replace("requestReferences", "XXXXXrequestReferences"),
178 if (simulatedError.equals(ERROR_GENERAL_SERVER_EXCEPTION)) {
179 throw new IOException("an example of an IO exception");
182 if (simulatedError.equals(ERROR_SERVER_TIMEOUT_INITIAL)) {
183 Thread.sleep(TEST_DELAY_LONG_MSEC);
186 if (simulatedError.equals(ERROR_MAX_POLLS)) {
187 maximumPolls = MAXIMUM_POLLS_LARGE;
191 * End of block of simulated error code.
194 return new ResponseEntity<String>(acceptResponse, HttpStatus.OK);
198 * Delete svc instance.
200 * @param serviceInstanceId the service instance id
201 * @param request the request
202 * @return the response entity
203 * @throws Exception the exception
205 @RequestMapping(value = "/mso_delete_svc_instance/{serviceInstanceId}", method = RequestMethod.POST)
206 public ResponseEntity<String> deleteSvcInstance(@PathVariable("serviceInstanceId") String serviceInstanceId,
207 HttpServletRequest request) throws Exception {
208 readAndLogRequest("DELETE SERVICE INSTANCE: serviceInstanceId: " + serviceInstanceId, request);
209 Thread.sleep(TEST_DELAY_SHORT_MSEC);
210 maximumPolls = MAXIMUM_POLLS_DEFAULT; // Simulates MSO polling behavior
212 return new ResponseEntity<String>(acceptResponse, HttpStatus.OK);
216 * Creates the vnf instance.
218 * @param serviceInstanceId the service instance id
219 * @param request the request
220 * @return the response entity
221 * @throws Exception the exception
223 @RequestMapping(value = "/mso_create_vnf_instance/{serviceInstanceId}", method = RequestMethod.POST)
224 public ResponseEntity<String> createVnfInstance(@PathVariable("serviceInstanceId") String serviceInstanceId,
225 HttpServletRequest request) throws Exception {
226 readAndLogRequest("CREATE VNF INSTANCE: serviceInstanceId: " + serviceInstanceId, request);
227 Thread.sleep(TEST_DELAY_SHORT_MSEC);
228 maximumPolls = MAXIMUM_POLLS_DEFAULT; // Simulates MSO polling behavior
230 return new ResponseEntity<String>(acceptResponse, HttpStatus.OK);
234 * Delete vnf instance.
236 * @param serviceInstanceId the service instance id
237 * @param vnfInstanceId the vnf instance id
238 * @param request the request
239 * @return the response entity
240 * @throws Exception the exception
242 @RequestMapping(value = "/mso_delete_vnf_instance/{serviceInstanceId}/vnfs/{vnfInstanceId}", method = RequestMethod.POST)
243 public ResponseEntity<String> deleteVnfInstance(@PathVariable("serviceInstanceId") String serviceInstanceId,
244 @PathVariable("vnfInstanceId") String vnfInstanceId, HttpServletRequest request) throws Exception {
246 "DELETE VNF INSTANCE: serviceInstanceId: " + serviceInstanceId + " vnfInstanceId: " + vnfInstanceId,
248 Thread.sleep(TEST_DELAY_SHORT_MSEC);
249 maximumPolls = MAXIMUM_POLLS_DEFAULT; // Simulates MSO polling behavior
251 return new ResponseEntity<String>(acceptResponse, HttpStatus.OK);
255 * Creates the vf module instance.
257 * @param serviceInstanceId the service instance id
258 * @param vnfInstanceId the vnf instance id
259 * @param request the request
260 * @return the response entity
261 * @throws Exception the exception
263 // /serviceInstances/v2/ff305d54-75b4-431b-adb2-eb6b9e5ff000/vnfs/ff305d54-75b4-ff1b-adb2-eb6b9e5460ff/vfModules
264 @RequestMapping(value = "/mso_create_vfmodule_instance/{serviceInstanceId}/vnfs/{vnfInstanceId}", method = RequestMethod.POST)
265 public ResponseEntity<String> createVfModuleInstance(@PathVariable("serviceInstanceId") String serviceInstanceId,
266 @PathVariable("vnfInstanceId") String vnfInstanceId, HttpServletRequest request) throws Exception {
267 readAndLogRequest("CREATE VF MODULE INSTANCE: serviceInstanceId: " + serviceInstanceId + " vnfInstanceId: "
268 + vnfInstanceId, request);
269 Thread.sleep(TEST_DELAY_SHORT_MSEC);
270 maximumPolls = MAXIMUM_POLLS_DEFAULT; // Simulates MSO polling behavior
272 return new ResponseEntity<String>(acceptResponse, HttpStatus.OK);
276 * Delete vf module instance.
278 * @param serviceInstanceId the service instance id
279 * @param vnfInstanceId the vnf instance id
280 * @param vfModuleInstanceId the vf module instance id
281 * @param request the request
282 * @return the response entity
283 * @throws Exception the exception
285 // /serviceInstances/v2/ff305d54-75b4-431b-adb2-eb6b9e5ff000/vnfs/ff305d54-75b4-ff1b-adb2-eb6b9e5460ff/vfModules/ff305d54-75b4-ff1b-bdb2-eb6b9e5460ff
286 @RequestMapping(value = "/mso_delete_vfmodule_instance/{serviceInstanceId}/vnfs/{vnfInstanceId}/vfModules/{vfModuleInstanceId}", method = RequestMethod.POST)
287 public ResponseEntity<String> deleteVfModuleInstance(@PathVariable("serviceInstanceId") String serviceInstanceId,
288 @PathVariable("vnfInstanceId") String vnfInstanceId,
289 @PathVariable("vfModuleInstanceId") String vfModuleInstanceId, HttpServletRequest request)
291 readAndLogRequest("DELETE VF MODULE INSTANCE: serviceInstanceId: " + serviceInstanceId + " vnfInstanceId: "
292 + vnfInstanceId + " vfModuleInstanceId: " + vfModuleInstanceId, request);
293 Thread.sleep(TEST_DELAY_SHORT_MSEC);
294 maximumPolls = MAXIMUM_POLLS_DEFAULT; // Simulates MSO polling behavior
296 return new ResponseEntity<String>(acceptResponse, HttpStatus.OK);
301 * Creates the volume group instance.
303 * @param serviceInstanceId the service instance id
304 * @param vnfInstanceId the vnf instance id
305 * @param request the request
306 * @return the response entity
307 * @throws Exception the exception
309 // /serviceInstances/v2/ff305d54-75b4-431b-adb2-eb6b9e5ff000/volumeGroups
310 @RequestMapping(value = "/mso_create_volumegroup_instance/{serviceInstanceId}/vnfs/{vnfInstanceId}", method = RequestMethod.POST)
311 public ResponseEntity<String> createVolumeGroupInstance(@PathVariable("serviceInstanceId") String serviceInstanceId,
312 @PathVariable("vnfInstanceId") String vnfInstanceId, HttpServletRequest request) throws Exception {
313 readAndLogRequest("CREATE VOLUME GROUP INSTANCE: seviceInstanceId: " + serviceInstanceId + " vnfInstanceId: "
314 + vnfInstanceId, request);
315 Thread.sleep(TEST_DELAY_SHORT_MSEC);
316 maximumPolls = MAXIMUM_POLLS_DEFAULT; // Simulates MSO polling behavior
318 return new ResponseEntity<String>(acceptResponse, HttpStatus.OK);
322 * Delete volume group instance.
324 * @param serviceInstanceId the service instance id
325 * @param vnfInstanceId the vnf instance id
326 * @param volumeGroupInstanceId the volume group instance id
327 * @param request the request
328 * @return the response entity
329 * @throws Exception the exception
331 // /serviceInstances/v2/ff305d54-75b4-431b-adb2-eb6b9e5ff000/volumeGroups/ff305d54-75b4-ff1b-cdb2-eb6b9e5460ff
332 @RequestMapping(value = "/mso_delete_volumegroup_instance/{serviceInstanceId}/vnfs/{vnfInstanceId}/volumeGroups/{volumeGroupInstanceId}", method = RequestMethod.POST)
333 public ResponseEntity<String> deleteVolumeGroupInstance(@PathVariable("serviceInstanceId") String serviceInstanceId,
334 @PathVariable("vnfInstanceId") String vnfInstanceId,
335 @PathVariable("volumeGroupInstanceId") String volumeGroupInstanceId, HttpServletRequest request)
337 readAndLogRequest("DELETE NW INSTANCE: serviceInstanceId: " + serviceInstanceId + " vnfInstanceId: "
338 + vnfInstanceId + " volumeGroupInstanceId: " + volumeGroupInstanceId, request);
339 Thread.sleep(TEST_DELAY_SHORT_MSEC);
340 maximumPolls = MAXIMUM_POLLS_DEFAULT; // Simulates MSO polling behavior
342 return new ResponseEntity<String>(acceptResponse, HttpStatus.OK);
346 * Creates the nw instance.
348 * @param serviceInstanceId the service instance id
349 * @param request the request
350 * @return the response entity
351 * @throws Exception the exception
353 @RequestMapping(value = "/mso_create_nw_instance/{serviceInstanceId}", method = RequestMethod.POST)
354 public ResponseEntity<String> createNwInstance(@PathVariable("serviceInstanceId") String serviceInstanceId,
355 HttpServletRequest request) throws Exception {
356 readAndLogRequest("CREATE NW INSTANCE: serviceInstanceId: " + serviceInstanceId, request);
357 Thread.sleep(TEST_DELAY_SHORT_MSEC);
358 maximumPolls = MAXIMUM_POLLS_DEFAULT; // Simulates MSO polling behavior
360 return new ResponseEntity<String>(acceptResponse, HttpStatus.OK);
364 * Delete nw instance.
366 * @param serviceInstanceId the service instance id
367 * @param networkInstanceId the network instance id
368 * @param request the request
369 * @return the response entity
370 * @throws Exception the exception
372 @RequestMapping(value = "/mso_delete_nw_instance/{serviceInstanceId}/networks/{networkInstanceId}", method = RequestMethod.POST)
373 public ResponseEntity<String> deleteNwInstance(@PathVariable("serviceInstanceId") String serviceInstanceId,
374 @PathVariable("networkInstanceId") String networkInstanceId, HttpServletRequest request) throws Exception {
375 readAndLogRequest("DELETE NW INSTANCE: serviceInstanceId: " + serviceInstanceId + " networkInstanceId: "
376 + networkInstanceId, request);
377 Thread.sleep(TEST_DELAY_SHORT_MSEC);
378 maximumPolls = MAXIMUM_POLLS_DEFAULT; // Simulates MSO polling behavior
380 return new ResponseEntity<String>(acceptResponse, HttpStatus.OK);
384 * Gets the orchestration request.
386 * @param requestId the request id
387 * @param request the request
388 * @return the orchestration request
389 * @throws Exception the exception
391 @RequestMapping(value = "/mso_get_orch_req/{requestId}", method = RequestMethod.GET)
392 public ResponseEntity<String> getOrchestrationRequest(@PathVariable("requestId") String requestId,
393 HttpServletRequest request) throws Exception {
395 System.err.println("GET ORCHESTRATION REQUEST: requestId: " + requestId);
398 * This block of code simulates various errors and would NOT be expected
399 * in a non-test method
402 if (simulatedError.equals(ERROR_INVALID_FIELD_POLL)) {
404 * Force invalid response field name. Return "XXXXXrequestStatus"
405 * instead of "requestStatus"
407 return new ResponseEntity<String>(inProgressResponse.replace("requestStatus", "XXXXXrequestStatus"),
411 if (simulatedError.equals(ERROR_POLL_FAILURE)) {
413 * Force status field with "Failure"
415 return new ResponseEntity<String>(inProgressResponse.replace("InProgress", "Failure"), HttpStatus.OK);
418 if (simulatedError.equals(ERROR_SERVER_TIMEOUT_POLL)) {
419 Thread.sleep(TEST_DELAY_LONG_MSEC);
423 * End of block of simulated error code.
427 * This logic simulates how MSO might behave ... i.e. return different
428 * results depending on the value of 'maximumPolls'.
431 int percentProgress = (++attemptCount * 100) / maximumPolls;
433 System.err.println("attempts: " + attemptCount + " max: " + maximumPolls + " percent: " + percentProgress);
435 String response = inProgressResponse.replace("\"50\"", "\"" + Integer.toString(percentProgress) + "\"");
437 if (attemptCount < maximumPolls) {
438 if (attemptCount > 1) {
439 response = response.replace("vLan setup", "setup step " + Integer.toString(attemptCount));
441 return new ResponseEntity<String>(response, HttpStatus.OK);
443 return new ResponseEntity<String>(
444 response.replace("InProgress", "Complete").replace("vLan setup complete", ""), HttpStatus.OK);
449 * Gets the orchestration requests.
451 * @param filterString the filter string
452 * @param request the request
453 * @return the orchestration requests
454 * @throws Exception the exception
456 @RequestMapping(value = "/mso_get_orch_reqs/{filterString}", method = RequestMethod.GET)
457 public ResponseEntity<String> getOrchestrationRequests(@PathVariable("filterString") String filterString,
458 HttpServletRequest request) throws Exception {
460 System.err.println("GET ORCHESTRATION REQUESTS: filterString: " + filterString);
462 return new ResponseEntity<String>(getOrchestrationRequestsResponse, HttpStatus.OK);
467 * General purpose exception handler that could be used in application code.
469 * The method returns exceptions as error code 500. Both the exception type
470 * and message are written as a JSON object.
472 * See the following references:
474 * 1) The ExceptionResponse POJO.
476 * 2) The "getHttpErrorMessage" function in "utilityService.js" - an example
477 * of how the browser JavaScript code can interpret this response.
484 * @param response the response
485 * @throws IOException Signals that an I/O exception has occurred.
487 @ExceptionHandler(Exception.class)
488 private void exception(Exception e, HttpServletResponse response) throws IOException {
491 * This logging step should preferably be replaced with an appropriate
492 * logging method consistent whatever logging mechanism the rest of the
493 * application code uses.
496 e.printStackTrace(System.err);
498 response.setContentType("application/json; charset=UTF-8");
499 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
501 ExceptionResponse exceptionResponse = new ExceptionResponse();
502 exceptionResponse.setException(e.getClass().toString().replaceFirst("^.*\\.", ""));
503 exceptionResponse.setMessage(e.getMessage());
505 response.getWriter().write(new ObjectMapper().writeValueAsString(exceptionResponse));
507 response.flushBuffer();
512 * 'readAndLogRequest' only intended to be used for testing.
514 * The method reads JSON from the input stream and thus prevents other
515 * mechanisms from reading the input.
519 * Read and log request.
521 * @param label the label
522 * @param request the request
523 * @throws Exception the exception
525 private void readAndLogRequest(String label, HttpServletRequest request) throws Exception {
526 String input = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
528 ObjectMapper mapper = new ObjectMapper();
529 Object json = mapper.readValue(input, Object.class);
531 System.err.println(label + "\n" + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(json));
534 * Only needed for error simulation ...
536 if (input.matches("^.*modelName.*$")) {
537 simulatedError = input.replaceAll("^.*\"modelName\":\"", "").replaceAll("\".*$", "");
542 * Various test responses:
547 /** The accept response. */
549 * Sample responses to initial create / delete transaction
551 private String acceptResponse =
553 " \"status\": 202," +
555 " \"requestReferences\": {" +
556 " \"instanceId\": \"bc305d54-75b4-431b-adb2-eb6b9e546014\"," +
557 " \"requestId\": \"rq1234d1-5a33-55df-13ab-12abad84e331\"" +
562 /** The policy exception response. */
563 private String policyExceptionResponse =
565 " \"status\": 400," +
567 " \"requestError\": {" +
568 " \"policyException\": {" +
569 " \"messageId\": \"POL9003\"," +
570 " \"text\": \"Message content size exceeds the allowable limit\"" +
576 /** The service exception response. */
577 private String serviceExceptionResponse =
579 " \"status\": 400," +
581 " \"requestError\": {" +
582 " \"serviceException\": {" +
583 " \"messageId\": \"SVC2000\"," +
584 " \"text\": \"Missing Parameter: %1. Error code is %2\"," +
585 " \"variables\": [" +
595 /** The in progress response. */
597 * Sample response to subsequent getOrchestrationRequest
599 private String inProgressResponse =
601 " \"status\": 200," +
604 " \"requestId\": \"rq1234d1-5a33-55df-13ab-12abad84e333\"," +
605 " \"startTime\": \"Thu, 04 Jun 2009 02:51:59 GMT\"," +
606 " \"instanceIds\": {" +
607 " \"serviceInstanceId\": \"bc305d54-75b4-431b-adb2-eb6b9e546014\"" +
609 " \"requestScope\": \"service\"," +
610 " \"requestType\": \"createInstance\"," +
611 " \"requestDetails\": {" +
612 " \"modelInfo\": {" +
613 " \"modelType\": \"service\"," +
614 " \"modelId\": \"sn5256d1-5a33-55df-13ab-12abad84e764\"," +
615 " \"modelNameVersionId\": \"ab6478e4-ea33-3346-ac12-ab121484a333\"," +
616 " \"modelName\": \"WanBonding\"," +
617 " \"modelVersion\": \"1\"" +
619 " \"subscriberInfo\": {" +
620 " \"globalSubscriberId\": \"C12345\"," +
621 " \"subscriberName\": \"General Electric Division 12\"" +
623 " \"requestParameters\": {" +
624 " \"vpnId\": \"1a2b3c4d5e6f\"," +
625 " \"productName\": \"Trinity\"," +
626 " \"customerId\": \"icore9883749\"" +
629 " \"requestStatus\": {" +
630 " \"timestamp\": \"Thu, 04 Jun 2009 02:53:39 GMT\"," +
631 " \"requestState\": \"InProgress\"," +
632 " \"statusMessage\": \"vLan setup complete\"," +
633 " \"percentProgress\": \"50\"" +
640 * Sample response to subsequent getOrchestrationRequests
643 /** The get orchestration requests response. */
644 private String getOrchestrationRequestsResponse =
646 " \"status\": 200," +
648 " \"requestList\": [" +
651 " \"requestId\": \"rq1234d1-5a33-55df-13ab-12abad84e333\"," +
652 " \"startTime\": \"Thu, 04 Jun 2009 02:51:59 GMT\"," +
653 " \"finishTime\": \"Thu, 04 Jun 2009 02:55:59 GMT\"," +
654 " \"instanceReferences\": {" +
655 " \"serviceInstanceId\": \"bc305d54-75b4-431b-adb2-eb6b9e546014\"" +
657 " \"requestScope\": \"service\"," +
658 " \"requestType\": \"createInstance\"," +
659 " \"requestDetails\": {" +
660 " \"modelInfo\": {" +
661 " \"modelType\": \"service\"," +
662 " \"modelId\": \"sn5256d1-5a33-55df-13ab-12abad84e764\"," +
663 " \"modelNameVersionId\": \"ab6478e4-ea33-3346-ac12-ab121484a333\"," +
664 " \"modelName\": \"WanBonding\"," +
665 " \"modelVersion\": \"1\"" +
667 " \"subscriberInfo\": {" +
668 " \"globalSubscriberId\": \"C12345\"," +
669 " \"subscriberName\": \"General Electric Division 12\"" +
671 " \"requestParameters\": {" +
672 " \"vpnId\": \"1a2b3c4d5e6f\"," +
673 " \"productName\": \"Trinity\"," +
674 " \"customerId\": \"icore9883749\"" +
677 " \"requestStatus\": {" +
678 " \"timestamp\": \"Thu, 04 Jun 2009 02:54:49 GMT\"," +
679 " \"requestState\": \"complete\"," +
680 " \"statusMessage\": \"Resource Created\"," +
681 " \"percentProgress\": \"100\"" +
687 " \"requestId\": \"rq1234d1-5a33-55df-13ab-12abad84e334\"," +
688 " \"startTime\": \"Thu, 04 Jun 2009 03:52:59 GMT\"," +
689 " \"instanceReferences\": {" +
690 " \"serviceInstanceId\": \"bc305d54-75b4-431b-adb2-eb6b9e546014\"" +
692 " \"requestScope\": \"service\"," +
693 " \"requestType\": \"updateInstance\"," +
694 " \"requestDetails\": {" +
695 " \"modelInfo\": {" +
696 " \"modelType\": \"service\"," +
697 " \"modelId\": \"sn5256d1-5a33-55df-13ab-12abad84e764\"," +
698 " \"modelNameVersionId\": \"ab6478e4-ea33-3346-ac12-ab121484a333\"," +
699 " \"modelName\": \"WanBonding\"," +
700 " \"modelVersion\": \"1\"" +
702 " \"subscriberInfo\": {" +
703 " \"globalSubscriberId\": \"C12345\"," +
704 " \"subscriberName\": \"General Electric Division 12\"" +
706 " \"requestParameters\": {" +
707 " \"vpnId\": \"1a2b3c4d5e70\"," +
708 " \"productName\": \"Trinity\"," +
709 " \"customerId\": \"icore9883749\"" +
712 " \"requestStatus\": {" +
713 " \"timestamp\": \"Thu, 04 Jun 2009 03:53:39 GMT\"," +
714 " \"requestState\": \"InProgress\"," +
715 " \"statusMessage\": \"vLan setup complete\"," +
716 " \"percentProgress\": \"50\"" +