Added exception protection.
[holmes/rule-management.git] / rulemgt / src / main / java / org / onap / holmes / rulemgt / dcae / ConfigFileScanningTask.java
1 /**
2  * Copyright 2021 ZTE Corporation.
3  * <p>
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  * <p>
8  * http://www.apache.org/licenses/LICENSE-2.0
9  * <p>
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.onap.holmes.rulemgt.dcae;
18
19 import com.google.gson.JsonArray;
20 import com.google.gson.JsonElement;
21 import com.google.gson.JsonObject;
22 import com.google.gson.JsonParser;
23 import org.apache.commons.lang.StringUtils;
24 import org.onap.holmes.common.ConfigFileScanner;
25 import org.onap.holmes.common.utils.FileUtils;
26 import org.onap.holmes.common.utils.JerseyClient;
27 import org.onap.holmes.rulemgt.bean.request.RuleCreateRequest;
28 import org.onap.holmes.rulemgt.bean.response.RuleQueryListResponse;
29 import org.onap.holmes.rulemgt.bean.response.RuleResult4API;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
32
33 import javax.ws.rs.client.Entity;
34 import javax.ws.rs.core.MediaType;
35 import java.io.File;
36 import java.nio.file.Paths;
37 import java.util.*;
38
39 public class ConfigFileScanningTask implements Runnable {
40     final public static long POLLING_PERIOD = 30L;
41     final private static Logger LOGGER = LoggerFactory.getLogger(ConfigFileScanningTask.class);
42     final private static long FILE_SIZE_LMT = 1024 * 1024 * 10; // 10MB
43     final private Map<String, String> configInEffect = new HashMap(); // Contents for configInEffect are <closedControlLoop>:<ruleContents> pairs.
44     private String configFile = "/opt/hrmrules/index.json";
45     private ConfigFileScanner configFileScanner;
46     private String url = "https://127.0.0.1:9101/api/holmes-rule-mgmt/v1/rule";
47
48     public ConfigFileScanningTask(ConfigFileScanner configFileScanner) {
49         this.configFileScanner = configFileScanner;
50     }
51
52     @Override
53     public void run() {
54         if (null == configFileScanner) {
55             configFileScanner = new ConfigFileScanner();
56         }
57
58         try {
59             Map<String, String> newConfig = extractConfigItems(configFileScanner.scan(configFile));
60
61             List<RuleResult4API> deployedRules = getExistingRules();
62
63             // deal with newly added rules
64             final Set<String> existingKeys = new HashSet(configInEffect.keySet());
65             final Set<String> newKeys = new HashSet(newConfig.keySet());
66             newKeys.stream()
67                     .filter(key -> !existingKeys.contains(key))
68                     .forEach(key -> {
69                         if (deployRule(key, newConfig.get(key))) {
70                             configInEffect.put(key, newConfig.get(key));
71                             LOGGER.info("Rule '{}' has been deployed.", key);
72                         }
73                     });
74
75             // deal with removed rules
76             existingKeys.stream().filter(key -> !newKeys.contains(key)).forEach(key -> {
77                 if (deleteRule(find(deployedRules, key))) {
78                     configInEffect.remove(key);
79                     LOGGER.info("Rule '{}' has been removed.", key);
80                 }
81             });
82
83             // deal with changed rules
84             existingKeys.stream().filter(key -> newKeys.contains(key)).forEach(key -> {
85                 if (changed(configInEffect.get(key), newConfig.get(key))) {
86                     if (deleteRule(find(deployedRules, key))) {
87                         configInEffect.remove(key);
88                         deployRule(key, newConfig.get(key));
89                         configInEffect.put(key, newConfig.get(key));
90                         LOGGER.info("Rule '{}' has been updated.", key);
91                     }
92                 }
93             });
94         } catch (Exception e) {
95             LOGGER.warn("Unhandled error: \n" + e.getMessage(), e);
96         }
97     }
98
99     private Map<String, String> extractConfigItems(Map<String, String> configFiles) {
100         Map<String, String> ret = new HashMap();
101         for (Map.Entry entry : configFiles.entrySet()) {
102             JsonArray ja = JsonParser.parseString(entry.getValue().toString()).getAsJsonArray();
103             Iterator<JsonElement> iterator = ja.iterator();
104             while (iterator.hasNext()) {
105                 JsonObject jo = iterator.next().getAsJsonObject();
106                 String contents = readFile(jo.get("file").getAsString());
107                 if (StringUtils.isNotBlank(contents)) {
108                     ret.put(jo.get("closedControlLoopName").getAsString(), contents);
109                 }
110             }
111         }
112         return ret;
113     }
114
115     private String normalizePath(String path) {
116         if (!path.startsWith("/")) {
117             return Paths.get(new File(configFile).getParent(), path).toString();
118         }
119         return path;
120     }
121
122     private String readFile(String path) {
123         String finalPath = normalizePath(path);
124         File file = new File(finalPath);
125         if (file.exists() && !file.isDirectory() && file.length() <= FILE_SIZE_LMT) {
126             return FileUtils.readTextFile(finalPath);
127         } else {
128             LOGGER.warn("The file {} does not exist or it is a directory or it is too large to load.", finalPath);
129         }
130         return null;
131     }
132
133     private RuleResult4API find(final List<RuleResult4API> rules, String clName) {
134         for (RuleResult4API rule : rules) {
135             if (rule.getLoopControlName().equals(clName)) {
136                 return rule;
137             }
138         }
139         return null;
140     }
141
142     private boolean changed(String con1, String con2) {
143         // if either of the arguments is null, consider it as invalid and unchanged
144         if (con1 == null || con2 == null) {
145             return false;
146         }
147
148         if (!con1.replaceAll("\\s", StringUtils.EMPTY)
149                 .equals(con2.replaceAll("\\s", StringUtils.EMPTY))) {
150             return true;
151         }
152
153         return false;
154     }
155
156     private List<RuleResult4API> getExistingRules() {
157         RuleQueryListResponse ruleQueryListResponse = JerseyClient.newInstance().get(url, RuleQueryListResponse.class);
158         List<RuleResult4API> deployedRules = Collections.EMPTY_LIST;
159         if (null != ruleQueryListResponse) {
160             deployedRules = ruleQueryListResponse.getCorrelationRules();
161         }
162         return deployedRules;
163     }
164
165     private boolean deployRule(String clName, String contents) {
166         RuleCreateRequest ruleCreateRequest = getRuleCreateRequest(clName, contents);
167         if (JerseyClient.newInstance().header("Accept", MediaType.APPLICATION_JSON)
168                 .put(url, Entity.json(ruleCreateRequest)) == null) {
169             LOGGER.error("Failed to deploy rule: {}.", clName);
170             return false;
171         }
172         return true;
173     }
174
175     private RuleCreateRequest getRuleCreateRequest(String clName, String contents) {
176         RuleCreateRequest ruleCreateRequest = new RuleCreateRequest();
177         ruleCreateRequest.setLoopControlName(clName);
178         ruleCreateRequest.setRuleName(clName);
179         ruleCreateRequest.setContent(contents);
180         ruleCreateRequest.setDescription("");
181         ruleCreateRequest.setEnabled(1);
182         return ruleCreateRequest;
183     }
184
185     private boolean deleteRule(RuleResult4API rule) {
186         if (rule == null) {
187             LOGGER.info("No rule found, nothing to delete.");
188             return false;
189         }
190         if (null == JerseyClient.newInstance().delete(url + "/" + rule.getRuleId())) {
191             LOGGER.warn("Failed to delete rule, the rule id is: {}", rule.getRuleId());
192             return false;
193         }
194         return true;
195     }
196 }