Aaron Kruglikov
Committed by Gerrit Code Review

Added automatic mastership load balancing. Added comments in patch set.

Change-Id: Ia29dfbdd9d2afcc79b1f25c7690d073e6ad94f61
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2015 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +<project xmlns="http://maven.apache.org/POM/4.0.0"
18 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
19 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
20 + <modelVersion>4.0.0</modelVersion>
21 +
22 + <parent>
23 + <groupId>org.onosproject</groupId>
24 + <artifactId>onos-apps</artifactId>
25 + <version>1.4.0-SNAPSHOT</version>
26 + <relativePath>../pom.xml</relativePath>
27 + </parent>
28 +
29 + <artifactId>onos-app-mlb</artifactId>
30 + <packaging>bundle</packaging>
31 +
32 + <description>Balances mastership among nodes</description>
33 +
34 + <properties>
35 + <onos.app.name>org.onosproject.mlb</onos.app.name>
36 + </properties>
37 +
38 + <dependencies>
39 + <dependency>
40 + <groupId>org.osgi</groupId>
41 + <artifactId>org.osgi.compendium</artifactId>
42 + </dependency>
43 + </dependencies>
44 +
45 +</project>
1 +/*
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 +
17 +package org.onosproject.mlb;
18 +
19 +import com.google.common.util.concurrent.ListenableScheduledFuture;
20 +import com.google.common.util.concurrent.ListeningScheduledExecutorService;
21 +import com.google.common.util.concurrent.MoreExecutors;
22 +import org.apache.felix.scr.annotations.Activate;
23 +import org.apache.felix.scr.annotations.Component;
24 +import org.apache.felix.scr.annotations.Deactivate;
25 +import org.apache.felix.scr.annotations.Reference;
26 +import org.apache.felix.scr.annotations.ReferenceCardinality;
27 +import org.onosproject.cluster.ClusterService;
28 +import org.onosproject.cluster.LeadershipEvent;
29 +import org.onosproject.cluster.LeadershipEventListener;
30 +import org.onosproject.cluster.LeadershipService;
31 +import org.onosproject.cluster.NodeId;
32 +import org.onosproject.mastership.MastershipAdminService;
33 +import org.onosproject.mastership.MastershipEvent;
34 +import org.onosproject.mastership.MastershipListener;
35 +import org.onosproject.mastership.MastershipService;
36 +import org.slf4j.Logger;
37 +
38 +import java.util.concurrent.Executors;
39 +import java.util.concurrent.Future;
40 +import java.util.concurrent.TimeUnit;
41 +import java.util.concurrent.atomic.AtomicBoolean;
42 +import java.util.concurrent.atomic.AtomicReference;
43 +
44 +import static org.slf4j.LoggerFactory.getLogger;
45 +
46 +/**
47 + * An app to perform automatic load balancing in response to events. Load balancing events are triggered by any
48 + * change in mastership and are limited to a frequency of one every 30 seconds, all load balancing is run on an outside
49 + * thread executor that must only have one thread due to issues that can occur is multiple balancing events occur in
50 + * parallel.
51 + */
52 +@Component(immediate = true)
53 +public class MastershipLoadBalancer {
54 +
55 + private final Logger log = getLogger(getClass());
56 +
57 + private static final String REBALANCE_MASTERSHIP = "rebalance/mastership";
58 +
59 + private NodeId localId;
60 +
61 + private AtomicBoolean isLeader = new AtomicBoolean(false);
62 +
63 + private AtomicReference<Future> nextTask = new AtomicReference<>();
64 +
65 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 + protected MastershipService mastershipService;
67 +
68 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
69 + protected MastershipAdminService mastershipAdminService;
70 +
71 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 + protected LeadershipService leadershipService;
73 +
74 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 + protected ClusterService clusterService;
76 +
77 + private InnerLeadershipListener leadershipListener = new InnerLeadershipListener();
78 +
79 + /* This listener is used to trigger balancing for any mastership event which will include switches changing state
80 + between active and inactive states as well as the same variety of event occurring with ONOS nodes. Must
81 + use a listenable executor to ensure events are triggered with no frequency greater than once every 30 seconds.
82 + */
83 + private InnerMastershipListener mastershipListener = new InnerMastershipListener();
84 +
85 + //Ensures that all executions do not interfere with one another (single thread)
86 + private ListeningScheduledExecutorService executorService = MoreExecutors.
87 + listeningDecorator(Executors.newSingleThreadScheduledExecutor());
88 +
89 + @Activate
90 + public void activate() {
91 + mastershipService.addListener(mastershipListener);
92 + localId = clusterService.getLocalNode().id();
93 + leadershipService.addListener(leadershipListener);
94 + leadershipService.runForLeadership(REBALANCE_MASTERSHIP);
95 + log.info("Started");
96 + }
97 +
98 + @Deactivate
99 + public void deactivate() {
100 + mastershipService.removeListener(mastershipListener);
101 + leadershipService.withdraw(REBALANCE_MASTERSHIP);
102 + leadershipService.removeListener(leadershipListener);
103 + cancelBalance();
104 + executorService.shutdown();
105 + log.info("Stopped");
106 + }
107 +
108 + private synchronized void processLeadershipChange(NodeId newLeader) {
109 + if (newLeader == null) {
110 + return;
111 + }
112 + boolean currLeader = newLeader.equals(localId);
113 + if (isLeader.getAndSet(currLeader) != currLeader) {
114 + if (currLeader) {
115 + scheduleBalance();
116 + } else {
117 + cancelBalance();
118 + }
119 + }
120 + }
121 +
122 + private void scheduleBalance() {
123 + if (isLeader.get() && nextTask.get() == null) {
124 +
125 + ListenableScheduledFuture task = executorService.schedule(mastershipAdminService::balanceRoles, 30,
126 + TimeUnit.SECONDS);
127 + task.addListener(() -> {
128 + log.info("Completed balance roles");
129 + nextTask.set(null);
130 + }, MoreExecutors.directExecutor()
131 + );
132 + if (!nextTask.compareAndSet(null, task)) {
133 + task.cancel(false);
134 + }
135 + }
136 + }
137 +
138 + private void cancelBalance() {
139 + Future task = nextTask.getAndSet(null);
140 + if (task != null) {
141 + task.cancel(false);
142 + }
143 + }
144 +
145 + private class InnerMastershipListener implements MastershipListener {
146 +
147 + @Override
148 + public void event(MastershipEvent event) {
149 + //Sets flag at execution to indicate there is currently a scheduled rebalancing, reverts upon completion
150 + scheduleBalance();
151 + }
152 + }
153 +
154 + private class InnerLeadershipListener implements LeadershipEventListener {
155 + @Override
156 + public boolean isRelevant(LeadershipEvent event) {
157 + return REBALANCE_MASTERSHIP.equals(event.subject().topic());
158 + }
159 +
160 + @Override
161 + public void event(LeadershipEvent event) {
162 + processLeadershipChange(event.subject().leader());
163 + }
164 + }
165 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -59,6 +59,7 @@ ...@@ -59,6 +59,7 @@
59 <module>mfwd</module> 59 <module>mfwd</module>
60 <module>igmp</module> 60 <module>igmp</module>
61 <module>pim</module> 61 <module>pim</module>
62 + <module>mlb</module>
62 </modules> 63 </modules>
63 64
64 <properties> 65 <properties>
......