blob: 35383dcf263da82a8587242cf78fb761a19863e0 [file] [log] [blame]
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -08001/*
Ray Milkey34c95902015-04-15 09:47:53 -07002 * Copyright 2014-2015 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 */
Brian O'Connorabafb502014-12-02 22:26:20 -080016package org.onosproject.sdnip;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080017
Jonathan Hart365335e2015-12-10 11:09:53 -080018import org.apache.felix.scr.annotations.Activate;
19import org.apache.felix.scr.annotations.Component;
20import org.apache.felix.scr.annotations.Deactivate;
21import org.apache.felix.scr.annotations.Reference;
22import org.apache.felix.scr.annotations.ReferenceCardinality;
23import org.apache.felix.scr.annotations.Service;
24import org.onosproject.cluster.ClusterService;
25import org.onosproject.cluster.LeadershipEvent;
26import org.onosproject.cluster.LeadershipEventListener;
27import org.onosproject.cluster.LeadershipService;
28import org.onosproject.core.ApplicationId;
29import org.onosproject.core.CoreService;
30import org.onosproject.net.intent.Intent;
31import org.onosproject.net.intent.IntentService;
32import org.onosproject.net.intent.IntentState;
33import org.onosproject.net.intent.Key;
34import org.onosproject.routing.IntentSynchronizationAdminService;
35import org.onosproject.routing.IntentSynchronizationService;
36import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
Pingping Linc07781f2015-10-30 00:44:41 -070038
39import java.util.HashMap;
40import java.util.LinkedList;
41import java.util.List;
42import java.util.Map;
43import java.util.Map.Entry;
Jonathan Hart365335e2015-12-10 11:09:53 -080044import java.util.Objects;
Pingping Linc07781f2015-10-30 00:44:41 -070045import java.util.concurrent.ConcurrentHashMap;
46import java.util.concurrent.ExecutorService;
47
Luca Prete86ac7d12015-12-02 23:36:49 -080048import org.onosproject.net.intent.IntentUtils;
Jonathan Hart365335e2015-12-10 11:09:53 -080049
50import 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
57@Component(immediate = true)
58public 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
77 private ApplicationId appId;
78
79 private final InternalLeadershipListener leadershipEventListener =
80 new InternalLeadershipListener();
81
82 private final Map<Key, Intent> intents = new ConcurrentHashMap<>();
83
84 private ExecutorService intentsSynchronizerExecutor;
85
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -080086 private volatile boolean isElectedLeader = false;
87 private volatile boolean isActivatedLeader = false;
88
Jonathan Hart365335e2015-12-10 11:09:53 -080089 @Activate
90 public void activate() {
91 intentsSynchronizerExecutor = createExecutor();
92
93 this.appId = coreService.registerApplication(APP_NAME);
94
95 leadershipService.addListener(leadershipEventListener);
96 leadershipService.runForLeadership(appId.name());
97
98 log.info("Started");
Jonathan Hart9a426f82015-09-03 15:43:13 +020099 }
100
Jonathan Hart365335e2015-12-10 11:09:53 -0800101 @Deactivate
102 public void deactivate() {
103 leadershipService.withdraw(appId.name());
104 leadershipService.removeListener(leadershipEventListener);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800105
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800106 synchronized (this) {
Luca Prete00043db2015-11-03 15:40:40 -0800107 intentsSynchronizerExecutor.shutdownNow();
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800108 }
Jonathan Hart365335e2015-12-10 11:09:53 -0800109
110 log.info("Stopped");
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800111 }
112
Pingping Linc07781f2015-10-30 00:44:41 -0700113 /**
Jonathan Hart365335e2015-12-10 11:09:53 -0800114 * Creates an executor that will be used for synchronization tasks.
115 * <p>
116 * Can be overridden to change the type of executor used.
117 * </p>
118 *
119 * @return executor service
Pingping Linc07781f2015-10-30 00:44:41 -0700120 */
Jonathan Hart365335e2015-12-10 11:09:53 -0800121 protected ExecutorService createExecutor() {
122 return newSingleThreadExecutor(groupedThreads("onos/" + appId, "sync"));
123 }
124
125 @Override
Pingping Linc07781f2015-10-30 00:44:41 -0700126 public void removeIntents() {
127 if (!isElectedLeader) {
Jonathan Hart365335e2015-12-10 11:09:53 -0800128 // Only leader will withdraw intents
Pingping Linc07781f2015-10-30 00:44:41 -0700129 return;
130 }
131
132 log.debug("Intent Synchronizer shutdown: withdrawing all intents...");
133
134 for (Entry<Key, Intent> entry : intents.entrySet()) {
135 intentService.withdraw(entry.getValue());
136 log.debug("Intent Synchronizer withdrawing intent: {}",
137 entry.getValue());
138 }
139
140 intents.clear();
141 log.info("Tried to clean all intents");
142 }
143
Jonathan Hart9a426f82015-09-03 15:43:13 +0200144 @Override
145 public void submit(Intent intent) {
146 synchronized (this) {
147 intents.put(intent.key(), intent);
148 if (isElectedLeader && isActivatedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800149 log.trace("Submitting intent: {}", intent);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200150 intentService.submit(intent);
151 }
152 }
153 }
154
155 @Override
156 public void withdraw(Intent intent) {
157 synchronized (this) {
158 intents.remove(intent.key(), intent);
159 if (isElectedLeader && isActivatedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800160 log.trace("Withdrawing intent: {}", intent);
Jonathan Hart9a426f82015-09-03 15:43:13 +0200161 intentService.withdraw(intent);
162 }
163 }
164 }
165
Jonathan Hart51372182014-12-03 21:32:34 -0800166 /**
Luca Prete00043db2015-11-03 15:40:40 -0800167 * Signals the synchronizer that the leadership has changed.
Jonathan Hart51372182014-12-03 21:32:34 -0800168 *
169 * @param isLeader true if this instance is now the leader, otherwise false
170 */
Jonathan Hart365335e2015-12-10 11:09:53 -0800171 private void leaderChanged(boolean isLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800172 log.debug("Leader changed: {}", isLeader);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800173
174 if (!isLeader) {
175 this.isElectedLeader = false;
176 this.isActivatedLeader = false;
Jonathan Hart365335e2015-12-10 11:09:53 -0800177 // Nothing to do
178 return;
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800179 }
180 this.isActivatedLeader = false;
181 this.isElectedLeader = true;
182
Jonathan Hart365335e2015-12-10 11:09:53 -0800183 // Run the synchronization task
Luca Prete00043db2015-11-03 15:40:40 -0800184 intentsSynchronizerExecutor.execute(this::synchronizeIntents);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800185 }
186
Jonathan Hart9a426f82015-09-03 15:43:13 +0200187 private void synchronizeIntents() {
188 Map<Key, Intent> serviceIntents = new HashMap<>();
189 intentService.getIntents().forEach(i -> {
190 if (i.appId().equals(appId)) {
191 serviceIntents.put(i.key(), i);
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800192 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200193 });
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800194
Jonathan Hart9a426f82015-09-03 15:43:13 +0200195 List<Intent> intentsToAdd = new LinkedList<>();
196 List<Intent> intentsToRemove = new LinkedList<>();
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800197
Jonathan Hart9a426f82015-09-03 15:43:13 +0200198 for (Intent localIntent : intents.values()) {
199 Intent serviceIntent = serviceIntents.remove(localIntent.key());
200 if (serviceIntent == null) {
201 intentsToAdd.add(localIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800202 } else {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200203 IntentState state = intentService.getIntentState(serviceIntent.key());
Ray Milkey4fd3ceb2015-12-10 14:43:08 -0800204 if (!IntentUtils.intentsAreEqual(serviceIntent, localIntent) || state == null ||
Jonathan Hart9a426f82015-09-03 15:43:13 +0200205 state == IntentState.WITHDRAW_REQ ||
206 state == IntentState.WITHDRAWING ||
207 state == IntentState.WITHDRAWN) {
208 intentsToAdd.add(localIntent);
Pavlin Radoslavova7243cc2014-11-22 21:38:02 -0800209 }
Pingping Line28ae4c2015-03-13 11:37:03 -0700210 }
211 }
Jonathan Hart9a426f82015-09-03 15:43:13 +0200212
213 for (Intent serviceIntent : serviceIntents.values()) {
214 IntentState state = intentService.getIntentState(serviceIntent.key());
215 if (state != null && state != IntentState.WITHDRAW_REQ
216 && state != IntentState.WITHDRAWING
217 && state != IntentState.WITHDRAWN) {
218 intentsToRemove.add(serviceIntent);
219 }
220 }
221
Luca Prete00043db2015-11-03 15:40:40 -0800222 log.debug("Intent Synchronizer: submitting {}, withdrawing {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200223 intentsToAdd.size(), intentsToRemove.size());
224
225 // Withdraw Intents
226 for (Intent intent : intentsToRemove) {
227 intentService.withdraw(intent);
Luca Prete00043db2015-11-03 15:40:40 -0800228 log.trace("Intent Synchronizer: withdrawing intent: {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200229 intent);
230 }
231 if (!isElectedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800232 log.debug("Intent Synchronizer: cannot withdraw intents: " +
Jonathan Hart9a426f82015-09-03 15:43:13 +0200233 "not elected leader anymore");
234 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700235 return;
236 }
237
Jonathan Hart9a426f82015-09-03 15:43:13 +0200238 // Add Intents
239 for (Intent intent : intentsToAdd) {
240 intentService.submit(intent);
Luca Prete00043db2015-11-03 15:40:40 -0800241 log.trace("Intent Synchronizer: submitting intent: {}",
Jonathan Hart9a426f82015-09-03 15:43:13 +0200242 intent);
243 }
244 if (!isElectedLeader) {
Luca Prete00043db2015-11-03 15:40:40 -0800245 log.debug("Intent Synchronizer: cannot submit intents: " +
Jonathan Hart9a426f82015-09-03 15:43:13 +0200246 "not elected leader anymore");
247 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700248 return;
Jonathan Hart9a426f82015-09-03 15:43:13 +0200249 }
250
251 if (isElectedLeader) {
Jonathan Hart365335e2015-12-10 11:09:53 -0800252 // Allow push of Intents
253 isActivatedLeader = true;
Pingping Line28ae4c2015-03-13 11:37:03 -0700254 } else {
Jonathan Hart9a426f82015-09-03 15:43:13 +0200255 isActivatedLeader = false;
Pingping Line28ae4c2015-03-13 11:37:03 -0700256 }
Luca Prete00043db2015-11-03 15:40:40 -0800257 log.debug("Intent synchronization completed");
Pingping Line28ae4c2015-03-13 11:37:03 -0700258 }
Jonathan Hart365335e2015-12-10 11:09:53 -0800259
260 @Override
261 public void modifyPrimary(boolean isPrimary) {
262 leaderChanged(isPrimary);
263 }
264
265 /**
266 * A listener for leadership events.
267 */
268 private class InternalLeadershipListener implements LeadershipEventListener {
269
270 @Override
271 public void event(LeadershipEvent event) {
272 if (!event.subject().topic().equals(appId.name())) {
273 // Not our topic: ignore
274 return;
275 }
276 if (!Objects.equals(event.subject().leader(),
277 clusterService.getLocalNode().id())) {
278 // The event is not about this instance: ignore
279 return;
280 }
281
282 switch (event.type()) {
283 case LEADER_ELECTED:
284 log.info("IntentSynchronizer gained leadership");
285 leaderChanged(true);
286 break;
287 case LEADER_BOOTED:
288 log.info("IntentSynchronizer lost leadership");
289 leaderChanged(false);
290 break;
291 case LEADER_REELECTED:
292 default:
293 break;
294 }
295 }
296 }
Pavlin Radoslavova071b1e2014-11-17 13:37:57 -0800297}