blob: b55bdf9f2ccb33cc202dd10f4520b8d99afd58f6 [file] [log] [blame]
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -08003 *
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 */
Jonathan Hart6af92be2016-01-05 20:52:25 -080016
17package org.onosproject.routing.impl;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080018
Jonathan Hart365335e2015-12-10 11:09:53 -080019import org.apache.felix.scr.annotations.Activate;
20import org.apache.felix.scr.annotations.Component;
21import org.apache.felix.scr.annotations.Deactivate;
22import org.apache.felix.scr.annotations.Reference;
23import org.apache.felix.scr.annotations.ReferenceCardinality;
24import org.apache.felix.scr.annotations.Service;
25import org.onosproject.cluster.ClusterService;
26import org.onosproject.cluster.LeadershipEvent;
27import org.onosproject.cluster.LeadershipEventListener;
28import org.onosproject.cluster.LeadershipService;
Madan Jampani620f70d2016-01-30 22:22:47 -080029import org.onosproject.cluster.NodeId;
Jonathan Hart365335e2015-12-10 11:09:53 -080030import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
32import org.onosproject.net.intent.Intent;
33import org.onosproject.net.intent.IntentService;
34import org.onosproject.net.intent.IntentState;
Jonathan Hart6af92be2016-01-05 20:52:25 -080035import org.onosproject.net.intent.IntentUtils;
Jonathan Hart365335e2015-12-10 11:09:53 -080036import org.onosproject.net.intent.Key;
37import org.onosproject.routing.IntentSynchronizationAdminService;
38import org.onosproject.routing.IntentSynchronizationService;
39import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
Pingping Linc07781f2015-10-30 00:44:41 -070041
42import java.util.HashMap;
43import java.util.LinkedList;
44import java.util.List;
45import java.util.Map;
46import java.util.Map.Entry;
47import java.util.concurrent.ConcurrentHashMap;
48import java.util.concurrent.ExecutorService;
49
Jonathan Hart365335e2015-12-10 11:09:53 -080050import static java.util.concurrent.Executors.newSingleThreadExecutor;
51import static org.onlab.util.Tools.groupedThreads;
Jonathan Hart96c5a4a2015-07-31 14:23:33 -070052
Jonathan Hart51372182014-12-03 21:32:34 -080053/**
Jonathan Hart365335e2015-12-10 11:09:53 -080054 * Synchronizes intents between an in-memory intent store and the IntentService.
Jonathan Hart51372182014-12-03 21:32:34 -080055 */
Jonathan Hart365335e2015-12-10 11:09:53 -080056@Service
Jonathan Hart6af92be2016-01-05 20:52:25 -080057@Component(immediate = false)
Jonathan Hart365335e2015-12-10 11:09:53 -080058public class IntentSynchronizer implements IntentSynchronizationService,
59 IntentSynchronizationAdminService {
Pavlin Radoslavov2aa1f322015-03-11 17:59:44 -070060
Jonathan Hart365335e2015-12-10 11:09:53 -080061 private static final Logger log = LoggerFactory.getLogger(IntentSynchronizer.class);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080062
Jonathan Hart365335e2015-12-10 11:09:53 -080063 private static final String APP_NAME = "org.onosproject.intentsynchronizer";
Jonathan Hart9a426f82015-09-03 15:43:13 +020064
Jonathan Hart365335e2015-12-10 11:09:53 -080065 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
66 protected CoreService coreService;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080067
Jonathan Hart365335e2015-12-10 11:09:53 -080068 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
69 protected LeadershipService leadershipService;
70
71 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
72 protected ClusterService clusterService;
73
74 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
75 protected IntentService intentService;
76
Madan Jampani620f70d2016-01-30 22:22:47 -080077 private NodeId localNodeId;
Jonathan Hart365335e2015-12-10 11:09:53 -080078 private ApplicationId appId;
79
80 private final InternalLeadershipListener leadershipEventListener =
81 new InternalLeadershipListener();
82
83 private final Map<Key, Intent> intents = new ConcurrentHashMap<>();
84
85 private ExecutorService intentsSynchronizerExecutor;
86
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080087 private volatile boolean isElectedLeader = false;
88 private volatile boolean isActivatedLeader = false;
89
Jonathan Hart365335e2015-12-10 11:09:53 -080090 @Activate
91 public void activate() {
92 intentsSynchronizerExecutor = createExecutor();
Madan Jampani620f70d2016-01-30 22:22:47 -080093 this.localNodeId = clusterService.getLocalNode().id();
Jonathan Hart365335e2015-12-10 11:09:53 -080094 this.appId = coreService.registerApplication(APP_NAME);
95
96 leadershipService.addListener(leadershipEventListener);
97 leadershipService.runForLeadership(appId.name());
98
99 log.info("Started");
Jonathan Hart9a426f82015-09-03 15:43:13 +0200100 }
101
Jonathan Hart365335e2015-12-10 11:09:53 -0800102 @Deactivate
103 public void deactivate() {
104 leadershipService.withdraw(appId.name());
105 leadershipService.removeListener(leadershipEventListener);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800106
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800107 synchronized (this) {
Luca Prete00043db2015-11-03 15:40:40 -0800108 intentsSynchronizerExecutor.shutdownNow();
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800109 }
Jonathan Hart365335e2015-12-10 11:09:53 -0800110
111 log.info("Stopped");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800112 }
113
Pingping Linc07781f2015-10-30 00:44:41 -0700114 /**
Jonathan Hart365335e2015-12-10 11:09:53 -0800115 * Creates an executor that will be used for synchronization tasks.
116 * <p>
117 * Can be overridden to change the type of executor used.
118 * </p>
119 *
120 * @return executor service
Pingping Linc07781f2015-10-30 00:44:41 -0700121 */
Jonathan Hart365335e2015-12-10 11:09:53 -0800122 protected ExecutorService createExecutor() {
123 return newSingleThreadExecutor(groupedThreads("onos/" + appId, "sync"));
124 }
125
126 @Override
Pingping Linc07781f2015-10-30 00:44:41 -0700127 public void removeIntents() {
128 if (!isElectedLeader) {
Jonathan Hart365335e2015-12-10 11:09:53 -0800129 // Only leader will withdraw intents
Pingping Linc07781f2015-10-30 00:44:41 -0700130 return;
131 }
132
133 log.debug("Intent Synchronizer shutdown: withdrawing all intents...");
134
135 for (Entry<Key, Intent> entry : intents.entrySet()) {
136 intentService.withdraw(entry.getValue());
137 log.debug("Intent Synchronizer withdrawing intent: {}",
138 entry.getValue());
139 }
140
141 intents.clear();
142 log.info("Tried to clean all intents");
143 }
144
Jonathan Hart9a426f82015-09-03 15:43:13 +0200145 @Override
146 public void submit(Intent intent) {
147 synchronized (this) {
148 intents.put(intent.key(), intent);
149 if (isElectedLeader && isActivatedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800150 log.trace("Submitting intent: {}", intent);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200151 intentService.submit(intent);
152 }
153 }
154 }
155
156 @Override
157 public void withdraw(Intent intent) {
158 synchronized (this) {
159 intents.remove(intent.key(), intent);
160 if (isElectedLeader && isActivatedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800161 log.trace("Withdrawing intent: {}", intent);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200162 intentService.withdraw(intent);
163 }
164 }
165 }
166
Jonathan Hart51372182014-12-03 21:32:34 -0800167 /**
Luca Prete00043db2015-11-03 15:40:40 -0800168 * Signals the synchronizer that the leadership has changed.
Jonathan Hart51372182014-12-03 21:32:34 -0800169 *
170 * @param isLeader true if this instance is now the leader, otherwise false
171 */
Jonathan Hart365335e2015-12-10 11:09:53 -0800172 private void leaderChanged(boolean isLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800173 log.debug("Leader changed: {}", isLeader);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800174
175 if (!isLeader) {
176 this.isElectedLeader = false;
177 this.isActivatedLeader = false;
Jonathan Hart365335e2015-12-10 11:09:53 -0800178 // Nothing to do
179 return;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800180 }
181 this.isActivatedLeader = false;
182 this.isElectedLeader = true;
183
Jonathan Hart365335e2015-12-10 11:09:53 -0800184 // Run the synchronization task
Luca Prete00043db2015-11-03 15:40:40 -0800185 intentsSynchronizerExecutor.execute(this::synchronizeIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800186 }
187
Jonathan Hart9a426f82015-09-03 15:43:13 +0200188 private void synchronizeIntents() {
189 Map<Key, Intent> serviceIntents = new HashMap<>();
190 intentService.getIntents().forEach(i -> {
191 if (i.appId().equals(appId)) {
192 serviceIntents.put(i.key(), i);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800193 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200194 });
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800195
Jonathan Hart9a426f82015-09-03 15:43:13 +0200196 List<Intent> intentsToAdd = new LinkedList<>();
197 List<Intent> intentsToRemove = new LinkedList<>();
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800198
Jonathan Hart9a426f82015-09-03 15:43:13 +0200199 for (Intent localIntent : intents.values()) {
200 Intent serviceIntent = serviceIntents.remove(localIntent.key());
201 if (serviceIntent == null) {
202 intentsToAdd.add(localIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800203 } else {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200204 IntentState state = intentService.getIntentState(serviceIntent.key());
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800205 if (!IntentUtils.intentsAreEqual(serviceIntent, localIntent) || state == null ||
Jonathan Hart9a426f82015-09-03 15:43:13 +0200206 state == IntentState.WITHDRAW_REQ ||
207 state == IntentState.WITHDRAWING ||
208 state == IntentState.WITHDRAWN) {
209 intentsToAdd.add(localIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800210 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700211 }
212 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200213
214 for (Intent serviceIntent : serviceIntents.values()) {
215 IntentState state = intentService.getIntentState(serviceIntent.key());
216 if (state != null && state != IntentState.WITHDRAW_REQ
217 && state != IntentState.WITHDRAWING
218 && state != IntentState.WITHDRAWN) {
219 intentsToRemove.add(serviceIntent);
220 }
221 }
222
Luca Prete00043db2015-11-03 15:40:40 -0800223 log.debug("Intent Synchronizer: submitting {}, withdrawing {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200224 intentsToAdd.size(), intentsToRemove.size());
225
226 // Withdraw Intents
227 for (Intent intent : intentsToRemove) {
228 intentService.withdraw(intent);
Luca Prete00043db2015-11-03 15:40:40 -0800229 log.trace("Intent Synchronizer: withdrawing intent: {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200230 intent);
231 }
232 if (!isElectedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800233 log.debug("Intent Synchronizer: cannot withdraw intents: " +
Jonathan Hart9a426f82015-09-03 15:43:13 +0200234 "not elected leader anymore");
235 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700236 return;
237 }
238
Jonathan Hart9a426f82015-09-03 15:43:13 +0200239 // Add Intents
240 for (Intent intent : intentsToAdd) {
241 intentService.submit(intent);
Luca Prete00043db2015-11-03 15:40:40 -0800242 log.trace("Intent Synchronizer: submitting intent: {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200243 intent);
244 }
245 if (!isElectedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800246 log.debug("Intent Synchronizer: cannot submit intents: " +
Jonathan Hart9a426f82015-09-03 15:43:13 +0200247 "not elected leader anymore");
248 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700249 return;
Jonathan Hart9a426f82015-09-03 15:43:13 +0200250 }
251
252 if (isElectedLeader) {
Jonathan Hart365335e2015-12-10 11:09:53 -0800253 // Allow push of Intents
254 isActivatedLeader = true;
Pingping Line28ae4c2015-03-13 11:37:03 -0700255 } else {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200256 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700257 }
Luca Prete00043db2015-11-03 15:40:40 -0800258 log.debug("Intent synchronization completed");
Pingping Line28ae4c2015-03-13 11:37:03 -0700259 }
Jonathan Hart365335e2015-12-10 11:09:53 -0800260
261 @Override
262 public void modifyPrimary(boolean isPrimary) {
263 leaderChanged(isPrimary);
264 }
265
266 /**
267 * A listener for leadership events.
268 */
269 private class InternalLeadershipListener implements LeadershipEventListener {
270
271 @Override
Madan Jampani620f70d2016-01-30 22:22:47 -0800272 public boolean isRelevant(LeadershipEvent event) {
273 return event.subject().topic().equals(appId.name());
274 }
Jonathan Hart365335e2015-12-10 11:09:53 -0800275
Madan Jampani620f70d2016-01-30 22:22:47 -0800276 @Override
277 public void event(LeadershipEvent event) {
Jonathan Hart365335e2015-12-10 11:09:53 -0800278 switch (event.type()) {
Madan Jampani620f70d2016-01-30 22:22:47 -0800279 case LEADER_CHANGED:
280 case LEADER_AND_CANDIDATES_CHANGED:
281 if (localNodeId.equals(event.subject().leaderNodeId())) {
282 log.info("IntentSynchronizer gained leadership");
283 leaderChanged(true);
284 } else {
285 log.info("IntentSynchronizer leader changed. New leader is {}", event.subject().leaderNodeId());
286 leaderChanged(false);
287 }
Jonathan Hart365335e2015-12-10 11:09:53 -0800288 default:
289 break;
290 }
291 }
292 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800293}