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