blob: e54f2f239c65bc6182d9776c98c4014aaf0f23c6 [file] [log] [blame]
Charles Chan7f987c52018-07-31 18:22:46 -07001/*
2 * Copyright 2018-present Open Networking Foundation
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.l2lb.app;
18
Charles Chan7f987c52018-07-31 18:22:46 -070019import com.google.common.collect.Sets;
20import org.onlab.util.KryoNamespace;
pierddc59d92018-11-20 15:06:43 +010021import org.onosproject.cluster.ClusterService;
22import org.onosproject.cluster.LeadershipService;
23import org.onosproject.cluster.NodeId;
Charles Chan7f987c52018-07-31 18:22:46 -070024import org.onosproject.core.ApplicationId;
25import org.onosproject.core.CoreService;
26import org.onosproject.l2lb.api.L2Lb;
pierddc59d92018-11-20 15:06:43 +010027import org.onosproject.l2lb.api.L2LbData;
Charles Chan7f987c52018-07-31 18:22:46 -070028import org.onosproject.l2lb.api.L2LbEvent;
29import org.onosproject.l2lb.api.L2LbAdminService;
30import org.onosproject.l2lb.api.L2LbId;
31import org.onosproject.l2lb.api.L2LbListener;
32import org.onosproject.l2lb.api.L2LbMode;
33import org.onosproject.l2lb.api.L2LbService;
34import org.onosproject.mastership.MastershipService;
35import org.onosproject.net.DeviceId;
36import org.onosproject.net.PortNumber;
pierddc59d92018-11-20 15:06:43 +010037import org.onosproject.net.device.DeviceEvent;
38import org.onosproject.net.device.DeviceListener;
Charles Chan7f987c52018-07-31 18:22:46 -070039import org.onosproject.net.device.DeviceService;
40import org.onosproject.net.flow.DefaultTrafficSelector;
41import org.onosproject.net.flow.DefaultTrafficTreatment;
42import org.onosproject.net.flow.TrafficSelector;
43import org.onosproject.net.flow.TrafficTreatment;
44import org.onosproject.net.flowobjective.DefaultNextObjective;
45import org.onosproject.net.flowobjective.FlowObjectiveService;
46import org.onosproject.net.flowobjective.NextObjective;
47import org.onosproject.net.flowobjective.Objective;
48import org.onosproject.net.flowobjective.ObjectiveContext;
49import org.onosproject.net.flowobjective.ObjectiveError;
50import org.onosproject.net.intf.InterfaceService;
51import org.onosproject.net.packet.PacketService;
52import org.onosproject.store.serializers.KryoNamespaces;
53import org.onosproject.store.service.ConsistentMap;
54import org.onosproject.store.service.MapEvent;
55import org.onosproject.store.service.MapEventListener;
56import org.onosproject.store.service.Serializer;
57import org.onosproject.store.service.StorageService;
58import org.onosproject.store.service.Versioned;
59import org.osgi.service.component.annotations.Activate;
60import org.osgi.service.component.annotations.Component;
61import org.osgi.service.component.annotations.Deactivate;
62import org.osgi.service.component.annotations.Reference;
63import org.osgi.service.component.annotations.ReferenceCardinality;
64import org.slf4j.Logger;
65
66import java.util.Map;
67import java.util.Set;
68import java.util.concurrent.ExecutorService;
69import java.util.concurrent.Executors;
70
71import static org.onlab.util.Tools.groupedThreads;
72import static org.slf4j.LoggerFactory.getLogger;
73
74@Component(
75 immediate = true,
76 service = {
77 L2LbService.class,
78 L2LbAdminService.class
79 }
80)
81public class L2LbManager implements L2LbService, L2LbAdminService {
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY)
84 private CoreService coreService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY)
87 private PacketService packetService;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY)
90 private InterfaceService interfaceService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY)
93 private StorageService storageService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY)
96 private FlowObjectiveService flowObjService;
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY)
99 private MastershipService mastershipService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY)
pierddc59d92018-11-20 15:06:43 +0100102 private LeadershipService leadershipService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY)
105 private ClusterService clusterService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Charles Chan7f987c52018-07-31 18:22:46 -0700108 private DeviceService deviceService;
109
110 private static final Logger log = getLogger(L2LbManager.class);
111 private static final String APP_NAME = "org.onosproject.l2lb";
112
113 private ApplicationId appId;
114 private ConsistentMap<L2LbId, L2Lb> l2LbStore;
115 private ConsistentMap<L2LbId, Integer> l2LbNextStore;
pierddc59d92018-11-20 15:06:43 +0100116 // TODO Evaluate if ResourceService is a better option
117 private ConsistentMap<L2LbId, ApplicationId> l2LbResStore;
Charles Chan7f987c52018-07-31 18:22:46 -0700118 private Set<L2LbListener> listeners = Sets.newConcurrentHashSet();
119
120 private ExecutorService l2LbEventExecutor;
121 private ExecutorService l2LbProvExecutor;
pierddc59d92018-11-20 15:06:43 +0100122 private ExecutorService deviceEventExecutor;
123
Charles Chan7f987c52018-07-31 18:22:46 -0700124 private MapEventListener<L2LbId, L2Lb> l2LbStoreListener;
125 // TODO build CLI to view and clear the next store
126 private MapEventListener<L2LbId, Integer> l2LbNextStoreListener;
pierddc59d92018-11-20 15:06:43 +0100127 private MapEventListener<L2LbId, ApplicationId> l2LbResStoreListener;
128 private final DeviceListener deviceListener = new InternalDeviceListener();
Charles Chan7f987c52018-07-31 18:22:46 -0700129
130 @Activate
131 public void activate() {
132 appId = coreService.registerApplication(APP_NAME);
133
pierddc59d92018-11-20 15:06:43 +0100134 l2LbEventExecutor = Executors.newSingleThreadExecutor(
135 groupedThreads("l2lb-event", "%d", log));
136 l2LbProvExecutor = Executors.newSingleThreadExecutor(
137 groupedThreads("l2lb-prov", "%d", log));
138 deviceEventExecutor = Executors.newSingleThreadScheduledExecutor(
139 groupedThreads("l2lb-dev-event", "%d", log));
Charles Chan7f987c52018-07-31 18:22:46 -0700140 l2LbStoreListener = new L2LbStoreListener();
141 l2LbNextStoreListener = new L2LbNextStoreListener();
pierddc59d92018-11-20 15:06:43 +0100142 l2LbResStoreListener = new L2LbResStoreListener();
Charles Chan7f987c52018-07-31 18:22:46 -0700143
144 KryoNamespace serializer = KryoNamespace.newBuilder()
145 .register(KryoNamespaces.API)
146 .register(L2Lb.class)
147 .register(L2LbId.class)
148 .register(L2LbMode.class)
149 .build();
150 l2LbStore = storageService.<L2LbId, L2Lb>consistentMapBuilder()
151 .withName("onos-l2lb-store")
152 .withRelaxedReadConsistency()
153 .withSerializer(Serializer.using(serializer))
154 .build();
155 l2LbStore.addListener(l2LbStoreListener);
156 l2LbNextStore = storageService.<L2LbId, Integer>consistentMapBuilder()
157 .withName("onos-l2lb-next-store")
158 .withRelaxedReadConsistency()
159 .withSerializer(Serializer.using(serializer))
160 .build();
161 l2LbNextStore.addListener(l2LbNextStoreListener);
pierddc59d92018-11-20 15:06:43 +0100162 l2LbResStore = storageService.<L2LbId, ApplicationId>consistentMapBuilder()
163 .withName("onos-l2lb-res-store")
164 .withRelaxedReadConsistency()
165 .withSerializer(Serializer.using(serializer))
166 .build();
167 l2LbResStore.addListener(l2LbResStoreListener);
168
169 deviceService.addListener(deviceListener);
Charles Chan7f987c52018-07-31 18:22:46 -0700170
171 log.info("Started");
172 }
173
174 @Deactivate
175 public void deactivate() {
176 l2LbStore.removeListener(l2LbStoreListener);
177 l2LbNextStore.removeListener(l2LbNextStoreListener);
178
179 l2LbEventExecutor.shutdown();
pierddc59d92018-11-20 15:06:43 +0100180 l2LbProvExecutor.shutdown();
181 deviceEventExecutor.shutdown();
Charles Chan7f987c52018-07-31 18:22:46 -0700182
183 log.info("Stopped");
184 }
185
186 @Override
187 public void addListener(L2LbListener listener) {
188 listeners.add(listener);
189 }
190
191 @Override
192 public void removeListener(L2LbListener listener) {
193 listeners.remove(listener);
194 }
195
196 @Override
197 public L2Lb createOrUpdate(DeviceId deviceId, int key, Set<PortNumber> ports, L2LbMode mode) {
198 L2LbId l2LbId = new L2LbId(deviceId, key);
199 log.debug("Putting {} -> {} {} into L2 load balancer store", l2LbId, mode, ports);
200 return Versioned.valueOrNull(l2LbStore.put(l2LbId, new L2Lb(l2LbId, ports, mode)));
201 }
202
203 @Override
204 public L2Lb remove(DeviceId deviceId, int key) {
205 L2LbId l2LbId = new L2LbId(deviceId, key);
pierddc59d92018-11-20 15:06:43 +0100206 ApplicationId reservation = Versioned.valueOrNull(l2LbResStore.get(l2LbId));
207 // Remove only if it is not used - otherwise it is necessary to release first
208 if (reservation == null) {
209 log.debug("Removing {} from L2 load balancer store", l2LbId);
210 return Versioned.valueOrNull(l2LbStore.remove(l2LbId));
211 }
212 log.warn("Removal {} from L2 load balancer store was not possible " +
213 "due to a previous reservation", l2LbId);
214 return null;
Charles Chan7f987c52018-07-31 18:22:46 -0700215 }
216
217 @Override
218 public Map<L2LbId, L2Lb> getL2Lbs() {
219 return l2LbStore.asJavaMap();
220 }
221
222 @Override
223 public L2Lb getL2Lb(DeviceId deviceId, int key) {
224 return Versioned.valueOrNull(l2LbStore.get(new L2LbId(deviceId, key)));
225 }
226
227 @Override
228 public Map<L2LbId, Integer> getL2LbNexts() {
229 return l2LbNextStore.asJavaMap();
230 }
231
232 @Override
piercc6ca772018-11-24 11:16:28 -0800233 public int getL2LbNext(DeviceId deviceId, int key) {
Charles Chan7f987c52018-07-31 18:22:46 -0700234 return Versioned.valueOrNull(l2LbNextStore.get(new L2LbId(deviceId, key)));
235 }
236
pierddc59d92018-11-20 15:06:43 +0100237 @Override
238 public boolean reserve(L2LbId l2LbId, ApplicationId appId) {
239 // Check if the resource is available
240 ApplicationId reservation = Versioned.valueOrNull(l2LbResStore.get(l2LbId));
241 L2Lb l2Lb = Versioned.valueOrNull(l2LbStore.get(l2LbId));
242 if (reservation == null && l2Lb != null) {
243 log.debug("Reserving {} -> {} into L2 load balancer reservation store", l2LbId, appId);
244 return l2LbResStore.put(l2LbId, appId) == null;
245 } else if (reservation != null && reservation.equals(appId)) {
246 // App try to reserve the resource a second time
247 log.debug("Already reserved {} -> {} skip reservation", l2LbId, appId);
248 return true;
249 }
250 log.warn("Reservation failed {} -> {}", l2LbId, appId);
251 return false;
252 }
253
254 @Override
255 public boolean release(L2LbId l2LbId, ApplicationId appId) {
256 // Check if the resource is reserved
257 ApplicationId reservation = Versioned.valueOrNull(l2LbResStore.get(l2LbId));
258 if (reservation != null && reservation.equals(appId)) {
259 log.debug("Removing {} -> {} from L2 load balancer reservation store", l2LbId, appId);
260 return l2LbResStore.remove(l2LbId) != null;
261 }
262 log.warn("Release failed {} -> {}", l2LbId, appId);
263 return false;
264 }
265
266 @Override
267 public ApplicationId getReservation(L2LbId l2LbId) {
268 return Versioned.valueOrNull(l2LbResStore.get(l2LbId));
269 }
270
271 @Override
272 public Map<L2LbId, ApplicationId> getReservations() {
273 return l2LbResStore.asJavaMap();
274 }
275
Charles Chan7f987c52018-07-31 18:22:46 -0700276 private class L2LbStoreListener implements MapEventListener<L2LbId, L2Lb> {
277 public void event(MapEvent<L2LbId, L2Lb> event) {
278 switch (event.type()) {
279 case INSERT:
280 log.debug("L2Lb {} insert new={}, old={}", event.key(), event.newValue(), event.oldValue());
pierddc59d92018-11-20 15:06:43 +0100281 post(new L2LbEvent(L2LbEvent.Type.ADDED, event.newValue().value().data(), null));
Charles Chan7f987c52018-07-31 18:22:46 -0700282 populateL2Lb(event.newValue().value());
283 break;
284 case REMOVE:
285 log.debug("L2Lb {} remove new={}, old={}", event.key(), event.newValue(), event.oldValue());
pierddc59d92018-11-20 15:06:43 +0100286 post(new L2LbEvent(L2LbEvent.Type.REMOVED, null, event.oldValue().value().data()));
Charles Chan7f987c52018-07-31 18:22:46 -0700287 revokeL2Lb(event.oldValue().value());
288 break;
289 case UPDATE:
290 log.debug("L2Lb {} update new={}, old={}", event.key(), event.newValue(), event.oldValue());
pierddc59d92018-11-20 15:06:43 +0100291 post(new L2LbEvent(L2LbEvent.Type.UPDATED, event.newValue().value().data(),
292 event.oldValue().value().data()));
Charles Chan7f987c52018-07-31 18:22:46 -0700293 updateL2Lb(event.newValue().value(), event.oldValue().value());
294 break;
295 default:
296 break;
297 }
298 }
299 }
300
301 private class L2LbNextStoreListener implements MapEventListener<L2LbId, Integer> {
302 public void event(MapEvent<L2LbId, Integer> event) {
303 switch (event.type()) {
304 case INSERT:
305 log.debug("L2Lb next {} insert new={}, old={}", event.key(), event.newValue(), event.oldValue());
306 break;
307 case REMOVE:
308 log.debug("L2Lb next {} remove new={}, old={}", event.key(), event.newValue(), event.oldValue());
309 break;
310 case UPDATE:
311 log.debug("L2Lb next {} update new={}, old={}", event.key(), event.newValue(), event.oldValue());
312 break;
313 default:
314 break;
315 }
316 }
317 }
318
pierddc59d92018-11-20 15:06:43 +0100319 private class L2LbResStoreListener implements MapEventListener<L2LbId, ApplicationId> {
320 public void event(MapEvent<L2LbId, ApplicationId> event) {
321 switch (event.type()) {
322 case INSERT:
323 log.debug("L2Lb reservation {} insert new={}, old={}", event.key(), event.newValue(),
324 event.oldValue());
325 break;
326 case REMOVE:
327 log.debug("L2Lb reservation {} remove new={}, old={}", event.key(), event.newValue(),
328 event.oldValue());
329 break;
330 case UPDATE:
331 log.debug("L2Lb reservation {} update new={}, old={}", event.key(), event.newValue(),
332 event.oldValue());
333 break;
334 default:
335 break;
336 }
337 }
338 }
339
340 private class InternalDeviceListener implements DeviceListener {
341 // We want to manage only a subset of events and if we are the leader
342 @Override
343 public void event(DeviceEvent event) {
344 deviceEventExecutor.execute(() -> {
345 DeviceId deviceId = event.subject().id();
346 if (!isLocalLeader(deviceId)) {
347 log.debug("Not the leader of {}. Skip event {}", deviceId, event);
348 return;
349 }
350 // Populate or revoke according to the device availability
351 if (deviceService.isAvailable(deviceId)) {
352 init(deviceId);
353 } else {
354 cleanup(deviceId);
355 }
356 });
357 }
358 // Some events related to the devices are skipped
359 @Override
360 public boolean isRelevant(DeviceEvent event) {
361 return event.type() == DeviceEvent.Type.DEVICE_ADDED ||
362 event.type() == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED ||
363 event.type() == DeviceEvent.Type.DEVICE_UPDATED;
364 }
365 }
366
Charles Chan7f987c52018-07-31 18:22:46 -0700367 private void post(L2LbEvent l2LbEvent) {
368 l2LbEventExecutor.execute(() -> {
369 for (L2LbListener l : listeners) {
pierddc59d92018-11-20 15:06:43 +0100370 if (l.isRelevant(l2LbEvent)) {
371 l.event(l2LbEvent);
372 }
Charles Chan7f987c52018-07-31 18:22:46 -0700373 }
374 });
375 }
376
pierddc59d92018-11-20 15:06:43 +0100377 private void init(DeviceId deviceId) {
378 l2LbStore.entrySet().stream()
379 .filter(l2lbentry -> l2lbentry.getKey().deviceId().equals(deviceId))
380 .forEach(l2lbentry -> populateL2Lb(l2lbentry.getValue().value()));
381 }
382
383 private void cleanup(DeviceId deviceId) {
384 l2LbStore.entrySet().stream()
385 .filter(entry -> entry.getKey().deviceId().equals(deviceId))
386 .forEach(entry -> l2LbNextStore.remove(entry.getKey()));
387 log.debug("{} is removed from l2LbNextObjStore", deviceId);
388 }
389
Charles Chan7f987c52018-07-31 18:22:46 -0700390 private void populateL2Lb(L2Lb l2Lb) {
391 DeviceId deviceId = l2Lb.l2LbId().deviceId();
pierddc59d92018-11-20 15:06:43 +0100392 if (!isLocalLeader(deviceId)) {
393 log.debug("Not the leader of {}. Skip populateL2Lb {}", deviceId, l2Lb.l2LbId());
Charles Chan7f987c52018-07-31 18:22:46 -0700394 return;
395 }
396
397 l2LbProvExecutor.execute(() -> {
pierddc59d92018-11-20 15:06:43 +0100398 Integer nextid = Versioned.valueOrNull(l2LbNextStore.get(l2Lb.l2LbId()));
399 if (nextid == null) {
400 // Build a new context and new next objective
401 L2LbObjectiveContext context = new L2LbObjectiveContext(l2Lb.l2LbId());
402 NextObjective nextObj = nextObjBuilder(l2Lb.l2LbId(), l2Lb.ports(), nextid).add(context);
403 // Finally submit, store, and register the resource
404 flowObjService.next(deviceId, nextObj);
405 l2LbNextStore.put(l2Lb.l2LbId(), nextObj.id());
406 } else {
407 log.info("NextObj for {} already exists. Skip populateL2Lb", l2Lb.l2LbId());
408 }
Charles Chan7f987c52018-07-31 18:22:46 -0700409 });
410 }
411
412 private void revokeL2Lb(L2Lb l2Lb) {
413 DeviceId deviceId = l2Lb.l2LbId().deviceId();
pierddc59d92018-11-20 15:06:43 +0100414 if (!isLocalLeader(deviceId)) {
415 log.debug("Not the leader of {}. Skip revokeL2Lb {}", deviceId, l2Lb.l2LbId());
Charles Chan7f987c52018-07-31 18:22:46 -0700416 return;
417 }
418
419 l2LbProvExecutor.execute(() -> {
pierddc59d92018-11-20 15:06:43 +0100420 Integer nextid = Versioned.valueOrNull(l2LbNextStore.get(l2Lb.l2LbId()));
421 if (nextid != null) {
422 // Build a new context and remove old next objective
423 L2LbObjectiveContext context = new L2LbObjectiveContext(l2Lb.l2LbId());
424 NextObjective nextObj = nextObjBuilder(l2Lb.l2LbId(), l2Lb.ports(), nextid).remove(context);
425 // Finally submit and invalidate the store
426 flowObjService.next(deviceId, nextObj);
427 l2LbNextStore.remove(l2Lb.l2LbId());
428 } else {
429 log.info("NextObj for {} does not exist. Skip revokeL2Lb", l2Lb.l2LbId());
430 }
Charles Chan7f987c52018-07-31 18:22:46 -0700431 });
432 }
433
434 private void updateL2Lb(L2Lb newL2Lb, L2Lb oldL2Lb) {
435 DeviceId deviceId = newL2Lb.l2LbId().deviceId();
pierddc59d92018-11-20 15:06:43 +0100436 if (!isLocalLeader(deviceId)) {
437 log.debug("Not the leader of {}. Skip updateL2Lb {}", deviceId, newL2Lb.l2LbId());
Charles Chan7f987c52018-07-31 18:22:46 -0700438 return;
439 }
440
441 l2LbProvExecutor.execute(() -> {
pierddc59d92018-11-20 15:06:43 +0100442 Integer nextid = Versioned.valueOrNull(l2LbNextStore.get(newL2Lb.l2LbId()));
443 if (nextid != null) {
444 // Compute modifications and context
445 L2LbObjectiveContext context = new L2LbObjectiveContext(newL2Lb.l2LbId());
446 Set<PortNumber> portsToBeAdded = Sets.difference(newL2Lb.ports(), oldL2Lb.ports());
447 Set<PortNumber> portsToBeRemoved = Sets.difference(oldL2Lb.ports(), newL2Lb.ports());
448 // and send them to the flowobj subsystem
449 if (!portsToBeAdded.isEmpty()) {
450 flowObjService.next(deviceId, nextObjBuilder(newL2Lb.l2LbId(), portsToBeAdded, nextid)
451 .addToExisting(context));
452 } else {
453 log.debug("NextObj for {} nothing to add", newL2Lb.l2LbId());
Charles Chan7f987c52018-07-31 18:22:46 -0700454
pierddc59d92018-11-20 15:06:43 +0100455 }
456 if (!portsToBeRemoved.isEmpty()) {
457 flowObjService.next(deviceId, nextObjBuilder(newL2Lb.l2LbId(), portsToBeRemoved, nextid)
458 .removeFromExisting(context));
459 } else {
460 log.debug("NextObj for {} nothing to remove", newL2Lb.l2LbId());
461 }
462 } else {
463 log.info("NextObj for {} does not exist. Skip updateL2Lb", newL2Lb.l2LbId());
464 }
Charles Chan7f987c52018-07-31 18:22:46 -0700465 });
466 }
467
pierddc59d92018-11-20 15:06:43 +0100468 private NextObjective.Builder nextObjBuilder(L2LbId l2LbId, Set<PortNumber> ports, Integer nextId) {
469 if (nextId == null) {
470 nextId = flowObjService.allocateNextId();
471 }
Charles Chan7f987c52018-07-31 18:22:46 -0700472 // TODO replace logical l2lb port
473 TrafficSelector meta = DefaultTrafficSelector.builder()
474 .matchInPort(PortNumber.portNumber(l2LbId.key())).build();
475 NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
476 .withId(nextId)
477 .withMeta(meta)
478 .withType(NextObjective.Type.HASHED)
479 .fromApp(appId);
480 ports.forEach(port -> {
481 TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(port).build();
482 nextObjBuilder.addTreatment(treatment);
483 });
484 return nextObjBuilder;
485 }
486
pierddc59d92018-11-20 15:06:43 +0100487 // Custom-built function, when the device is not available we need a fallback mechanism
488 private boolean isLocalLeader(DeviceId deviceId) {
489 if (!mastershipService.isLocalMaster(deviceId)) {
490 // When the device is available we just check the mastership
491 if (deviceService.isAvailable(deviceId)) {
492 return false;
493 }
494 // Fallback with Leadership service - device id is used as topic
495 NodeId leader = leadershipService.runForLeadership(
496 deviceId.toString()).leaderNodeId();
497 // Verify if this node is the leader
498 return clusterService.getLocalNode().id().equals(leader);
499 }
500 return true;
501 }
502
Charles Chan7f987c52018-07-31 18:22:46 -0700503 private final class L2LbObjectiveContext implements ObjectiveContext {
504 private final L2LbId l2LbId;
505
506 private L2LbObjectiveContext(L2LbId l2LbId) {
507 this.l2LbId = l2LbId;
508 }
509
510 @Override
511 public void onSuccess(Objective objective) {
512 NextObjective nextObj = (NextObjective) objective;
pierddc59d92018-11-20 15:06:43 +0100513 log.debug("Success {} nextobj {} for L2 load balancer {}", nextObj.op(), nextObj, l2LbId);
514 // Operation done
515 L2LbData oldl2LbData = new L2LbData(l2LbId);
516 L2LbData newl2LbData = new L2LbData(l2LbId);
517 l2LbProvExecutor.execute(() -> {
518 // Other operations will not lead to a generation of an event
519 switch (nextObj.op()) {
520 case ADD:
521 newl2LbData.setNextId(nextObj.id());
522 post(new L2LbEvent(L2LbEvent.Type.INSTALLED, newl2LbData, oldl2LbData));
523 break;
524 case REMOVE:
525 oldl2LbData.setNextId(nextObj.id());
526 post(new L2LbEvent(L2LbEvent.Type.UNINSTALLED, newl2LbData, oldl2LbData));
527 break;
528 default:
529 break;
530 }
531 });
Charles Chan7f987c52018-07-31 18:22:46 -0700532 }
533
534 @Override
535 public void onError(Objective objective, ObjectiveError error) {
536 NextObjective nextObj = (NextObjective) objective;
pierddc59d92018-11-20 15:06:43 +0100537 log.debug("Failed {} nextobj {} for L2 load balancer {} due to {}", nextObj.op(), nextObj,
538 l2LbId, error);
539 l2LbProvExecutor.execute(() -> {
540 // Init the data structure
541 L2LbData l2LbData = new L2LbData(l2LbId);
542 // Update the next id and send the event;
543 switch (nextObj.op()) {
544 case ADD:
545 // If ADD is failing apps do not know the next id; let's update the store
546 l2LbNextStore.remove(l2LbId);
547 post(new L2LbEvent(L2LbEvent.Type.FAILED, l2LbData, l2LbData));
548 break;
549 case REMOVE:
550 // If REMOVE is failing let's send also the info about the next id; no need to update the store
551 l2LbData.setNextId(nextObj.id());
552 post(new L2LbEvent(L2LbEvent.Type.FAILED, l2LbData, l2LbData));
553 break;
554 default:
555 break;
556 }
557 });
Charles Chan7f987c52018-07-31 18:22:46 -0700558 }
559
560 }
561}