2 * Copyright 2017-2021 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;
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;
32 import javax.annotation.PostConstruct;
33 import javax.inject.Inject;
35 import java.util.concurrent.TimeUnit;
37 import static java.util.concurrent.TimeUnit.SECONDS;
41 public class RuleAllocator {
42 private static final Logger LOGGER = LoggerFactory.getLogger(RuleAllocator.class);
44 public final static int ENABLE = 1;
45 public final static int RETRY_TIMES = 5;
46 public final static long RETRY_INTERVAL_SEC = 15;
47 private RuleMgtWrapper ruleMgtWrapper;
48 private RuleQueryWrapper ruleQueryWrapper;
49 private EngineWrapper engineWrapper;
50 private EngineTools engineTools;
51 private CorrelationRuleDao correlationRuleDao;
54 public RuleAllocator(RuleMgtWrapper ruleMgtWrapper, RuleQueryWrapper ruleQueryWrapper,
55 EngineWrapper engineWrapper, EngineTools engineTools, DbDaoUtil daoUtil) {
56 this.ruleMgtWrapper = ruleMgtWrapper;
57 this.ruleQueryWrapper = ruleQueryWrapper;
58 this.engineWrapper = engineWrapper;
59 this.engineTools = engineTools;
60 correlationRuleDao = daoUtil.getJdbiDaoByOnDemand(CorrelationRuleDao.class);
64 private void initialize() {
65 new Timer("RuleAllocatorTimer").schedule(new TimerTask() {
70 } catch (Exception e) {
71 LOGGER.error("Failed to reallocate rules.", e);
75 }, SECONDS.toMillis(10), SECONDS.toMillis(30));
78 public synchronized void allocateRules() throws Exception {
79 List<String> engines = engineTools.getInstanceList();
81 if (engines == null) {
85 int numOfEngines = engines.size();
86 LOGGER.info(String.format("There are %d engine instance(s) running currently.", numOfEngines));
88 List<String> legacyEngineInstances = engineTools.getLegacyEngineInstances();
89 if (legacyEngineInstances == null) {
93 if (legacyEngineInstances.size() < numOfEngines) { // extend
94 List<CorrelationRule> rules2Allocate = calculateRule(legacyEngineInstances, numOfEngines);
95 List<CorrelationRule> rules2Delete = copyList(rules2Allocate);
96 List<String> newInstanceIds = sortOutNewEngineInstances(engines, legacyEngineInstances);
97 distributeRules(newInstanceIds, rules2Allocate);
98 cleanUpRulesFromEngines(rules2Delete, legacyEngineInstances);
100 // If new engine instances share the same IP addresses with the old ones, the
101 // rule management module will simply leave the them to cope with the legacy rules.
102 // Here, it only takes care of the rules that need to be moved from one IP address to another.
103 List<String> destroyed = getDestroyedEngines(engines, legacyEngineInstances);
104 distributeRules(getRemainingEngines(engines, destroyed), getRules(destroyed));
108 private List<CorrelationRule> copyList(List<CorrelationRule> rules) {
109 List<CorrelationRule> ret = new ArrayList<>(rules.size());
110 for (CorrelationRule r : rules) {
111 ret.add((CorrelationRule) r.clone());
116 // When the engine is expanding, the rules that need to be allocated are calculated.
117 private List<CorrelationRule> calculateRule(List<String> existingEngineIps,
118 int latestEngineInsNum) throws CorrelationException {
119 List<CorrelationRule> enabledRules = ruleQueryWrapper.queryRuleByEnable(ENABLE);
121 if (enabledRules != null) {
122 ruleCount = enabledRules.size();
124 // Average number of rule that's to be allocate into each instance
125 int count = ruleCount / latestEngineInsNum;
126 // The number of remaining rules (to be allocated) after each instance has been allocated with the average number of rules.
127 int remainder = ruleCount % latestEngineInsNum;
129 List<CorrelationRule> ret = new ArrayList<>();
130 for (String ip : existingEngineIps) {
131 List<CorrelationRule> rules = ruleQueryWrapper.queryRuleByEngineInstance(ip);
132 List<CorrelationRule> tmp = rules.subList(count + (remainder-- / existingEngineIps.size()), rules.size());
138 // Rules that need to be allocated after the engine is destroyed
139 private List<CorrelationRule> getRules(List<String> destroyIpList) throws CorrelationException {
140 List<CorrelationRule> rules = new ArrayList<>();
142 if (destroyIpList != null) {
143 for (String ip : destroyIpList) {
144 rules.addAll(ruleQueryWrapper.queryRuleByEngineInstance(ip));
147 } catch (CorrelationException e) {
148 LOGGER.error("method getRules get data from DB failed !", e);
154 private List<String> sortOutNewEngineInstances(List<String> newIps, List<String> oldIps) {
155 List<String> ret = new ArrayList<>();
157 for (String ip : newIps) {
158 if (!oldIps.contains(ip)) {
166 private List<String> getDestroyedEngines(List<String> latest, List<String> existing) {
167 List<String> ret = new ArrayList<>();
168 for (String ip : existing) {
169 if (!latest.contains(ip)) {
176 // Residual IP after destruction
177 private List<String> getRemainingEngines(List<String> all, List<String> destroyed) {
178 List<String> ret = new ArrayList<>();
179 for (String ip : all) {
180 if (!destroyed.contains(ip)) {
187 private void distributeRules(List<String> instanceIps, List<CorrelationRule> rules) throws CorrelationException {
188 List<String> sortedIps = sortIpByRuleNumDesc(instanceIps);
190 for (int i = 0, j = 0; j < rules.size(); i++, j++) {
191 int index = i % sortedIps.size();
192 String ip = sortedIps.get(index);
193 CorrelationRule rule = rules.get(j);
194 rule.setEngineInstance(ip);
195 allocateRule(rule, ip);
199 // Sorted by the number of rules each engine contains, in a descending order.
200 private List<String> sortIpByRuleNumDesc(List<String> ips) {
201 List<CorrelationRule> rules;
202 Map<String, Integer> ruleNumOfEngines = new HashMap();
205 for (String ip : ips) {
206 rules = ruleQueryWrapper.queryRuleByEngineInstance(ip);
208 ruleNumOfEngines.put(ip, rules.size());
211 } catch (Exception e) {
212 LOGGER.error("getEngineWithLeastRules failed !", e);
215 List<Map.Entry<String, Integer>> sortedEntries = new ArrayList<>(ruleNumOfEngines.entrySet());
216 Collections.sort(sortedEntries, (o1, o2) -> o2.getValue() - o1.getValue());
218 List<String> ret = new ArrayList<>();
219 for (Map.Entry<String, Integer> entry : sortedEntries) {
220 ret.add(entry.getKey());
225 private void allocateRule(CorrelationRule rule, String ip) throws CorrelationException {
226 // Retry for a couple of times in case of deployment failure
227 // due to unfinished initialization procedures of engine instances.
228 for (int i = 0; i <= RETRY_TIMES; ++i) {
230 ruleMgtWrapper.deployRule2Engine(rule, ip);
231 correlationRuleDao.updateRule(rule);
232 // If the codes reach here, it means everything's okay. There's no need to run the loop more.
234 } catch (CorrelationException e) {
235 LOGGER.warn(String.format("Failed to allocate rule <%s> to <%s>. Retry: %d.",
236 rule.getName(), ip, i), e);
237 if (i == RETRY_TIMES) {
238 throw new CorrelationException(String.format("Failed to allocate rule <%s> to <%s>",
239 rule.getName(), ip), e);
242 SECONDS.sleep(RETRY_INTERVAL_SEC * (i + 1));
243 } catch (InterruptedException interruptedException) {
244 LOGGER.info(interruptedException.getMessage(), interruptedException);
250 private void cleanUpRulesFromEngines(List<CorrelationRule> rules, List<String> ipList) {
252 for (String ip : ipList) {
253 for (CorrelationRule rule : rules) {
254 if (ip.equals(rule.getEngineInstance())) {
255 engineWrapper.deleteRuleFromEngine(rule.getPackageName(), ip);
259 } catch (CorrelationException e) {
260 LOGGER.error("When the engine is extended, deleting rule failed", e);