2 * Copyright 2021-2022 ZTE Corporation.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package org.onap.holmes.rulemgt.dcae;
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.lang3.StringUtils;
24 import org.onap.holmes.common.ConfigFileScanner;
25 import org.onap.holmes.common.utils.CommonUtils;
26 import org.onap.holmes.common.utils.FileUtils;
27 import org.onap.holmes.common.utils.JerseyClient;
28 import org.onap.holmes.rulemgt.bean.request.RuleCreateRequest;
29 import org.onap.holmes.rulemgt.bean.response.RuleQueryListResponse;
30 import org.onap.holmes.rulemgt.bean.response.RuleResult4API;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
34 import jakarta.ws.rs.client.Entity;
35 import jakarta.ws.rs.core.MediaType;
37 import java.nio.file.Paths;
40 public class ConfigFileScanningTask implements Runnable {
41 final public static long POLLING_PERIOD = 30L;
42 final private static Logger LOGGER = LoggerFactory.getLogger(ConfigFileScanningTask.class);
43 final private static long FILE_SIZE_LMT = 1024 * 1024 * 10; // 10MB
44 private String configFile = "/opt/hrmrules/index.json";
45 private ConfigFileScanner configFileScanner;
48 public ConfigFileScanningTask(ConfigFileScanner configFileScanner) {
49 this.configFileScanner = configFileScanner;
50 this.url = getRequestPref() + "://127.0.0.1:9101/api/holmes-rule-mgmt/v1/rule";
55 List<RuleResult4API> deployedRules = null;
56 boolean isRuleQueryAvailable = true;
59 deployedRules = getExistingRules();
60 } catch (Exception e) {
61 LOGGER.warn("Failed to get existing rules for comparison.", e);
62 isRuleQueryAvailable = false;
65 // If it fails to load rule through API, it means that something must be wrong with the
66 // holmes-rule-mgmt service. Hence, there's no need to go on with remaining steps.
67 if (!isRuleQueryAvailable) {
71 // Contents for configInEffect are <closedControlLoop>:<ruleContents> pairs.
72 Map<String, String> configInEffect = new HashMap();
73 for (RuleResult4API ruleResult4API : deployedRules) {
74 configInEffect.put(ruleResult4API.getLoopControlName(), ruleResult4API.getContent());
77 if (null == configFileScanner) {
78 configFileScanner = new ConfigFileScanner();
82 Map<String, String> newConfig = extractConfigItems(configFileScanner.scan(configFile));
84 // deal with newly added rules
85 final Set<String> existingKeys = new HashSet(configInEffect.keySet());
86 final Set<String> newKeys = new HashSet(newConfig.keySet());
88 .filter(key -> !existingKeys.contains(key))
90 if (deployRule(key, newConfig.get(key))) {
91 LOGGER.info("Rule '{}' has been deployed.", key);
95 // deal with removed rules
96 final List<RuleResult4API> existingRules = deployedRules;
97 existingKeys.stream().filter(key -> !newKeys.contains(key)).forEach(key -> {
98 if (deleteRule(find(existingRules, key))) {
99 LOGGER.info("Rule '{}' has been removed.", key);
103 // deal with changed rules
104 existingKeys.stream().filter(key -> newKeys.contains(key)).forEach(key -> {
105 if (changed(configInEffect.get(key), newConfig.get(key))) {
106 if (deleteRule(find(existingRules, key))) {
107 deployRule(key, newConfig.get(key));
108 LOGGER.info("Rule '{}' has been updated.", key);
112 } catch (Exception e) {
113 LOGGER.warn("Unhandled error: \n" + e.getMessage(), e);
117 private Map<String, String> extractConfigItems(Map<String, String> configFiles) {
118 Map<String, String> ret = new HashMap();
119 for (Map.Entry entry : configFiles.entrySet()) {
120 JsonArray ja = JsonParser.parseString(entry.getValue().toString()).getAsJsonArray();
121 Iterator<JsonElement> iterator = ja.iterator();
122 while (iterator.hasNext()) {
123 JsonObject jo = iterator.next().getAsJsonObject();
124 String contents = readFile(jo.get("file").getAsString());
125 if (StringUtils.isNotBlank(contents)) {
126 ret.put(jo.get("closedControlLoopName").getAsString(), contents);
133 private String normalizePath(String path) {
134 if (!path.startsWith("/")) {
135 return Paths.get(new File(configFile).getParent(), path).toString();
140 private String readFile(String path) {
141 String finalPath = normalizePath(path);
142 File file = new File(finalPath);
143 if (file.exists() && !file.isDirectory() && file.length() <= FILE_SIZE_LMT) {
144 return FileUtils.readTextFile(finalPath);
146 LOGGER.warn("The file {} does not exist or it is a directory or it is too large to load.", finalPath);
151 private RuleResult4API find(final List<RuleResult4API> rules, String clName) {
152 for (RuleResult4API rule : rules) {
153 if (rule.getLoopControlName().equals(clName)) {
160 private boolean changed(String con1, String con2) {
161 // if either of the arguments is null, consider it as invalid and unchanged
162 if (con1 == null || con2 == null) {
166 if (!con1.replaceAll("\\s", StringUtils.EMPTY)
167 .equals(con2.replaceAll("\\s", StringUtils.EMPTY))) {
174 private List<RuleResult4API> getExistingRules() {
175 RuleQueryListResponse ruleQueryListResponse = JerseyClient.newInstance().get(url, RuleQueryListResponse.class);
176 List<RuleResult4API> deployedRules = Collections.EMPTY_LIST;
177 if (null != ruleQueryListResponse) {
178 deployedRules = ruleQueryListResponse.getCorrelationRules();
180 return deployedRules;
183 private boolean deployRule(String clName, String contents) {
184 RuleCreateRequest ruleCreateRequest = getRuleCreateRequest(clName, contents);
185 if (JerseyClient.newInstance().header("Accept", MediaType.APPLICATION_JSON)
186 .put(url, Entity.json(ruleCreateRequest)) == null) {
187 LOGGER.error("Failed to deploy rule: {}.", clName);
193 private RuleCreateRequest getRuleCreateRequest(String clName, String contents) {
194 RuleCreateRequest ruleCreateRequest = new RuleCreateRequest();
195 ruleCreateRequest.setLoopControlName(clName);
196 ruleCreateRequest.setRuleName(clName);
197 ruleCreateRequest.setContent(contents);
198 ruleCreateRequest.setDescription("");
199 ruleCreateRequest.setEnabled(1);
200 return ruleCreateRequest;
203 private boolean deleteRule(RuleResult4API rule) {
205 LOGGER.info("No rule found, nothing to delete.");
208 if (null == JerseyClient.newInstance().delete(url + "/" + rule.getRuleId())) {
209 LOGGER.warn("Failed to delete rule, the rule id is: {}", rule.getRuleId());
215 private String getRequestPref() {
216 return CommonUtils.isHttpsEnabled() ? JerseyClient.PROTOCOL_HTTPS : JerseyClient.PROTOCOL_HTTP;