Bug fixes/mods to dmaap-listener for SDNR OOF PCI
[ccsdk/sli/northbound.git] / dmaap-listener / src / main / java / org / onap / ccsdk / sli / northbound / dmaapclient / OofPciPocDmaapConsumers.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.onap.ccsdk.sli.northbound.dmaapclient;
23
24 import java.io.BufferedReader;
25 import java.io.File;
26 import java.io.FileReader;
27 import java.io.IOException;
28 import java.io.StringWriter;
29 import java.io.Writer;
30 import java.time.Instant;
31 import java.util.HashMap;
32 import java.util.Map;
33 import java.util.Properties;
34 import org.apache.velocity.VelocityContext;
35 import org.apache.velocity.app.VelocityEngine;
36 import org.json.JSONArray;
37 import org.json.JSONObject;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40 import com.fasterxml.jackson.databind.JsonNode;
41 import com.fasterxml.jackson.databind.ObjectMapper;
42
43 public class OofPciPocDmaapConsumers extends SdncDmaapConsumerImpl {
44
45     private static final Logger LOG = LoggerFactory.getLogger(OofPciPocDmaapConsumers.class);
46     private static final String SDNC_ENDPOINT = "SDNC.endpoint";
47     private static final String TEMPLATE = "SDNC.template";
48     private static final String DMAAPLISTENERROOT = "DMAAPLISTENERROOT";
49     private static final String UTF_8 = "UTF-8";
50
51     private static final String PARAMETER_NAME = "parameter-name";
52     private static final String STRING_VALUE = "string-value";
53     private static final String PHYSICAL_CELL_ID_INPUT_FAP_SERVICE = "configuration-phy-cell-id-input.fap-service";
54     private static final String EVENT_HEADER = "event-header";
55         private static final String ACTION = "Action";
56         private static final String CONFIGURATIONS = "Configurations";
57         private static final String MODIFY_CONFIG = "ModifyConfig";
58         private static final String DATA = "data";
59         private static final String FAP_SERVICE = "FAPService";
60         
61         private static final String PAYLOAD = "Payload";
62         private static final String PCI_CHANGES_MAP_FILE_NAME = "pci-changes-from-policy-to-sdnr";
63         private static final String SLI_PARAMETERS = "sli_parameters";
64         private static final String RPC_NAME = "rpc-name";
65         private static final String BODY = "body";
66         private static final String INPUT = "input";
67         
68         private static final String EMPTY = "";
69         private static final String ESCAPE_SEQUENCE_QUOTES = "\"";
70         
71     private static final String GENERIC_NEIGHBOR_CONFIGURATION_INPUT = "generic-neighbor-configuration-input.";
72     private static final String GENERIC_NEIGHBOR_CONFIGURATION_INPUT_NEIGHBOR_LIST_IN_USE = GENERIC_NEIGHBOR_CONFIGURATION_INPUT.concat("neighbor-list-in-use");
73         private static final String MODIFY_CONFIG_ANR = "ModifyConfigANR";
74         private static final String ANR_CHANGES_MAP_FILE_NAME = "anr-changes-from-policy-to-sdnr";
75
76     private String rootDir;
77
78     protected VelocityEngine velocityEngine;
79
80     public OofPciPocDmaapConsumers() {
81         velocityEngine = new VelocityEngine();
82         Properties props = new Properties();
83         rootDir = System.getenv(DMAAPLISTENERROOT);
84
85         if ((rootDir == null) || (rootDir.length() == 0)) {
86             rootDir = "/opt/app/dmaap-listener/lib/";
87         }
88         else {
89             rootDir = rootDir + "/lib/";
90         }
91
92         props.put("file.resource.loader.path", rootDir);
93         velocityEngine.init(props);
94     }
95
96     /*
97      * for testing purposes
98      */
99     OofPciPocDmaapConsumers(Properties props) {
100         velocityEngine = new VelocityEngine();
101         velocityEngine.init(props);
102     }
103
104     protected String publish(String templatePath, String jsonString, JsonNode configurationsOrDataNode, boolean invokePciChangesPublish, boolean invokeAnrChangesPublish) throws IOException, InvalidMessageException
105     {
106         if (invokePciChangesPublish){
107             return publishPciChangesFromPolicyToSDNR(templatePath, configurationsOrDataNode);
108         } else if (invokeAnrChangesPublish){
109             return publishANRChangesFromPolicyToSDNR(templatePath, configurationsOrDataNode);
110         } else {
111             return publishFullMessage(templatePath, jsonString);
112         }
113     }
114
115     private String publishFullMessage(String templatePath, String jsonString) throws IOException
116     {
117         JSONObject jsonObj = new JSONObject(jsonString);
118         VelocityContext context = new VelocityContext();
119         for(Object key : jsonObj.keySet())
120         {
121             context.put((String)key, jsonObj.get((String)key));
122         }
123
124         String id = jsonObj.getJSONObject(EVENT_HEADER).get("id").toString();
125         context.put("req_id", id);
126
127         context.put("curr_time", Instant.now());
128
129         ObjectMapper oMapper = new ObjectMapper();
130
131         String rpcMsgbody = oMapper.writeValueAsString(jsonString);
132         context.put("full_message", rpcMsgbody);
133
134         Writer writer = new StringWriter();
135         velocityEngine.mergeTemplate(templatePath, UTF_8, context, writer);
136         writer.flush();
137
138         return writer.toString();
139     }
140     
141     private String publishANRChangesFromPolicyToSDNR(String templatePath, JsonNode dataNode) throws IOException, InvalidMessageException
142     {
143         VelocityContext context = new VelocityContext();
144         
145         String RPC_NAME_KEY_IN_VT = "rpc_name";
146         String RPC_NAME_VALUE_IN_VT = "generic-neighbor-configuration";
147         
148         String CELL_CONFIG = "CellConfig";
149         String ALIAS_LABEL = "alias";
150         String LTE = "LTE";
151         String RAN = "RAN";
152         String LTE_CELL = "LTECell";
153         String NEIGHBOR_LIST_IN_USE = "NeighborListInUse";
154         
155         JSONObject numberOfEntries = new JSONObject();
156         JSONObject alias = new JSONObject();
157         JSONArray sliParametersArray = new JSONArray();
158         
159         String aliasValue =  dataNode.get(DATA).get(FAP_SERVICE).get(ALIAS_LABEL).textValue();
160         
161         JsonNode nbrListInUse = dataNode.get(DATA).get(FAP_SERVICE).get(CELL_CONFIG).get(LTE).get(RAN).get(NEIGHBOR_LIST_IN_USE).get(LTE_CELL);
162         
163         int entryCount = 0;
164         
165         if(nbrListInUse.isArray()) {
166                 for(JsonNode lteCell:nbrListInUse) {
167                         sliParametersArray.put(new JSONObject().put(PARAMETER_NAME, GENERIC_NEIGHBOR_CONFIGURATION_INPUT_NEIGHBOR_LIST_IN_USE+"["+entryCount+"]."+"plmnid")
168                                 .put(STRING_VALUE, lteCell.get("PLMNID").toString().replace(ESCAPE_SEQUENCE_QUOTES, EMPTY)));
169                         sliParametersArray.put(new JSONObject().put(PARAMETER_NAME, GENERIC_NEIGHBOR_CONFIGURATION_INPUT_NEIGHBOR_LIST_IN_USE+"["+entryCount+"]."+"cid")
170                                 .put(STRING_VALUE, lteCell.get("CID").toString().replace(ESCAPE_SEQUENCE_QUOTES, EMPTY)));
171                         sliParametersArray.put(new JSONObject().put(PARAMETER_NAME, GENERIC_NEIGHBOR_CONFIGURATION_INPUT_NEIGHBOR_LIST_IN_USE+"["+entryCount+"]."+"phy-cell-id")
172                                 .put(STRING_VALUE, lteCell.get("PhyCellID").toString().replace(ESCAPE_SEQUENCE_QUOTES, EMPTY)));
173                         sliParametersArray.put(new JSONObject().put(PARAMETER_NAME, GENERIC_NEIGHBOR_CONFIGURATION_INPUT_NEIGHBOR_LIST_IN_USE+"["+entryCount+"]."+"pnf-name")
174                                 .put(STRING_VALUE, lteCell.get("PNFName").toString().replace(ESCAPE_SEQUENCE_QUOTES, EMPTY)));
175                         sliParametersArray.put(new JSONObject().put(PARAMETER_NAME, GENERIC_NEIGHBOR_CONFIGURATION_INPUT_NEIGHBOR_LIST_IN_USE+"["+entryCount+"]."+"blacklisted")
176                                 .put(STRING_VALUE, lteCell.get("Blacklisted").toString().replace(ESCAPE_SEQUENCE_QUOTES, EMPTY)));
177                         
178                         entryCount++;
179                 }
180                 
181                 alias.put(PARAMETER_NAME, GENERIC_NEIGHBOR_CONFIGURATION_INPUT+ALIAS_LABEL);
182             alias.put(STRING_VALUE, aliasValue);
183             
184             numberOfEntries.put(PARAMETER_NAME, GENERIC_NEIGHBOR_CONFIGURATION_INPUT+"number-of-neighbor-cell-entries");
185             numberOfEntries.put(STRING_VALUE, entryCount);
186             
187             sliParametersArray.put(alias);
188             sliParametersArray.put(numberOfEntries);
189             
190             context.put(SLI_PARAMETERS, sliParametersArray);
191             
192             context.put(RPC_NAME_KEY_IN_VT, RPC_NAME_VALUE_IN_VT);
193
194             Writer writer = new StringWriter();
195             velocityEngine.mergeTemplate(templatePath, UTF_8, context, writer);
196             writer.flush();
197
198             return writer.toString();
199                 
200         }else {
201                 throw new InvalidMessageException("nbrListInUse is not of Type Array. Could not read neighbor list elements");
202         }
203         
204     }
205     
206     private String publishPciChangesFromPolicyToSDNR(String templatePath, JsonNode configurationsJsonNode) throws IOException, InvalidMessageException
207     {
208         String RPC_NAME_KEY_IN_VT = "rpc_name";
209         String RPC_NAME_VALUE_IN_VT = "configuration-phy-cell-id";
210         String ALIAS = "alias";
211         String X0005b9Lte = "X0005b9Lte";
212         
213         VelocityContext context = new VelocityContext();
214         
215         JSONObject numberOfEntries = new JSONObject();
216         JSONArray sliParametersArray = new JSONArray();
217         
218         JsonNode configurations = configurationsJsonNode.get(CONFIGURATIONS);
219         
220         int entryCount = 0;
221         
222         if(configurations.isArray()) {
223                 for(JsonNode dataNode:configurations) {
224                         sliParametersArray.put(new JSONObject().put(PARAMETER_NAME, PHYSICAL_CELL_ID_INPUT_FAP_SERVICE+"["+entryCount+"]."+ALIAS)
225                                 .put(STRING_VALUE, dataNode.get(DATA).get(FAP_SERVICE).get(ALIAS).toString().replace(ESCAPE_SEQUENCE_QUOTES, EMPTY)));
226                         sliParametersArray.put(new JSONObject().put(PARAMETER_NAME, PHYSICAL_CELL_ID_INPUT_FAP_SERVICE+"["+entryCount+"]."+"cid")
227                                 .put(STRING_VALUE, dataNode.get(DATA).get(FAP_SERVICE).get("CellConfig").get("LTE").get("RAN").get("Common").get("CellIdentity").toString().replace(ESCAPE_SEQUENCE_QUOTES, EMPTY)));
228                         sliParametersArray.put(new JSONObject().put(PARAMETER_NAME, PHYSICAL_CELL_ID_INPUT_FAP_SERVICE+"["+entryCount+"]."+"phy-cell-id-in-use")
229                                 .put(STRING_VALUE, dataNode.get(DATA).get(FAP_SERVICE).get(X0005b9Lte).get("phyCellIdInUse").toString().replace(ESCAPE_SEQUENCE_QUOTES, EMPTY)));
230                         sliParametersArray.put(new JSONObject().put(PARAMETER_NAME, PHYSICAL_CELL_ID_INPUT_FAP_SERVICE+"["+entryCount+"]."+"pnf-name")
231                                 .put(STRING_VALUE, dataNode.get(DATA).get(FAP_SERVICE).get(X0005b9Lte).get("pnfName").toString().replace(ESCAPE_SEQUENCE_QUOTES, EMPTY)));
232                         entryCount++;
233                 }
234             
235             numberOfEntries.put(PARAMETER_NAME, PHYSICAL_CELL_ID_INPUT_FAP_SERVICE+"-number-of-entries");
236             numberOfEntries.put(STRING_VALUE, entryCount);
237             
238             sliParametersArray.put(numberOfEntries);
239             
240             context.put(SLI_PARAMETERS, sliParametersArray);
241             
242             context.put(RPC_NAME_KEY_IN_VT, RPC_NAME_VALUE_IN_VT);
243
244             Writer writer = new StringWriter();
245             velocityEngine.mergeTemplate(templatePath, UTF_8, context, writer);
246             writer.flush();
247
248             return writer.toString();
249                 
250         }else {
251                 throw new InvalidMessageException("Configurations is not of Type Array. Could not read configuration changes");
252         }
253         
254     }
255
256     @Override
257     public void processMsg(String msg) throws InvalidMessageException {
258
259         if (msg == null) {
260             throw new InvalidMessageException("Null message");
261         }
262
263         ObjectMapper oMapper = new ObjectMapper();
264         JsonNode dmaapMessageRootNode;
265         try {
266                 dmaapMessageRootNode = oMapper.readTree(msg);
267         } catch (Exception e) {
268             throw new InvalidMessageException("Cannot parse json object", e);
269         }
270         
271
272         JsonNode rpcnameNode = dmaapMessageRootNode.get(RPC_NAME);
273         if(rpcnameNode == null) {
274                  LOG.info("Unable to identify the respective consumer to invoke. Please verify the dmaap message..");
275                  return;
276         }
277         
278         String rpcname = rpcnameNode.textValue();
279         
280         if(!MODIFY_CONFIG.toLowerCase().equals(rpcname) && !MODIFY_CONFIG_ANR.toLowerCase().equals(rpcname)) {
281             LOG.info("Unknown rpc name {}", rpcname);
282             return;
283         }
284         
285         if(MODIFY_CONFIG.toLowerCase().equals(rpcname)) {
286                 invokePCIChangesConsumer(dmaapMessageRootNode, oMapper, msg);
287             return;
288         }
289         
290         if(MODIFY_CONFIG_ANR.toLowerCase().equals(rpcname)) {
291                 invokeANRChangesConsumer(dmaapMessageRootNode, oMapper, msg);
292             return;
293         }
294     
295     }
296
297     private void invokeANRChangesConsumer(JsonNode dmaapMessageRootNode, ObjectMapper oMapper,
298                         String msg) throws InvalidMessageException {
299         JsonNode body = dmaapMessageRootNode.get(BODY);
300         if(body == null) {
301                  LOG.info("Missing body node.");
302                  return;
303         }
304         
305         JsonNode input = body.get(INPUT);
306         if(input == null) {
307                  LOG.info("Missing input node.");
308                  return;
309         }
310         
311         JsonNode action = input.get(ACTION);
312         if(action == null) {
313                  LOG.info("Missing action node.");
314                  return;
315         }
316         
317         if(!MODIFY_CONFIG_ANR.equals(action.textValue())) {
318             LOG.info("Unknown Action {}", action);
319             return;
320         }
321         
322         JsonNode payload = input.get(PAYLOAD);
323         if(payload == null) {
324             LOG.info("Missing payload node.");
325             return;
326         }
327
328         String payloadText = payload.asText();
329         
330         if(!payloadText.contains(CONFIGURATIONS)) {
331          LOG.info("Missing configurations node.");
332          return;
333        }
334         
335        JsonNode configurationsJsonNode;
336             try {
337                 configurationsJsonNode = oMapper.readTree(payloadText);
338             } catch (Exception e) {
339                 throw new InvalidMessageException("Cannot parse payload value", e);
340             }
341
342         String mapFilename = rootDir + ANR_CHANGES_MAP_FILE_NAME + ".map";
343         Map<String, String> fieldMap = loadMap(mapFilename);
344         if (fieldMap == null) {
345             return;
346         }
347
348         if (!fieldMap.containsKey(SDNC_ENDPOINT)) {
349             return;
350         }
351         String sdncEndpoint = fieldMap.get(SDNC_ENDPOINT);
352
353         if (!fieldMap.containsKey(TEMPLATE)) {
354             throw new InvalidMessageException("No SDNC template known for message ");
355         }
356         String templateName = fieldMap.get(TEMPLATE);
357         
358         JsonNode configurations = configurationsJsonNode.get(CONFIGURATIONS);
359         
360         if(configurations.isArray()) {
361                 for(JsonNode dataNode:configurations) {
362                 if(dataNode.get(DATA).get(FAP_SERVICE) == null) {
363                     LOG.info("Could not make a rpc call. Missing fapService node for dataNode element::", dataNode.textValue());
364                 }else {
365                         buildAndInvokeANRChangesRPC(sdncEndpoint, templateName,msg, dataNode);
366                 }
367                 }
368         }else {
369                 throw new InvalidMessageException("Configurations is not of Type Array. Could not read configuration changes");
370         }
371         }
372
373         private void invokePCIChangesConsumer(JsonNode dmaapMessageRootNode, ObjectMapper oMapper,
374                         String msg) throws InvalidMessageException {
375                 JsonNode body = dmaapMessageRootNode.get(BODY);
376         if(body == null) {
377                  LOG.info("Missing body node.");
378                  return;
379         }
380         
381         JsonNode input = body.get(INPUT);
382         if(input == null) {
383                  LOG.info("Missing input node.");
384                  return;
385         }
386         
387         JsonNode action = input.get(ACTION);
388         if(action == null) {
389                  LOG.info("Missing action node.");
390                  return;
391         }
392         
393         if(!MODIFY_CONFIG.equals(action.textValue())) {
394             LOG.info("Unknown Action {}", action);
395             return;
396         }
397         
398         JsonNode payload = input.get(PAYLOAD);
399         if(payload == null) {
400             LOG.info("Missing payload node.");
401             return;
402         }
403
404         String configurations = payload.asText();
405         
406         if(!configurations.contains(CONFIGURATIONS)) {
407          LOG.info("Missing configurations node.");
408          return;
409        }
410         
411        JsonNode configurationsJsonNode;
412             try {
413                 configurationsJsonNode = oMapper.readTree(configurations);
414             } catch (Exception e) {
415                 throw new InvalidMessageException("Cannot parse payload value", e);
416             }
417
418         String mapFilename = rootDir + PCI_CHANGES_MAP_FILE_NAME + ".map";
419         Map<String, String> fieldMap = loadMap(mapFilename);
420         if (fieldMap == null) {
421             return;
422         }
423
424         if (!fieldMap.containsKey(SDNC_ENDPOINT)) {
425             return;
426         }
427         String sdncEndpoint = fieldMap.get(SDNC_ENDPOINT);
428
429         if (!fieldMap.containsKey(TEMPLATE)) {
430             throw new InvalidMessageException("No SDNC template known for message ");
431         }
432         String templateName = fieldMap.get(TEMPLATE);
433         
434         buildAndInvokePCIChangesRPC(sdncEndpoint, templateName, msg, configurationsJsonNode);
435         }
436
437         private void buildAndInvokePCIChangesRPC(String sdncEndpoint, String templateName, String msg, JsonNode configurationsOrDataNode) {
438                 try {
439             String rpcMsgbody = publish(templateName, msg, configurationsOrDataNode, true, false);
440             String odlUrlBase = getProperty("sdnc.odl.url-base");
441             String odlUser = getProperty("sdnc.odl.user");
442             String odlPassword = getProperty("sdnc.odl.password");
443
444             if ((odlUrlBase != null) && (odlUrlBase.length() > 0)) {
445                 SdncOdlConnection conn = SdncOdlConnection.newInstance(odlUrlBase + "/" + sdncEndpoint, odlUser, odlPassword);
446
447                 conn.send("POST", "application/json", rpcMsgbody);
448             } else {
449                 LOG.info("POST message body would be:\n" + rpcMsgbody);
450             }
451         } catch (Exception e) {
452             LOG.error("Unable to process message", e);
453         }
454         }
455         
456         private void buildAndInvokeANRChangesRPC(String sdncEndpoint, String templateName, String msg, JsonNode configurationsOrDataNode) {
457                 try {
458             String rpcMsgbody = publish(templateName, msg, configurationsOrDataNode, false, true);
459             String odlUrlBase = getProperty("sdnc.odl.url-base");
460             String odlUser = getProperty("sdnc.odl.user");
461             String odlPassword = getProperty("sdnc.odl.password");
462
463             if ((odlUrlBase != null) && (odlUrlBase.length() > 0)) {
464                 SdncOdlConnection conn = SdncOdlConnection.newInstance(odlUrlBase + "/" + sdncEndpoint, odlUser, odlPassword);
465
466                 conn.send("POST", "application/json", rpcMsgbody);
467             } else {
468                 LOG.info("POST message body would be:\n" + rpcMsgbody);
469             }
470         } catch (Exception e) {
471             LOG.error("Unable to process message", e);
472         }
473         }
474
475         private Map<String, String> loadMap(String mapFilename) {
476         File mapFile = new File(mapFilename);
477
478         if (!mapFile.canRead()) {
479             LOG.error(String.format("Cannot read map file (%s)", mapFilename));
480             return null;
481         }
482
483         Map<String, String> results = new HashMap<>();
484         try (BufferedReader mapReader = new BufferedReader(new FileReader(mapFile))) {
485
486             String curLine;
487
488             while ((curLine = mapReader.readLine()) != null) {
489                 curLine = curLine.trim();
490
491                 if ((curLine.length() > 0) && (!curLine.startsWith("#")) && curLine.contains("=>")) {
492                     String[] entry = curLine.split("=>");
493                     if (entry.length == 2) {
494                         results.put(entry[0].trim(), entry[1].trim());
495                     }
496                 }
497             }
498             mapReader.close();
499         } catch (Exception e) {
500             LOG.error("Caught exception reading map " + mapFilename, e);
501             return null;
502         }
503
504         return results;
505     }
506
507 }