blob: b2a33a9796f7fbdbed9252c1cd4b00e5b2c042a3 [file] [log] [blame]
Aaron Kruglikov29327982015-10-06 17:15:16 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
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 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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
17package org.onosproject.mlb;
18
19import com.google.common.util.concurrent.ListenableScheduledFuture;
20import com.google.common.util.concurrent.ListeningScheduledExecutorService;
21import com.google.common.util.concurrent.MoreExecutors;
Madan Jampani620f70d2016-01-30 22:22:47 -080022
Aaron Kruglikov29327982015-10-06 17:15:16 -070023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Component;
25import org.apache.felix.scr.annotations.Deactivate;
26import org.apache.felix.scr.annotations.Reference;
27import org.apache.felix.scr.annotations.ReferenceCardinality;
28import org.onosproject.cluster.ClusterService;
29import org.onosproject.cluster.LeadershipEvent;
30import org.onosproject.cluster.LeadershipEventListener;
31import org.onosproject.cluster.LeadershipService;
32import org.onosproject.cluster.NodeId;
33import org.onosproject.mastership.MastershipAdminService;
34import org.onosproject.mastership.MastershipEvent;
35import org.onosproject.mastership.MastershipListener;
36import org.onosproject.mastership.MastershipService;
37import org.slf4j.Logger;
38
39import java.util.concurrent.Executors;
40import java.util.concurrent.Future;
41import java.util.concurrent.TimeUnit;
42import java.util.concurrent.atomic.AtomicBoolean;
43import java.util.concurrent.atomic.AtomicReference;
44
45import static org.slf4j.LoggerFactory.getLogger;
46
47/**
48 * An app to perform automatic load balancing in response to events. Load balancing events are triggered by any
49 * change in mastership and are limited to a frequency of one every 30 seconds, all load balancing is run on an outside
50 * thread executor that must only have one thread due to issues that can occur is multiple balancing events occur in
51 * parallel.
52 */
53@Component(immediate = true)
54public class MastershipLoadBalancer {
55
56 private final Logger log = getLogger(getClass());
57
58 private static final String REBALANCE_MASTERSHIP = "rebalance/mastership";
59
60 private NodeId localId;
61
62 private AtomicBoolean isLeader = new AtomicBoolean(false);
63
64 private AtomicReference<Future> nextTask = new AtomicReference<>();
65
66 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
67 protected MastershipService mastershipService;
68
69 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 protected MastershipAdminService mastershipAdminService;
71
72 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
73 protected LeadershipService leadershipService;
74
75 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
76 protected ClusterService clusterService;
77
78 private InnerLeadershipListener leadershipListener = new InnerLeadershipListener();
79
80 /* This listener is used to trigger balancing for any mastership event which will include switches changing state
81 between active and inactive states as well as the same variety of event occurring with ONOS nodes. Must
82 use a listenable executor to ensure events are triggered with no frequency greater than once every 30 seconds.
83 */
84 private InnerMastershipListener mastershipListener = new InnerMastershipListener();
85
86 //Ensures that all executions do not interfere with one another (single thread)
87 private ListeningScheduledExecutorService executorService = MoreExecutors.
88 listeningDecorator(Executors.newSingleThreadScheduledExecutor());
89
90 @Activate
91 public void activate() {
92 mastershipService.addListener(mastershipListener);
93 localId = clusterService.getLocalNode().id();
94 leadershipService.addListener(leadershipListener);
95 leadershipService.runForLeadership(REBALANCE_MASTERSHIP);
96 log.info("Started");
97 }
98
99 @Deactivate
100 public void deactivate() {
101 mastershipService.removeListener(mastershipListener);
102 leadershipService.withdraw(REBALANCE_MASTERSHIP);
103 leadershipService.removeListener(leadershipListener);
104 cancelBalance();
105 executorService.shutdown();
106 log.info("Stopped");
107 }
108
Madan Jampani620f70d2016-01-30 22:22:47 -0800109 private synchronized void processLeaderChange(NodeId newLeader) {
Aaron Kruglikov29327982015-10-06 17:15:16 -0700110 boolean currLeader = newLeader.equals(localId);
111 if (isLeader.getAndSet(currLeader) != currLeader) {
112 if (currLeader) {
113 scheduleBalance();
114 } else {
115 cancelBalance();
116 }
117 }
118 }
119
120 private void scheduleBalance() {
121 if (isLeader.get() && nextTask.get() == null) {
122
123 ListenableScheduledFuture task = executorService.schedule(mastershipAdminService::balanceRoles, 30,
124 TimeUnit.SECONDS);
125 task.addListener(() -> {
126 log.info("Completed balance roles");
127 nextTask.set(null);
128 }, MoreExecutors.directExecutor()
129 );
130 if (!nextTask.compareAndSet(null, task)) {
131 task.cancel(false);
132 }
133 }
134 }
135
136 private void cancelBalance() {
137 Future task = nextTask.getAndSet(null);
138 if (task != null) {
139 task.cancel(false);
140 }
141 }
142
143 private class InnerMastershipListener implements MastershipListener {
144
145 @Override
146 public void event(MastershipEvent event) {
147 //Sets flag at execution to indicate there is currently a scheduled rebalancing, reverts upon completion
148 scheduleBalance();
149 }
150 }
151
152 private class InnerLeadershipListener implements LeadershipEventListener {
153 @Override
154 public boolean isRelevant(LeadershipEvent event) {
155 return REBALANCE_MASTERSHIP.equals(event.subject().topic());
156 }
157
158 @Override
159 public void event(LeadershipEvent event) {
Madan Jampani620f70d2016-01-30 22:22:47 -0800160 processLeaderChange(event.subject().leaderNodeId());
Aaron Kruglikov29327982015-10-06 17:15:16 -0700161 }
162 }
Madan Jampani620f70d2016-01-30 22:22:47 -0800163}