Fixed an Issue on API Calling via MSB
[holmes/rule-management.git] / rulemgt / src / main / java / org / onap / holmes / rulemgt / RuleAllocator.java
1 /**
2  * Copyright 2017-2020 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;
18
19 import lombok.extern.slf4j.Slf4j;
20 import org.jvnet.hk2.annotations.Service;
21 import org.onap.holmes.common.api.entity.CorrelationRule;
22 import org.onap.holmes.common.exception.CorrelationException;
23 import org.onap.holmes.common.utils.DbDaoUtil;
24 import org.onap.holmes.rulemgt.bolt.enginebolt.EngineWrapper;
25 import org.onap.holmes.rulemgt.db.CorrelationRuleDao;
26 import org.onap.holmes.rulemgt.tools.EngineTools;
27 import org.onap.holmes.rulemgt.wrapper.RuleMgtWrapper;
28 import org.onap.holmes.rulemgt.wrapper.RuleQueryWrapper;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 import javax.annotation.PostConstruct;
33 import javax.inject.Inject;
34 import java.util.*;
35
36 import static java.util.concurrent.TimeUnit.SECONDS;
37
38 @Slf4j
39 @Service
40 public class RuleAllocator {
41     private static final Logger LOGGER = LoggerFactory.getLogger(RuleAllocator.class);
42
43     public final static int ENABLE = 1;
44     private RuleMgtWrapper ruleMgtWrapper;
45     private RuleQueryWrapper ruleQueryWrapper;
46     private EngineWrapper engineWrapper;
47     private EngineTools engineTools;
48     private CorrelationRuleDao correlationRuleDao;
49
50     @Inject
51     public RuleAllocator(RuleMgtWrapper ruleMgtWrapper, RuleQueryWrapper ruleQueryWrapper,
52                          EngineWrapper engineWrapper, EngineTools engineTools, DbDaoUtil daoUtil) {
53         this.ruleMgtWrapper = ruleMgtWrapper;
54         this.ruleQueryWrapper = ruleQueryWrapper;
55         this.engineWrapper = engineWrapper;
56         this.engineTools = engineTools;
57         correlationRuleDao = daoUtil.getJdbiDaoByOnDemand(CorrelationRuleDao.class);
58     }
59
60     @PostConstruct
61     private void initialize() {
62         new Timer("RuleAllocatorTimer").schedule(new TimerTask() {
63
64             public void run() {
65                 try {
66                     allocateRules();
67                 } catch (Exception e) {
68                     LOGGER.error("Failed to reallocate rules.", e);
69                 }
70             }
71
72         }, SECONDS.toMillis(10), SECONDS.toMillis(30));
73     }
74
75     public synchronized void allocateRules() throws Exception {
76         List<String> engines = engineTools.getInstanceList();
77
78         if (engines == null) {
79             return;
80         }
81
82         int numOfEngines = engines.size();
83         LOGGER.info(String.format("There are %d engine instance(s) running currently.", numOfEngines));
84
85         List<String> legacyEngineInstances = engineTools.getLegacyEngineInstances();
86         if (legacyEngineInstances == null) {
87             return;
88         }
89
90         if (legacyEngineInstances.size() < numOfEngines) {
91             //extend
92             List<CorrelationRule> rules2Allocate = calculateRule(legacyEngineInstances, numOfEngines);
93             List<CorrelationRule> rules2Delete = copyList(rules2Allocate);
94             List<String> newInstanceIds = sortOutNewEngineInstances(engines, legacyEngineInstances);
95             distributeRules(newInstanceIds, rules2Allocate);
96             cleanUpRulesFromEngines(rules2Delete, legacyEngineInstances);
97         } else {
98             //destroy
99             List<String> destroyed = getDestroyedEngines(engines, legacyEngineInstances);
100             distributeRules(getRemainingEngines(engines, destroyed), getRules(destroyed));
101         }
102     }
103
104     private List<CorrelationRule> copyList(List<CorrelationRule> rules) {
105         List<CorrelationRule> ret = new ArrayList<>(rules.size());
106         for (CorrelationRule r : rules) {
107             ret.add((CorrelationRule) r.clone());
108         }
109         return ret;
110     }
111
112     // When the engine is expanding, the rules that need to be allocated are calculated.
113     private List<CorrelationRule> calculateRule(List<String> existingEngineIps,
114                                                 int latestEngineInsNum) throws CorrelationException {
115         List<CorrelationRule> enabledRules = ruleQueryWrapper.queryRuleByEnable(ENABLE);
116         int ruleCount = 0;
117         if (enabledRules != null) {
118             ruleCount = enabledRules.size();
119         }
120         // Average number of rule that's to be allocate into each instance
121         int count = ruleCount / latestEngineInsNum;
122         // The number of remaining rules (to be allocated) after each instance has been allocated with the average number of rules.
123         int remainder = ruleCount % latestEngineInsNum;
124
125         List<CorrelationRule> ret = new ArrayList<>();
126         for (String ip : existingEngineIps) {
127             List<CorrelationRule> rules = ruleQueryWrapper.queryRuleByEngineInstance(ip);
128             List<CorrelationRule> tmp = rules.subList(count + (remainder-- / existingEngineIps.size()), rules.size());
129             ret.addAll(tmp);
130         }
131         return ret;
132     }
133
134     // Rules that need to be allocated after the engine is destroyed
135     private List<CorrelationRule> getRules(List<String> destroyIpList) throws CorrelationException {
136         List<CorrelationRule> rules = new ArrayList<>();
137         try {
138             if (destroyIpList != null) {
139                 for (String ip : destroyIpList) {
140                     rules.addAll(ruleQueryWrapper.queryRuleByEngineInstance(ip));
141                 }
142             }
143         } catch (CorrelationException e) {
144             LOGGER.error("method getRules get data from DB failed !", e);
145         }
146         return rules;
147     }
148
149     // Extended IP
150     private List<String> sortOutNewEngineInstances(List<String> newIps, List<String> oldIps) {
151         List<String> ret = new ArrayList<>();
152
153         for (String ip : newIps) {
154             if (!oldIps.contains(ip)) {
155                 ret.add(ip);
156             }
157         }
158         return ret;
159     }
160
161     // Destroyed IP
162     private List<String> getDestroyedEngines(List<String> latest, List<String> existing) {
163         List<String> ret = new ArrayList<>();
164         for (String ip : existing) {
165             if (!latest.contains(ip)) {
166                 ret.add(ip);
167             }
168         }
169         return ret;
170     }
171
172     // Residual IP after destruction
173     private List<String> getRemainingEngines(List<String> all, List<String> destroyed) {
174         List<String> ret = new ArrayList<>();
175         for (String ip : all) {
176             if (!destroyed.contains(ip)) {
177                 ret.add(ip);
178             }
179         }
180         return ret;
181     }
182
183     private void distributeRules(List<String> instanceIps, List<CorrelationRule> rules) throws CorrelationException {
184         List<String> sortedIps = sortIpByRuleNumDesc(instanceIps);
185
186         for (int i = 0, j = 0; j < rules.size(); i++, j++) {
187             int index = i % sortedIps.size();
188             String ip = sortedIps.get(index);
189             CorrelationRule rule = rules.get(j);
190             rule.setEngineInstance(ip);
191             allocateRule(rule, ip);
192         }
193     }
194
195     // Sorted by the number of rules each engine contains, in a descending order.
196     private List<String> sortIpByRuleNumDesc(List<String> ips) {
197         List<CorrelationRule> rules = null;
198         Map<String, Integer> ruleNumOfEngines = new HashMap();
199
200         try {
201             for (String ip : ips) {
202                 rules = ruleQueryWrapper.queryRuleByEngineInstance(ip);
203                 if (rules != null) {
204                     ruleNumOfEngines.put(ip, rules.size());
205                 }
206             }
207         } catch (Exception e) {
208             LOGGER.error("getEngineWithLeastRules failed !", e);
209         }
210
211         List<Map.Entry<String, Integer>> sortedEntries = new ArrayList<>(ruleNumOfEngines.entrySet());
212         Collections.sort(sortedEntries, (o1, o2) -> o2.getValue() - o1.getValue());
213
214         List<String> ret = new ArrayList<>();
215         for (Map.Entry<String, Integer> entry : sortedEntries) {
216             ret.add(entry.getKey());
217         }
218         return ret;
219     }
220
221     private void allocateRule(CorrelationRule rule, String ip) throws CorrelationException {
222         try {
223             ruleMgtWrapper.deployRule2Engine(rule, ip);
224             correlationRuleDao.updateRule(rule);
225         } catch (CorrelationException e) {
226             throw new CorrelationException(String.format("Failed to allocate rule <%s> to <%s>",
227                     rule.getName(), ip), e);
228         }
229     }
230
231     private void cleanUpRulesFromEngines(List<CorrelationRule> rules, List<String> ipList) {
232         try {
233             for (String ip : ipList) {
234                 for (CorrelationRule rule : rules) {
235                     if (ip.equals(rule.getEngineInstance())) {
236                         engineWrapper.deleteRuleFromEngine(rule.getPackageName(), ip);
237                     }
238                 }
239             }
240         } catch (CorrelationException e) {
241             LOGGER.error("When the engine is extended, deleting rule failed", e);
242         }
243     }
244 }