700dbb03d4d852af2b62f3fbc0b30f99e825c04d
[sdnc/apps.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SDNC
4  * ================================================================================
5  * Copyright (C) 2020 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
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.onap.sdnc.apps.ms.gra.controllers;
22
23 import com.fasterxml.jackson.annotation.JsonInclude;
24 import com.fasterxml.jackson.core.JsonProcessingException;
25 import com.fasterxml.jackson.databind.JsonMappingException;
26 import com.fasterxml.jackson.databind.ObjectMapper;
27 import com.google.gson.JsonParser;
28
29 import org.onap.ccsdk.apps.services.RestException;
30 import org.onap.ccsdk.apps.services.SvcLogicFactory;
31 import org.onap.ccsdk.sli.core.sli.SvcLogicContext;
32 import org.onap.ccsdk.sli.core.sli.SvcLogicException;
33 import org.onap.ccsdk.sli.core.sli.provider.base.SvcLogicServiceBase;
34 import org.onap.sdnc.apps.ms.gra.data.ConfigPreloadData;
35 import org.onap.sdnc.apps.ms.gra.data.ConfigPreloadDataRepository;
36 import org.onap.sdnc.apps.ms.gra.data.ConfigServices;
37 import org.onap.sdnc.apps.ms.gra.data.ConfigServicesRepository;
38 import org.onap.sdnc.apps.ms.gra.data.OperationalPreloadData;
39 import org.onap.sdnc.apps.ms.gra.data.OperationalPreloadDataRepository;
40 import org.onap.sdnc.apps.ms.gra.data.OperationalServices;
41 import org.onap.sdnc.apps.ms.gra.data.OperationalServicesRepository;
42 import org.onap.sdnc.apps.ms.gra.swagger.OperationsApi;
43 import org.onap.sdnc.apps.ms.gra.swagger.model.*;
44 import org.springframework.beans.factory.annotation.Autowired;
45 import org.springframework.boot.autoconfigure.domain.EntityScan;
46 import org.springframework.context.annotation.ComponentScan;
47 import org.springframework.context.annotation.Import;
48 import org.springframework.http.HttpStatus;
49 import org.springframework.http.ResponseEntity;
50 import org.springframework.stereotype.Controller;
51
52 import javax.servlet.http.HttpServletRequest;
53 import javax.validation.Valid;
54
55 import java.text.DateFormat;
56 import java.text.SimpleDateFormat;
57 import java.util.*;
58 import java.util.concurrent.atomic.AtomicBoolean;
59
60 @Controller
61 @ComponentScan(basePackages = { "org.onap.sdnc.apps.ms.gra.*", "org.onap.ccsdk.apps.services" })
62 @EntityScan("org.onap.sdnc.apps.ms.gra.*")
63 @Import(value = SvcLogicFactory.class)
64 public class OperationsApiController implements OperationsApi {
65
66     private static final String CALLED_STR = "{} called.";
67     private static final String MODULE_NAME = "GENERIC-RESOURCE-API";
68
69     private final ObjectMapper objectMapper;
70
71     private final HttpServletRequest request;
72
73     @Autowired
74     protected SvcLogicServiceBase svc;
75
76     @Autowired
77     private ConfigPreloadDataRepository configPreloadDataRepository;
78
79     @Autowired
80     private OperationalPreloadDataRepository operationalPreloadDataRepository;
81
82     @Autowired
83     private ConfigServicesRepository configServicesRepository;
84
85     @Autowired
86     private OperationalServicesRepository operationalServicesRepository;
87
88     private static class Iso8601Util {
89
90         private static TimeZone timeZone = TimeZone.getTimeZone("UTC");
91         private static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
92
93         private Iso8601Util() {
94         }
95
96         static {
97             dateFormat.setTimeZone(timeZone);
98         }
99
100         private static String now() {
101             return dateFormat.format(new Date());
102         }
103     }
104
105     @org.springframework.beans.factory.annotation.Autowired
106     public OperationsApiController(ObjectMapper objectMapper, HttpServletRequest request) {
107         objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
108         objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
109         this.objectMapper = objectMapper;
110         this.request = request;
111     }
112
113     @Override
114     public Optional<ObjectMapper> getObjectMapper() {
115         return Optional.ofNullable(objectMapper);
116     }
117
118     @Override
119     public Optional<HttpServletRequest> getRequest() {
120         return Optional.ofNullable(request);
121     }
122
123     @Override
124     public ResponseEntity<GenericResourceApiPreloadNetworkTopologyOperation> operationsGENERICRESOURCEAPIpreloadNetworkTopologyOperationPost(
125             @Valid GenericResourceApiPreloadnetworktopologyoperationInputBodyparam graInput) {
126         final String svcOperation = "preload-network-topology-operation";
127         GenericResourceApiPreloadNetworkTopologyOperation retval = new GenericResourceApiPreloadNetworkTopologyOperation();
128         GenericResourceApiPreloadTopologyResponseBody resp = new GenericResourceApiPreloadTopologyResponseBody();
129
130         log.info(CALLED_STR, svcOperation);
131         if (hasInvalidPreloadNetwork(graInput)) {
132             log.debug("exiting {} because of null or empty preload-network-topology-information", svcOperation);
133
134             resp.setResponseCode("403");
135             resp.setResponseMessage("invalid input, null or empty preload-network-topology-information");
136             resp.setAckFinalIndicator("Y");
137
138             retval.setOutput(resp);
139
140             return new ResponseEntity<>(retval, HttpStatus.FORBIDDEN);
141         }
142
143         String preloadId = graInput.getInput().getPreloadNetworkTopologyInformation()
144                 .getNetworkTopologyIdentifierStructure().getNetworkId();
145         String preloadType = "network";
146
147         resp.setSvcRequestId(graInput.getInput().getSdncRequestHeader().getSvcRequestId());
148
149         SvcLogicContext ctxIn = new SvcLogicContext();
150
151         GenericResourceApiPreloaddataPreloadData preloadData = null;
152
153         // Add input to SvcLogicContext
154         try {
155             ctxIn.mergeJson(svcOperation + "-input", objectMapper.writeValueAsString(graInput.getInput()));
156         } catch (JsonProcessingException e) {
157             log.error("exiting {} due to parse error on input preload data", svcOperation);
158             resp.setResponseCode("500");
159             resp.setResponseMessage("internal error");
160             resp.setAckFinalIndicator("Y");
161             retval.setOutput(resp);
162             return new ResponseEntity<>(retval, HttpStatus.INTERNAL_SERVER_ERROR);
163         }
164
165         // Add config tree data to SvcLogicContext
166         try {
167             preloadData = getConfigPreloadData(preloadId, preloadType);
168             ctxIn.mergeJson("preload-data", objectMapper.writeValueAsString(preloadData));
169         } catch (JsonProcessingException e) {
170             log.error("exiting {} due to parse error on saved config preload data", svcOperation);
171             resp.setResponseCode("500");
172             resp.setResponseMessage("internal error");
173             resp.setAckFinalIndicator("Y");
174             retval.setOutput(resp);
175             return new ResponseEntity<>(retval, HttpStatus.INTERNAL_SERVER_ERROR);
176         }
177
178         // Add operational tree data to SvcLogicContext
179         try {
180             preloadData = getOperationalPreloadData(preloadId, preloadType);
181             ctxIn.mergeJson("operational-data", objectMapper.writeValueAsString(preloadData));
182         } catch (JsonProcessingException e) {
183             log.error("exiting {} due to parse error on saved operational preload data", svcOperation);
184             resp.setResponseCode("500");
185             resp.setResponseMessage("internal error");
186             resp.setAckFinalIndicator("Y");
187             retval.setOutput(resp);
188             return new ResponseEntity<>(retval, HttpStatus.INTERNAL_SERVER_ERROR);
189         }
190
191         // Call DG
192         try {
193             // Any of these can throw a nullpointer exception
194             // execute should only throw a SvcLogicException
195             SvcLogicContext ctxOut = svc.execute(MODULE_NAME, svcOperation, null, "sync", ctxIn);
196             Properties respProps = ctxOut.toProperties();
197
198             resp.setAckFinalIndicator(respProps.getProperty("ack-final-indicator", "Y"));
199             resp.setResponseCode(respProps.getProperty("error-code", "200"));
200             resp.setResponseMessage(respProps.getProperty("error-message", "SUCCESS"));
201
202             if ("200".equals(resp.getResponseCode())) {
203                 // If DG returns success, update database
204                 String ctxJson = ctxOut.toJsonString("preload-data");
205                 log.info("DG preload-data is {}", ctxJson);
206                 GenericResourceApiPreloaddataPreloadData preloadToLoad = objectMapper.readValue(ctxJson,
207                         GenericResourceApiPreloaddataPreloadData.class);
208                 saveConfigPreloadData(preloadId, preloadType, preloadToLoad);
209                 saveOperationalPreloadData(preloadId, preloadType, preloadToLoad);
210             }
211
212         } catch (NullPointerException npe) {
213             log.error("Caught NPE", npe);
214             resp.setAckFinalIndicator("true");
215             resp.setResponseCode("500");
216             resp.setResponseMessage("Check that you populated module, rpc and or mode correctly.");
217         } catch (SvcLogicException e) {
218             log.error("Caught SvcLogicException", e);
219             resp.setAckFinalIndicator("true");
220             resp.setResponseCode("500");
221             resp.setResponseMessage(e.getMessage());
222         } catch (JsonMappingException e) {
223             log.error("Caught JsonMappingException", e);
224             resp.setAckFinalIndicator("true");
225             resp.setResponseCode("500");
226             resp.setResponseMessage(e.getMessage());
227         } catch (JsonProcessingException e) {
228             log.error("Caught JsonProcessingException", e);
229             resp.setAckFinalIndicator("true");
230             resp.setResponseCode("500");
231             resp.setResponseMessage(e.getMessage());
232         }
233
234         retval.setOutput(resp);
235         return (new ResponseEntity<>(retval, HttpStatus.valueOf(Integer.parseInt(resp.getResponseCode()))));
236     }
237
238     @Override
239     public ResponseEntity<GenericResourceApiPreloadVfModuleTopologyOperation> operationsGENERICRESOURCEAPIpreloadVfModuleTopologyOperationPost(
240             @Valid GenericResourceApiPreloadvfmoduletopologyoperationInputBodyparam graInput) {
241         final String svcOperation = "preload-vf-module-topology-operation";
242         GenericResourceApiPreloadVfModuleTopologyOperation retval = new GenericResourceApiPreloadVfModuleTopologyOperation();
243         GenericResourceApiPreloadTopologyResponseBody resp = new GenericResourceApiPreloadTopologyResponseBody();
244
245         log.info(CALLED_STR, svcOperation);
246         if (hasInvalidPreloadNetwork(graInput)) {
247             log.debug("exiting {} because of null or empty preload-network-topology-information", svcOperation);
248
249             resp.setResponseCode("403");
250             resp.setResponseMessage("invalid input, null or empty preload-network-topology-information");
251             resp.setAckFinalIndicator("Y");
252
253             retval.setOutput(resp);
254
255             return new ResponseEntity<>(retval, HttpStatus.FORBIDDEN);
256         }
257
258         String preloadId = graInput.getInput().getPreloadVfModuleTopologyInformation().getVfModuleTopology()
259                 .getVfModuleTopologyIdentifier().getVfModuleName();
260         String preloadType = "vf-module";
261
262         resp.setSvcRequestId(graInput.getInput().getSdncRequestHeader().getSvcRequestId());
263
264         SvcLogicContext ctxIn = new SvcLogicContext();
265
266         GenericResourceApiPreloaddataPreloadData preloadData = null;
267
268         // Add input to SvcLogicContext
269         try {
270             ctxIn.mergeJson(svcOperation + "-input", objectMapper.writeValueAsString(graInput.getInput()));
271         } catch (JsonProcessingException e) {
272             log.error("exiting {} due to parse error on input preload data", svcOperation);
273             resp.setResponseCode("500");
274             resp.setResponseMessage("internal error");
275             resp.setAckFinalIndicator("Y");
276             retval.setOutput(resp);
277             return new ResponseEntity<>(retval, HttpStatus.INTERNAL_SERVER_ERROR);
278         }
279
280         // Add config tree data to SvcLogicContext
281         try {
282             preloadData = getConfigPreloadData(preloadId, preloadType);
283             ctxIn.mergeJson("preload-data", objectMapper.writeValueAsString(preloadData));
284         } catch (JsonProcessingException e) {
285             log.error("exiting {} due to parse error on saved config preload data", svcOperation);
286             resp.setResponseCode("500");
287             resp.setResponseMessage("internal error");
288             resp.setAckFinalIndicator("Y");
289             retval.setOutput(resp);
290             return new ResponseEntity<>(retval, HttpStatus.INTERNAL_SERVER_ERROR);
291         }
292
293         // Add operational tree data to SvcLogicContext
294         try {
295             preloadData = getOperationalPreloadData(preloadId, preloadType);
296             ctxIn.mergeJson("operational-data", objectMapper.writeValueAsString(preloadData));
297         } catch (JsonProcessingException e) {
298             log.error("exiting {} due to parse error on saved operational preload data", svcOperation);
299             resp.setResponseCode("500");
300             resp.setResponseMessage("internal error");
301             resp.setAckFinalIndicator("Y");
302             retval.setOutput(resp);
303             return new ResponseEntity<>(retval, HttpStatus.INTERNAL_SERVER_ERROR);
304         }
305
306         // Call DG
307         try {
308             // Any of these can throw a nullpointer exception
309             // execute should only throw a SvcLogicException
310             SvcLogicContext ctxOut = svc.execute(MODULE_NAME, svcOperation, null, "sync", ctxIn);
311             Properties respProps = ctxOut.toProperties();
312
313             resp.setAckFinalIndicator(respProps.getProperty("ack-final-indicator", "Y"));
314             resp.setResponseCode(respProps.getProperty("error-code", "200"));
315             resp.setResponseMessage(respProps.getProperty("error-message", "SUCCESS"));
316
317             if ("200".equals(resp.getResponseCode())) {
318                 // If DG returns success, update database
319                 String ctxJson = ctxOut.toJsonString("preload-data");
320                 GenericResourceApiPreloaddataPreloadData preloadToLoad = objectMapper.readValue(ctxJson,
321                         GenericResourceApiPreloaddataPreloadData.class);
322                 saveConfigPreloadData(preloadId, preloadType, preloadToLoad);
323                 saveOperationalPreloadData(preloadId, preloadType, preloadToLoad);
324             }
325
326         } catch (NullPointerException npe) {
327             resp.setAckFinalIndicator("true");
328             resp.setResponseCode("500");
329             resp.setResponseMessage("Check that you populated module, rpc and or mode correctly.");
330         } catch (SvcLogicException e) {
331             resp.setAckFinalIndicator("true");
332             resp.setResponseCode("500");
333             resp.setResponseMessage(e.getMessage());
334         } catch (JsonMappingException e) {
335             resp.setAckFinalIndicator("true");
336             resp.setResponseCode("500");
337             resp.setResponseMessage(e.getMessage());
338         } catch (JsonProcessingException e) {
339             resp.setAckFinalIndicator("true");
340             resp.setResponseCode("500");
341             resp.setResponseMessage(e.getMessage());
342         }
343
344         retval.setOutput(resp);
345         return (new ResponseEntity<>(retval, HttpStatus.valueOf(Integer.parseInt(resp.getResponseCode()))));
346     }
347
348     private boolean hasInvalidPreloadNetwork(
349             GenericResourceApiPreloadnetworktopologyoperationInputBodyparam preloadData) {
350         return ((preloadData == null) || (preloadData.getInput() == null)
351                 || (preloadData.getInput().getPreloadNetworkTopologyInformation() == null));
352     }
353
354     private boolean hasInvalidPreloadNetwork(
355             GenericResourceApiPreloadvfmoduletopologyoperationInputBodyparam preloadData) {
356         return ((preloadData == null) || (preloadData.getInput() == null)
357                 || (preloadData.getInput().getPreloadVfModuleTopologyInformation() == null));
358     }
359
360     private boolean hasInvalidServiceId(GenericResourceApiServiceOperationInformation input) {
361
362         return input == null || input.getServiceInformation() == null
363                 || input.getServiceInformation().getServiceInstanceId() == null
364                 || input.getServiceInformation().getServiceInstanceId().length() == 0;
365     }
366
367     private GenericResourceApiPreloaddataPreloadData getConfigPreloadData(String preloadId, String preloadType)
368             throws JsonProcessingException {
369
370         List<ConfigPreloadData> configPreloadData = configPreloadDataRepository.findByPreloadIdAndPreloadType(preloadId,
371                 preloadType);
372
373         if (configPreloadData.isEmpty()) {
374             return (null);
375         } else {
376             return (objectMapper.readValue(configPreloadData.get(0).getPreloadData(),
377                     GenericResourceApiPreloaddataPreloadData.class));
378         }
379     }
380
381     private GenericResourceApiPreloaddataPreloadData getOperationalPreloadData(String preloadId, String preloadType)
382             throws JsonProcessingException {
383
384         List<OperationalPreloadData> configPreloadData = operationalPreloadDataRepository
385                 .findByPreloadIdAndPreloadType(preloadId, preloadType);
386
387         if (configPreloadData.isEmpty()) {
388             return (null);
389         } else {
390             return (objectMapper.readValue(configPreloadData.get(0).getPreloadData(),
391                     GenericResourceApiPreloaddataPreloadData.class));
392         }
393     }
394
395     private void saveConfigPreloadData(String preloadId, String preloadType,
396             GenericResourceApiPreloaddataPreloadData preloadData) throws JsonProcessingException {
397
398         configPreloadDataRepository.deleteByPreloadIdAndPreloadType(preloadId, preloadType);
399         configPreloadDataRepository
400                 .save(new ConfigPreloadData(preloadId, preloadType, objectMapper.writeValueAsString(preloadData)));
401
402     }
403
404     private void saveOperationalPreloadData(String preloadId, String preloadType,
405             GenericResourceApiPreloaddataPreloadData preloadData) throws JsonProcessingException {
406
407         operationalPreloadDataRepository.deleteByPreloadIdAndPreloadType(preloadId, preloadType);
408         operationalPreloadDataRepository
409                 .save(new OperationalPreloadData(preloadId, preloadType, objectMapper.writeValueAsString(preloadData)));
410
411     }
412
413     private GenericResourceApiServicedataServiceData getConfigServiceData(String svcInstanceId) throws JsonProcessingException {
414
415         List<ConfigServices> configServices = configServicesRepository.findBySvcInstanceId(svcInstanceId);
416
417         if (configServices.isEmpty()) {
418             return (null);
419         } else {
420             return (objectMapper.readValue(configServices.get(0).getSvcData(),
421                     GenericResourceApiServicedataServiceData.class));
422         }
423     }
424
425
426     @Override
427     public ResponseEntity<GenericResourceApiNetworkTopologyOperation> operationsGENERICRESOURCEAPInetworkTopologyOperationPost(
428             @Valid GenericResourceApiNetworkOperationInformationBodyparam genericResourceApiNetworkOperationInformationBodyParam)
429             throws RestException {
430         // TODO Auto-generated method stub
431         return OperationsApi.super.operationsGENERICRESOURCEAPInetworkTopologyOperationPost(
432                 genericResourceApiNetworkOperationInformationBodyParam);
433     }
434
435     @Override
436     public ResponseEntity<GenericResourceApiServiceTopologyOperation> operationsGENERICRESOURCEAPIserviceTopologyOperationPost(
437             @Valid GenericResourceApiServiceOperationInformationBodyparam input) throws RestException {
438         final String svcOperation = "service-topology-operation";
439         GenericResourceApiServiceTopologyOperation retval = new GenericResourceApiServiceTopologyOperation();
440         GenericResourceApiServicetopologyoperationOutput resp = new GenericResourceApiServicetopologyoperationOutput();
441
442         log.info(CALLED_STR, svcOperation);
443
444         // Verify input contains service instance id
445         if (hasInvalidServiceId(input.getInput())) {
446             log.debug("exiting {} because of null or empty service-instance-id", svcOperation);
447
448             resp.setResponseCode("404");
449             resp.setResponseMessage("null or empty service-instance-id");
450             resp.setAckFinalIndicator("Y");
451
452             retval.setOutput(resp);
453
454             return new ResponseEntity<>(retval, HttpStatus.OK);
455         }
456
457         String svcInstanceId = input.getInput().getServiceInformation().getServiceInstanceId();
458
459         SvcLogicContext ctxIn = new SvcLogicContext();
460
461         // Add input to SvcLogicContext
462         try {
463             ctxIn.mergeJson(svcOperation + "-input", objectMapper.writeValueAsString(input.getInput()));
464         } catch (JsonProcessingException e) {
465             log.error("exiting {} due to parse error on input data", svcOperation);
466             resp.setResponseCode("500");
467             resp.setResponseMessage("internal error");
468             resp.setAckFinalIndicator("Y");
469             retval.setOutput(resp);
470             return new ResponseEntity<>(retval, HttpStatus.INTERNAL_SERVER_ERROR);
471         }
472
473         // Add config tree data to SvcLogicContext
474         List<ConfigServices> configServices = configServicesRepository.findBySvcInstanceId(svcInstanceId);
475         ConfigServices configService = null;
476         if (configServices != null && !configServices.isEmpty()) {
477             configService = configServices.get(0);
478             ctxIn.mergeJson("service-data", configService.getSvcData());
479         } else {
480             configService = new ConfigServices(svcInstanceId, null);
481         }
482
483         // Add operational tree data to SvcLogicContext
484         List<OperationalServices> operServices = operationalServicesRepository.findBySvcInstanceId(svcInstanceId);
485         OperationalServices operService = null;
486         boolean saveOperationalData = false;
487
488         if (operServices != null && !operServices.isEmpty()) {
489             operService = operServices.get(0);
490             ctxIn.mergeJson("operational-data", operService.getSvcData());
491         } else {
492             operService = new OperationalServices(svcInstanceId, null, null);
493         }
494
495         // Update service status info in config entry from input
496         configService.setServiceStatusAction(input.getInput().getRequestInformation().getRequestAction().toString());
497         configService.setServiceStatusRpcAction(input.getInput().getSdncRequestHeader().getSvcAction().toString());
498         configService.setServiceStatusRpcName(svcOperation);
499
500
501         // Call DG
502         try {
503             // Any of these can throw a nullpointer exception
504             // execute should only throw a SvcLogicException
505             SvcLogicContext ctxOut = svc.execute(MODULE_NAME, svcOperation, null, "sync", ctxIn);
506             Properties respProps = ctxOut.toProperties();
507
508             resp.setAckFinalIndicator(respProps.getProperty("ack-final-indicator", "Y"));
509             resp.setResponseCode(respProps.getProperty("error-code", "200"));
510             resp.setResponseMessage(respProps.getProperty("error-message", "SUCCESS"));
511             configService.setServiceStatusRequestStatus(GenericResourceApiRequestStatusEnumeration.SYNCCOMPLETE.toString());
512
513             if ("200".equals(resp.getResponseCode())) {
514                 // If DG returns success, update svcData in config and operational trees
515                 // and remember to save operational data.
516                 String ctxJson = ctxOut.toJsonString("service-data");
517                 configService.setSvcData(ctxJson);
518                 operService.setSvcData(ctxJson);
519                 saveOperationalData = true;
520             }
521
522         } catch (NullPointerException npe) {
523             resp.setAckFinalIndicator("true");
524             resp.setResponseCode("500");
525             resp.setResponseMessage("Check that you populated module, rpc and or mode correctly.");
526         } catch (SvcLogicException e) {
527             resp.setAckFinalIndicator("true");
528             resp.setResponseCode("500");
529             resp.setResponseMessage(e.getMessage());
530         }
531
532         // Update status in config services entry
533         
534         configService.setServiceStatusFinalIndicator(resp.getAckFinalIndicator());
535         configService.setServiceStatusResponseCode(resp.getResponseCode());
536         configService.setServiceStatusResponseMessage(resp.getResponseMessage());
537         configService.setServiceStatusResponseTimestamp(Iso8601Util.now());
538
539         // Update config tree
540         configServicesRepository.save(configService);
541
542         // If necessary, sync status to operation service entry and save
543         if (saveOperationalData) {
544             operService.setServiceStatus(configService.getServiceStatus());
545             operationalServicesRepository.save(operService);
546         }
547         retval.setOutput(resp);
548         return (new ResponseEntity<>(retval, HttpStatus.OK));
549
550     }
551
552 }