Refactor dblib
[ccsdk/sli/core.git] / sliapi / provider / src / main / java / org / openecomp / sdnc / sliapi / sliapiProvider.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP : CCSDK
4  * ================================================================================
5  * Copyright (C) 2017 ONAP
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
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
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=========================================================
19  */
20
21 package org.openecomp.sdnc.sliapi;
22
23 import java.util.Enumeration;
24 import java.util.LinkedList;
25 import java.util.Properties;
26 import java.util.concurrent.Future;
27
28 import org.onap.ccsdk.sli.core.sli.provider.SvcLogicService;
29 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
30 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
31 import org.opendaylight.controller.md.sal.binding.impl.AbstractForwardedDataBroker;
32 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
33 import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
34 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
35 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
36 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
37 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
38 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
39 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
40 import org.opendaylight.yang.gen.v1.org.openecomp.sdnc.sliapi.rev161110.ExecuteGraphInput;
41 import org.opendaylight.yang.gen.v1.org.openecomp.sdnc.sliapi.rev161110.ExecuteGraphInput.Mode;
42 import org.opendaylight.yang.gen.v1.org.openecomp.sdnc.sliapi.rev161110.ExecuteGraphInputBuilder;
43 import org.opendaylight.yang.gen.v1.org.openecomp.sdnc.sliapi.rev161110.ExecuteGraphOutput;
44 import org.opendaylight.yang.gen.v1.org.openecomp.sdnc.sliapi.rev161110.ExecuteGraphOutputBuilder;
45 import org.opendaylight.yang.gen.v1.org.openecomp.sdnc.sliapi.rev161110.HealthcheckOutput;
46 import org.opendaylight.yang.gen.v1.org.openecomp.sdnc.sliapi.rev161110.HealthcheckOutputBuilder;
47 import org.opendaylight.yang.gen.v1.org.openecomp.sdnc.sliapi.rev161110.SLIAPIService;
48 import org.opendaylight.yang.gen.v1.org.openecomp.sdnc.sliapi.rev161110.TestResults;
49 import org.opendaylight.yang.gen.v1.org.openecomp.sdnc.sliapi.rev161110.execute.graph.input.SliParameter;
50 import org.opendaylight.yang.gen.v1.org.openecomp.sdnc.sliapi.rev161110.test.results.TestResult;
51 import org.opendaylight.yang.gen.v1.org.openecomp.sdnc.sliapi.rev161110.test.results.TestResultBuilder;
52 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
53 import org.opendaylight.yangtools.yang.common.QName;
54 import org.opendaylight.yangtools.yang.common.RpcResult;
55 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
56 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
57 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
58 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
59 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
60 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
61 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
62 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
63 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
64 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder;
65 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
66 import org.osgi.framework.BundleContext;
67 import org.osgi.framework.FrameworkUtil;
68 import org.osgi.framework.ServiceReference;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71
72 import com.google.common.util.concurrent.Futures;
73
74
75 /**
76  * Defines a base implementation for your provider. This class extends from a helper class
77  * which provides storage for the most commonly used components of the MD-SAL. Additionally the
78  * base class provides some basic logging and initialization / clean up methods.
79  *
80  * To use this, copy and paste (overwrite) the following method into the TestApplicationProviderModule
81  * class which is auto generated under src/main/java in this project
82  * (created only once during first compilation):
83  *
84  * <pre>
85
86     @Override
87     public java.lang.AutoCloseable createInstance() {
88
89          final sliapiProvider provider = new sliapiProvider();
90          provider.setDataBroker( getDataBrokerDependency() );
91          provider.setNotificationService( getNotificationServiceDependency() );
92          provider.setRpcRegistry( getRpcRegistryDependency() );
93          provider.initialize();
94          return new AutoCloseable() {
95
96             @Override
97             public void close() throws Exception {
98                 //TODO: CLOSE ANY REGISTRATION OBJECTS CREATED USING ABOVE BROKER/NOTIFICATION
99                 //SERVIE/RPC REGISTRY
100                 provider.close();
101             }
102         };
103     }
104
105
106     </pre>
107  */
108 public class sliapiProvider implements AutoCloseable, SLIAPIService{
109
110     private final Logger LOG = LoggerFactory.getLogger( sliapiProvider.class );
111     private final String appName = "slitester";
112
113     protected DataBroker dataBroker;
114     protected DOMDataBroker domDataBroker;
115     protected NotificationProviderService notificationService;
116     protected RpcProviderRegistry rpcRegistry;
117
118         protected BindingAwareBroker.RpcRegistration<SLIAPIService> rpcRegistration;
119
120         private static String SLIAPI_NAMESPACE = "org:openecomp:sdnc:sliapi";
121         private static String SLIAPI_REVISION = "2016-11-10";
122
123         private static QName TEST_RESULTS_QNAME = null;
124         private static QName TEST_RESULT_QNAME = null;
125         private static QName TEST_ID_QNAME = null;
126         private static QName RESULTS_QNAME = null;
127
128         static {
129
130                 TEST_RESULTS_QNAME = QName.create(SLIAPI_NAMESPACE, SLIAPI_REVISION, "test-results");
131                 TEST_RESULT_QNAME = QName.create(TEST_RESULTS_QNAME, "test-result");
132                 TEST_ID_QNAME = QName.create(TEST_RESULT_QNAME, "test-identifier");
133                 RESULTS_QNAME = QName.create(TEST_RESULT_QNAME, "results");
134         }
135
136
137     public sliapiProvider() {
138         this.LOG.info( "Creating provider for " + appName );
139     }
140
141     public void initialize(){
142         LOG.info( "Initializing provider for " + appName );
143         //initialization code goes here.
144
145                 rpcRegistration = rpcRegistry.addRpcImplementation(SLIAPIService.class, this);
146         LOG.info( "Initialization complete for " + appName );
147     }
148
149     protected void initializeChild() {
150         //Override if you have custom initialization intelligence
151     }
152
153     @Override
154     public void close() throws Exception {
155         LOG.info( "Closing provider for " + appName );
156         //closing code goes here
157
158             rpcRegistration.close();
159         LOG.info( "Successfully closed provider for " + appName );
160     }
161
162     public void setDataBroker(DataBroker dataBroker) {
163         this.dataBroker = dataBroker;
164                 if (dataBroker instanceof AbstractForwardedDataBroker) {
165                         domDataBroker = ((AbstractForwardedDataBroker) dataBroker).getDelegate();
166                 }
167         if( LOG.isDebugEnabled() ){
168             LOG.debug( "DataBroker set to " + (dataBroker==null?"null":"non-null") + "." );
169         }
170     }
171
172     public void setNotificationService(
173             NotificationProviderService notificationService) {
174         this.notificationService = notificationService;
175         if( LOG.isDebugEnabled() ){
176             LOG.debug( "Notification Service set to " + (notificationService==null?"null":"non-null") + "." );
177         }
178     }
179
180     public void setRpcRegistry(RpcProviderRegistry rpcRegistry) {
181         this.rpcRegistry = rpcRegistry;
182         if( LOG.isDebugEnabled() ){
183             LOG.debug( "RpcRegistry set to " + (rpcRegistry==null?"null":"non-null") + "." );
184         }
185     }
186
187         @Override
188         public Future<RpcResult<ExecuteGraphOutput>> executeGraph(ExecuteGraphInput input) {
189                 RpcResult<ExecuteGraphOutput> rpcResult = null;
190
191                 SvcLogicService svcLogic = getSvcLogicService();
192                 ExecuteGraphOutputBuilder respBuilder = new ExecuteGraphOutputBuilder();
193
194                 String calledModule = input.getModuleName();
195                 String calledRpc = input.getRpcName();
196                 Mode calledMode = input.getMode();
197                 String modeStr = "sync";
198
199                 if (calledMode == Mode.Async) {
200                         modeStr = "async";
201                 }
202
203                 if (svcLogic == null) {
204                         respBuilder.setResponseCode("500");
205                         respBuilder.setResponseMessage("Could not locate OSGi SvcLogicService service");
206                         respBuilder.setAckFinalIndicator("Y");
207
208                     rpcResult = RpcResultBuilder.<ExecuteGraphOutput> status(true).withResult(respBuilder.build()).build();
209                     return(Futures.immediateFuture(rpcResult));
210                 }
211
212
213                 try {
214                         if (!svcLogic.hasGraph(calledModule, calledRpc, null, modeStr)) {
215                                 respBuilder.setResponseCode("404");
216                                 respBuilder.setResponseMessage("Directed graph for "+calledModule+"/"+calledRpc+"/"+modeStr+" not found");
217                                 respBuilder.setAckFinalIndicator("Y");
218
219                             rpcResult = RpcResultBuilder.<ExecuteGraphOutput> status(true).withResult(respBuilder.build()).build();
220                             return(Futures.immediateFuture(rpcResult));
221                         }
222                 } catch (Exception e) {
223                         LOG.error("Caught exception looking for directed graph for "+calledModule+"/"+calledRpc+"/"+modeStr, e);
224
225                         respBuilder.setResponseCode("500");
226                         respBuilder.setResponseMessage("Internal error : could not determine if target graph exists");
227                         respBuilder.setAckFinalIndicator("Y");
228
229                     rpcResult = RpcResultBuilder.<ExecuteGraphOutput> status(true).withResult(respBuilder.build()).build();
230                     return(Futures.immediateFuture(rpcResult));
231                 }
232
233                 // Load properties
234                 Properties parms = new Properties();
235
236                 // Pass properties using names from sli-parameters
237                 for (SliParameter sliParm : input.getSliParameter()) {
238
239                         String propValue = "";
240
241                         Boolean boolval = sliParm.isBooleanValue();
242
243                         if (boolval != null) {
244                                 propValue = boolval.toString();
245                         } else {
246                                 Integer intval = sliParm.getIntValue();
247                                 if (intval != null) {
248                                         propValue = intval.toString();
249                                 } else {
250                                         propValue = sliParm.getStringValue();
251                                         if (propValue == null) {
252                                                 propValue = "";
253                                         }
254                                 }
255                         }
256                         parms.setProperty(sliParm.getParameterName(), propValue);
257                 }
258
259                 // Also, pass "meta" properties (i.e. pass SliParameter objects themselves)
260                 ExecuteGraphInputBuilder inputBuilder = new ExecuteGraphInputBuilder(input);
261
262                 SliapiHelper.toProperties(parms, "input", inputBuilder);
263
264                 try {
265                         LOG.info("Calling directed graph for "+calledModule+"/"+calledRpc+"/"+modeStr);
266
267                         if (LOG.isTraceEnabled()) {
268                                 StringBuffer argList = new StringBuffer();
269                                 argList.append("Parameters : {");
270                                 Enumeration e = parms.propertyNames();
271                                 while (e.hasMoreElements()) {
272                                         String propName = (String) e.nextElement();
273                                         argList.append(" ("+propName+","+parms.getProperty(propName)+") ");
274                                 }
275                                 argList.append("}");
276                                 LOG.trace(argList.toString());
277                                 argList = null;
278                         }
279
280
281
282                         Properties respProps = svcLogic.execute(calledModule, calledRpc,
283                                         null, modeStr, parms, domDataBroker);
284
285                         StringBuilder sb = new StringBuilder("{");
286
287                         for (Object key : respProps.keySet()) {
288                                 String keyValue = (String) key;
289                                 if (keyValue != null && !"".equals(keyValue) && !keyValue.contains("input.sli-parameter")) {
290                                         sb.append("\"").append(keyValue).append("\": \"").append(respProps.getProperty(keyValue)).append("\",");
291                                 }
292                         }
293
294                         sb.setLength(sb.length() - 1);
295                         sb.append("}");
296
297                         respBuilder.setResponseCode(respProps.getProperty("error-code", "0"));
298                         respBuilder.setResponseMessage(respProps.getProperty("error-message", ""));// TODO change response-text to response-message to match other BVC APIs
299                         respBuilder.setAckFinalIndicator(respProps.getProperty("ack-final", "Y"));
300                         respBuilder.setContextMemoryJson(sb.toString());
301
302                         TestResultBuilder testResultBuilder = new TestResultBuilder();
303
304                         SliapiHelper.toBuilder(respProps, testResultBuilder);
305
306                         String testIdentifier = testResultBuilder.getTestIdentifier();
307
308                         if ((testIdentifier != null) && (testIdentifier.length() > 0)) {
309
310                                 // Add test results to config tree
311                                 LOG.debug("Saving test results for test id "+testIdentifier);
312
313                                 DomSaveTestResult(testResultBuilder.build(), true, LogicalDatastoreType.CONFIGURATION);
314
315                         }
316
317                 } catch (Exception e) {
318                         LOG.error("Caught exception executing directed graph for"
319                                         + calledModule + ":" + calledRpc + "," + modeStr + ">", e);
320
321                         respBuilder.setResponseCode("500");
322                         respBuilder
323                                         .setResponseMessage("Internal error : caught exception executing directed graph "
324                                                         + calledModule
325                                                         + "/"
326                                                         + calledRpc
327                                                         + "/"
328                                                         + modeStr);
329                         respBuilder.setAckFinalIndicator("Y");
330
331                 }
332
333                 rpcResult = RpcResultBuilder.<ExecuteGraphOutput> status(true)
334                                 .withResult(respBuilder.build()).build();
335                 return (Futures.immediateFuture(rpcResult));
336         }
337
338
339         private SvcLogicService getSvcLogicService() {
340                 BundleContext bctx = FrameworkUtil.getBundle(SvcLogicService.class).getBundleContext();
341
342                 SvcLogicService svcLogic = null;
343
344         // Get SvcLogicService reference
345                 ServiceReference sref = bctx.getServiceReference(SvcLogicService.NAME);
346                 if (sref  != null)
347                 {
348                         svcLogic =  (SvcLogicService) bctx.getService(sref);
349
350                 }
351                 else
352                 {
353                         LOG.warn("Cannot find service reference for "+SvcLogicService.NAME);
354
355                 }
356
357                 return(svcLogic);
358         }
359
360         @Override
361         public Future<RpcResult<HealthcheckOutput>> healthcheck() {
362
363                 RpcResult<HealthcheckOutput> rpcResult = null;
364                 SvcLogicService svcLogic = getSvcLogicService();
365
366                 HealthcheckOutputBuilder respBuilder = new HealthcheckOutputBuilder();
367
368                 String calledModule = "sli";
369                 String calledRpc = "healthcheck";
370                 String modeStr = "sync";
371
372                 if (svcLogic == null) {
373                         respBuilder.setResponseCode("500");
374                         respBuilder.setResponseMessage("Could not locate OSGi SvcLogicService service");
375                         respBuilder.setAckFinalIndicator("Y");
376
377                     rpcResult = RpcResultBuilder.<HealthcheckOutput> failed().withResult(respBuilder.build()).build();
378                     return(Futures.immediateFuture(rpcResult));
379                 }
380
381                 try {
382                         if (!svcLogic.hasGraph(calledModule, calledRpc, null, modeStr)) {
383                                 respBuilder.setResponseCode("404");
384                                 respBuilder.setResponseMessage("Directed graph for "+calledModule+"/"+calledRpc+"/"+modeStr+" not found");
385
386                                 respBuilder.setAckFinalIndicator("Y");
387
388                             rpcResult = RpcResultBuilder.<HealthcheckOutput> status(true).withResult(respBuilder.build()).build();
389                             return(Futures.immediateFuture(rpcResult));
390                         }
391                 } catch (Exception e) {
392                         LOG.error("Caught exception looking for directed graph for "+calledModule+"/"+calledRpc+"/"+modeStr, e);
393
394                         respBuilder.setResponseCode("500");
395                         respBuilder.setResponseMessage("Internal error : could not determine if target graph exists");
396                         respBuilder.setAckFinalIndicator("Y");
397
398                     rpcResult = RpcResultBuilder.<HealthcheckOutput> failed().withResult(respBuilder.build()).build();
399                     return(Futures.immediateFuture(rpcResult));
400                 }
401
402                 try {
403                         LOG.info("Calling directed graph for "+calledModule+"/"+calledRpc+"/"+modeStr);
404
405                         Properties parms = new Properties();
406
407                         Properties respProps = svcLogic.execute(calledModule, calledRpc,
408                                         null, modeStr, parms);
409
410                         respBuilder.setResponseCode(respProps.getProperty("error-code", "0"));
411                         respBuilder.setResponseMessage(respProps.getProperty("error-message", ""));
412                         respBuilder.setAckFinalIndicator(respProps.getProperty("ack-final", "Y"));
413
414                 } catch (Exception e) {
415                         LOG.error("Caught exception executing directed graph for"
416                                         + calledModule + ":" + calledRpc + "," + modeStr + ">", e);
417
418                         respBuilder.setResponseCode("500");
419                         respBuilder
420                                         .setResponseMessage("Internal error : caught exception executing directed graph "
421                                                         + calledModule
422                                                         + "/"
423                                                         + calledRpc
424                                                         + "/"
425                                                         + modeStr);
426                         respBuilder.setAckFinalIndicator("Y");
427
428                 }
429
430                 rpcResult = RpcResultBuilder.<HealthcheckOutput> status(true)
431                                 .withResult(respBuilder.build()).build();
432                 return (Futures.immediateFuture(rpcResult));
433         }
434
435         private void DomSaveTestResult(final TestResult entry, boolean merge, LogicalDatastoreType storeType) {
436
437
438                 if (domDataBroker == null) {
439                         LOG.error("domDataBroker unset - cannot save test result using DOMDataBroker");
440                         return;
441                 }
442
443                 MapEntryNode resultNode = null;
444
445                 try {
446                         resultNode = toMapEntryNode(entry);
447                 } catch (Exception e) {
448                         LOG.error("Caught exception trying to create map entry node", e);
449                 }
450
451                 if (resultNode == null) {
452                         LOG.error("Could not convert entry to MapEntryNode");
453                         return;
454                 }
455
456
457                 YangInstanceIdentifier testResultsPid = YangInstanceIdentifier.builder().node(TEST_RESULTS_QNAME).node(QName.create(TEST_RESULTS_QNAME, "test-result")).build();
458                 YangInstanceIdentifier testResultPid = testResultsPid.node(new NodeIdentifierWithPredicates(TEST_RESULT_QNAME, resultNode.getIdentifier().getKeyValues()));
459
460
461
462                 int tries = 2;
463                 while(true) {
464                         try {
465                                 DOMDataWriteTransaction wtx = domDataBroker.newWriteOnlyTransaction();
466                                 if (merge) {
467                                         LOG.info("Merging test identifier "+entry.getTestIdentifier());
468                                         wtx.merge(storeType, testResultPid, resultNode);
469                                 } else {
470                                         LOG.info("Putting test identifier "+entry.getTestIdentifier());
471                                         wtx.put(storeType,  testResultPid, resultNode);
472                                 }
473                                 wtx.submit().checkedGet();
474                                 LOG.trace("Update DataStore succeeded");
475                                 break;
476                         } catch (final TransactionCommitFailedException e) {
477                                 if(e instanceof OptimisticLockFailedException) {
478                                         if(--tries <= 0) {
479                                                 LOG.trace("Got OptimisticLockFailedException on last try - failing ");
480                                                 throw new IllegalStateException(e);
481                                         }
482                                         LOG.trace("Got OptimisticLockFailedException - trying again ");
483                                 } else {
484                                         LOG.trace("Update DataStore failed");
485                                         throw new IllegalStateException(e);
486                                 }
487                         }
488                 }
489
490         }
491
492                 private void SaveTestResult(final TestResult entry, boolean merge, LogicalDatastoreType storeType) throws IllegalStateException
493                 {
494                         // Each entry will be identifiable by a unique key, we have to create that identifier
495                         InstanceIdentifier.InstanceIdentifierBuilder<TestResult> testResultIdBuilder =
496                                         InstanceIdentifier.<TestResults>builder(TestResults.class)
497                                         .child(TestResult.class, entry.getKey());
498                         InstanceIdentifier<TestResult> path = testResultIdBuilder.toInstance();
499                         int tries = 2;
500                         while(true) {
501                                 try {
502                                         WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
503                                         if (merge) {
504                                                 tx.merge(storeType, path, entry);
505                                         } else {
506                                                 tx.put(storeType, path, entry);
507                                         }
508                                         tx.submit().checkedGet();
509                                         LOG.trace("Update DataStore succeeded");
510                                         break;
511                                 } catch (final TransactionCommitFailedException e) {
512                                         if(e instanceof OptimisticLockFailedException) {
513                                                 if(--tries <= 0) {
514                                                         LOG.trace("Got OptimisticLockFailedException on last try - failing ");
515                                                         throw new IllegalStateException(e);
516                                                 }
517                                                 LOG.trace("Got OptimisticLockFailedException - trying again ");
518                                         } else {
519                                                 LOG.trace("Update DataStore failed");
520                                                 throw new IllegalStateException(e);
521                                         }
522                                 }
523                         }
524                 }
525
526                 private MapEntryNode toMapEntryNode(TestResult testResult) {
527
528
529                         YangInstanceIdentifier testResultId = YangInstanceIdentifier.builder().node(TEST_RESULTS_QNAME).node(TEST_RESULT_QNAME).build();
530
531                         // Construct results list
532                         LinkedList<LeafSetEntryNode<Object>> entryList = new LinkedList<LeafSetEntryNode<Object>>();
533                         for (String result : testResult.getResults()) {
534                                 LeafSetEntryNode<Object> leafSetEntryNode = ImmutableLeafSetEntryNodeBuilder.create()
535                                                                                                                                                                 .withNodeIdentifier(new NodeWithValue(RESULTS_QNAME, result))
536                                                                                                                                                                 .withValue(result)
537                                                                                                                                                                 .build();
538                                 entryList.add(leafSetEntryNode);
539                         }
540                         // Construct results LeafSetNode
541                         LeafSetNode<?> resultsNode = ImmutableLeafSetNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(RESULTS_QNAME)).withValue(entryList).build();
542
543
544
545                         // Construct test result ContainerNode with 2 children - test-identifier leaf and results leaf-set
546                         MapEntryNode testResultNode = ImmutableNodes.mapEntryBuilder()
547                                         .withNodeIdentifier(new NodeIdentifierWithPredicates(TEST_RESULT_QNAME, TEST_ID_QNAME, testResult.getTestIdentifier()))
548                                         .withChild(ImmutableNodes.leafNode(TEST_ID_QNAME, testResult.getTestIdentifier()))
549                                         .withChild(resultsNode)
550                                         .build();
551
552                         return(testResultNode);
553
554                 }
555
556 }