2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2023-2024 Nordix Foundation
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.cps.ncmp.dmi.rest.stub.controller;
23 import static org.onap.cps.ncmp.api.NcmpResponseStatus.SUCCESS;
25 import com.fasterxml.jackson.core.JsonProcessingException;
26 import com.fasterxml.jackson.databind.ObjectMapper;
27 import io.cloudevents.CloudEvent;
28 import io.cloudevents.core.builder.CloudEventBuilder;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.UUID;
33 import lombok.RequiredArgsConstructor;
34 import lombok.extern.slf4j.Slf4j;
35 import org.json.simple.parser.JSONParser;
36 import org.json.simple.parser.ParseException;
37 import org.onap.cps.ncmp.api.impl.utils.EventDateTimeFormatter;
38 import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.CmHandle;
39 import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DataOperationRequest;
40 import org.onap.cps.ncmp.dmi.rest.stub.model.data.operational.DmiDataOperationRequest;
41 import org.onap.cps.ncmp.dmi.rest.stub.utils.ResourceFileReaderUtil;
42 import org.onap.cps.ncmp.events.async1_0_0.Data;
43 import org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent;
44 import org.onap.cps.ncmp.events.async1_0_0.Response;
45 import org.springframework.beans.factory.annotation.Value;
46 import org.springframework.context.ApplicationContext;
47 import org.springframework.core.io.Resource;
48 import org.springframework.core.io.ResourceLoader;
49 import org.springframework.http.HttpStatus;
50 import org.springframework.http.ResponseEntity;
51 import org.springframework.kafka.core.KafkaTemplate;
52 import org.springframework.web.bind.annotation.PathVariable;
53 import org.springframework.web.bind.annotation.PostMapping;
54 import org.springframework.web.bind.annotation.RequestBody;
55 import org.springframework.web.bind.annotation.RequestMapping;
56 import org.springframework.web.bind.annotation.RequestParam;
57 import org.springframework.web.bind.annotation.RestController;
60 @RequestMapping("${rest.api.dmi-stub-base-path}")
61 @RequiredArgsConstructor
63 public class DmiRestStubController {
65 private final KafkaTemplate<String, CloudEvent> cloudEventKafkaTemplate;
66 private final ObjectMapper objectMapper;
67 private final ApplicationContext applicationContext;
69 @Value("${app.ncmp.async-m2m.topic}")
70 private String ncmpAsyncM2mTopic;
72 @Value("${delay.module-references-delay-ms}")
73 private long moduleReferencesDelayMs;
75 @Value("${delay.module-resources-delay-ms}")
76 private long moduleResourcesDelayMs;
78 @Value("${delay.data-for-cm-handle-delay-ms}")
79 private long dataForCmHandleDelayMs;
81 private String dataOperationEventType = "org.onap.cps.ncmp.events.async1_0_0.DataOperationEvent";
84 * Get all modules for given cm handle.
86 * @param cmHandleId The identifier for a network function, network element, subnetwork,
87 * or any other cm object by managed Network CM Proxy
88 * @param moduleReferencesRequest module references request body
89 * @return ResponseEntity response entity having module response as json string.
91 @PostMapping("/v1/ch/{cmHandleId}/modules")
92 public ResponseEntity<String> getModuleReferences(@PathVariable("cmHandleId") final String cmHandleId,
93 @RequestBody final Object moduleReferencesRequest) {
94 delay(moduleReferencesDelayMs);
95 final String moduleResponseContent = getModuleResourceResponse(cmHandleId,
96 "ModuleResponse.json");
97 log.info("cm handle: {} requested for modules", cmHandleId);
98 return ResponseEntity.ok(moduleResponseContent);
102 * Retrieves module resources for a given cmHandleId.
104 * @param cmHandleId The identifier for a network function, network element, subnetwork,
105 * or any other cm object by managed Network CM Proxy
106 * @param moduleResourcesReadRequest module resources read request body
107 * @return ResponseEntity response entity having module resources response as json string.
109 @PostMapping("/v1/ch/{cmHandleId}/moduleResources")
110 public ResponseEntity<String> retrieveModuleResources(
111 @PathVariable("cmHandleId") final String cmHandleId,
112 @RequestBody final Object moduleResourcesReadRequest) {
113 delay(moduleResourcesDelayMs);
114 final String moduleResourcesResponseContent = getModuleResourceResponse(cmHandleId,
115 "ModuleResourcesResponse.json");
116 log.info("cm handle: {} requested for modules resources", cmHandleId);
117 return ResponseEntity.ok(moduleResourcesResponseContent);
121 * Get resource data from passthrough operational or running for a cm handle.
123 * @param cmHandleId The identifier for a network function, network element, subnetwork,
124 * or any other cm object by managed Network CM Proxy
125 * @param datastoreName datastore name
126 * @param resourceIdentifier resource identifier
127 * @param options options
128 * @param topic client given topic name
129 * @return (@ code ResponseEntity) response entity
131 @PostMapping("/v1/ch/{cmHandleId}/data/ds/{datastoreName}")
132 public ResponseEntity<String> getResourceDataForCmHandle(
133 @PathVariable("cmHandleId") final String cmHandleId,
134 @PathVariable("datastoreName") final String datastoreName,
135 @RequestParam(value = "resourceIdentifier") final String resourceIdentifier,
136 @RequestParam(value = "options", required = false) final String options,
137 @RequestParam(value = "topic", required = false) final String topic) {
138 delay(dataForCmHandleDelayMs);
139 final String sampleJson = ResourceFileReaderUtil.getResourceFileContent(applicationContext.getResource(
140 ResourceLoader.CLASSPATH_URL_PREFIX + "data/operational/ietf-network-topology-sample-rfc8345.json"));
141 return ResponseEntity.ok(sampleJson);
145 * This method is not implemented for ONAP DMI plugin.
147 * @param topic client given topic name
148 * @param requestId requestId generated by NCMP as an ack for client
149 * @param dmiDataOperationRequest list of operation details
150 * @return (@ code ResponseEntity) response entity
152 @PostMapping("/v1/data")
153 public ResponseEntity<Void> getResourceDataForCmHandleDataOperation(
154 @RequestParam(value = "topic") final String topic,
155 @RequestParam(value = "requestId") final String requestId,
156 @RequestBody final DmiDataOperationRequest dmiDataOperationRequest) {
157 delay(dataForCmHandleDelayMs);
159 log.info("Request received from the NCMP to DMI Plugin: {}",
160 objectMapper.writeValueAsString(dmiDataOperationRequest));
161 } catch (final JsonProcessingException jsonProcessingException) {
162 log.info("Unable to process dmi data operation request to json string");
164 dmiDataOperationRequest.getOperations().forEach(dmiDataOperation -> {
165 final DataOperationEvent dataOperationEvent = getDataOperationEvent(dmiDataOperation);
166 dmiDataOperation.getCmHandles().forEach(cmHandle -> {
167 dataOperationEvent.getData().getResponses().get(0).setIds(List.of(cmHandle.getId()));
168 final CloudEvent cloudEvent = buildAndGetCloudEvent(topic, requestId, dataOperationEvent);
169 cloudEventKafkaTemplate.send(ncmpAsyncM2mTopic, UUID.randomUUID().toString(), cloudEvent);
172 return new ResponseEntity<>(HttpStatus.ACCEPTED);
175 private CloudEvent buildAndGetCloudEvent(final String topic, final String requestId,
176 final DataOperationEvent dataOperationEvent) {
177 CloudEvent cloudEvent = null;
179 cloudEvent = CloudEventBuilder.v1()
180 .withId(UUID.randomUUID().toString())
181 .withSource(URI.create("DMI"))
182 .withType(dataOperationEventType)
183 .withDataSchema(URI.create("urn:cps:" + dataOperationEventType + ":1.0.0"))
184 .withTime(EventDateTimeFormatter.toIsoOffsetDateTime(
185 EventDateTimeFormatter.getCurrentIsoFormattedDateTime()))
186 .withData(objectMapper.writeValueAsBytes(dataOperationEvent))
187 .withExtension("destination", topic)
188 .withExtension("correlationid", requestId)
190 } catch (final JsonProcessingException jsonProcessingException) {
191 log.error("Unable to parse event into bytes. cause : {}", jsonProcessingException.getMessage());
196 private DataOperationEvent getDataOperationEvent(final DataOperationRequest dataOperationRequest) {
197 final Response response = new Response();
198 response.setOperationId(dataOperationRequest.getOperationId());
199 response.setStatusCode(SUCCESS.getCode());
200 response.setStatusMessage(SUCCESS.getMessage());
201 response.setIds(dataOperationRequest.getCmHandles().stream().map(CmHandle::getId).toList());
202 response.setResourceIdentifier(dataOperationRequest.getResourceIdentifier());
203 response.setOptions(dataOperationRequest.getOptions());
204 final String ietfNetworkTopologySample = ResourceFileReaderUtil
205 .getResourceFileContent(applicationContext.getResource(
206 ResourceLoader.CLASSPATH_URL_PREFIX
207 + "data/operational/ietf-network-topology-sample-rfc8345.json"));
208 final JSONParser jsonParser = new JSONParser();
210 response.setResult(jsonParser.parse(ietfNetworkTopologySample));
211 } catch (final ParseException parseException) {
212 log.error("Unable to parse event result as json object. cause : {}", parseException.getMessage());
214 final List<Response> responseList = new ArrayList<>(1);
215 responseList.add(response);
216 final Data data = new Data();
217 data.setResponses(responseList);
218 final DataOperationEvent dataOperationEvent = new DataOperationEvent();
219 dataOperationEvent.setData(data);
220 return dataOperationEvent;
223 private String getModuleResourceResponse(final String cmHandleId, final String moduleResponseType) {
224 final String nodeType = cmHandleId.split("-")[0];
225 final String moduleResponseFilePath = String.format("module/%s%s", nodeType, moduleResponseType);
226 final Resource moduleResponseResource = applicationContext.getResource(
227 ResourceLoader.CLASSPATH_URL_PREFIX + moduleResponseFilePath);
228 if (moduleResponseResource.exists()) {
229 log.info("Using requested node type: {}", nodeType);
230 return ResourceFileReaderUtil.getResourceFileContent(moduleResponseResource);
232 log.info("Using default node type: ietfYang");
233 return ResourceFileReaderUtil.getResourceFileContent(applicationContext.getResource(
234 ResourceLoader.CLASSPATH_URL_PREFIX + "module/ietfYang" + moduleResponseType));
237 private void delay(final long milliseconds) {
239 Thread.sleep(milliseconds);
240 } catch (final InterruptedException e) {
241 log.error("Thread sleep interrupted: {}", e.getMessage());
242 Thread.currentThread().interrupt();