Extends L2LbManager
- reacts to device events
- updates/removes l2lb
- implements bookeeping of the l2lb
- extends L2LbEvent to proper notify other components
Change-Id: I944fe6415324d71c361bafc4146dd176493f2dc7
diff --git a/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2Lb.java b/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2Lb.java
index 3c377ff..633af1e 100644
--- a/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2Lb.java
+++ b/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2Lb.java
@@ -70,6 +70,15 @@
return mode;
}
+ /**
+ * Gets L2 load balancer data.
+ *
+ * @return L2 load balancer data
+ */
+ public L2LbData data() {
+ return new L2LbData(l2LbId);
+ }
+
@Override
public int hashCode() {
return Objects.hash(l2LbId, ports, mode);
diff --git a/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2LbAdminService.java b/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2LbAdminService.java
index 076ed36..b429fe1 100644
--- a/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2LbAdminService.java
+++ b/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2LbAdminService.java
@@ -40,7 +40,7 @@
*
* @param deviceId Device ID
* @param key L2 load balancer key
- * @return L2 load balancer that is removed
+ * @return L2 load balancer that is removed or null if it was not possible
*/
L2Lb remove(DeviceId deviceId, int key);
diff --git a/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2LbData.java b/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2LbData.java
new file mode 100644
index 0000000..d50dbbd
--- /dev/null
+++ b/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2LbData.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.l2lb.api;
+
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Represents L2 load balancer event data.
+ */
+public class L2LbData {
+
+ // We exchange only id and nextid in the events
+ private L2LbId l2LbId;
+ private int nextId;
+
+ /**
+ * Constructs a L2 load balancer data.
+ *
+ * @param l2LbId L2 load balancer ID
+ */
+ public L2LbData(L2LbId l2LbId) {
+ this.l2LbId = l2LbId;
+ this.nextId = -1;
+ }
+
+ /**
+ * Constructs a L2 load balancer data.
+ *
+ * @param l2LbId L2 load balancer ID
+ * @param nextId L2 load balancer next id
+ */
+ public L2LbData(L2LbId l2LbId, int nextId) {
+ this.l2LbId = l2LbId;
+ this.nextId = nextId;
+ }
+
+ /**
+ * Gets L2 load balancer ID.
+ *
+ * @return L2 load balancer ID
+ */
+ public L2LbId l2LbId() {
+ return l2LbId;
+ }
+
+ /**
+ * Gets L2 load balancer next id.
+ *
+ * @return L2 load balancer next id
+ */
+ public int nextId() {
+ return nextId;
+ }
+
+ /**
+ * Sets L2 load balancer next id.
+ *
+ * @param nextId L2 load balancer next id
+ */
+ public void setNextId(int nextId) {
+ this.nextId = nextId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(l2LbId, nextId);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof L2LbData)) {
+ return false;
+ }
+ final L2LbData other = (L2LbData) obj;
+
+ return Objects.equals(this.l2LbId, other.l2LbId) &&
+ this.nextId == other.nextId;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(getClass())
+ .add("l2LbId", l2LbId)
+ .add("nextId", nextId)
+ .toString();
+ }
+}
diff --git a/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2LbEvent.java b/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2LbEvent.java
index f0ac847..a0ce9ce 100644
--- a/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2LbEvent.java
+++ b/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2LbEvent.java
@@ -22,9 +22,9 @@
import static com.google.common.base.MoreObjects.toStringHelper;
-public class L2LbEvent extends AbstractEvent<L2LbEvent.Type, L2Lb> {
+public class L2LbEvent extends AbstractEvent<L2LbEvent.Type, L2LbData> {
- private L2Lb prevSubject;
+ private L2LbData prevSubject;
/**
* L2 load balancer event type.
@@ -32,7 +32,10 @@
public enum Type {
ADDED,
REMOVED,
- UPDATED
+ UPDATED,
+ INSTALLED,
+ UNINSTALLED,
+ FAILED
}
/**
@@ -42,7 +45,7 @@
* @param subject current L2 load balancer information
* @param prevSubject previous L2 load balancer information
*/
- public L2LbEvent(Type type, L2Lb subject, L2Lb prevSubject) {
+ public L2LbEvent(Type type, L2LbData subject, L2LbData prevSubject) {
super(type, subject);
this.prevSubject = prevSubject;
}
@@ -52,7 +55,7 @@
*
* @return previous subject
*/
- public L2Lb prevSubject() {
+ public L2LbData prevSubject() {
return prevSubject;
}
diff --git a/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2LbService.java b/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2LbService.java
index 8779192..db5f20c 100644
--- a/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2LbService.java
+++ b/apps/l2lb/src/main/java/org/onosproject/l2lb/api/L2LbService.java
@@ -16,6 +16,7 @@
package org.onosproject.l2lb.api;
+import org.onosproject.core.ApplicationId;
import org.onosproject.event.ListenerService;
import org.onosproject.net.DeviceId;
@@ -56,4 +57,43 @@
* @return next ID
*/
int getL2LbNexts(DeviceId deviceId, int key);
+
+ /**
+ * Reserves a l2 load balancer. Only one application
+ * at time can reserve a given l2 load balancer.
+ *
+ * @param l2LbId the l2 load balancer id
+ * @param appId the application id
+ * @return true if reservation was successful false otherwise
+ */
+ boolean reserve(L2LbId l2LbId, ApplicationId appId);
+
+ /**
+ * Releases a l2 load balancer. Once released
+ * by the owner the l2 load balancer is eligible
+ * for removal.
+ *
+ * @param l2LbId the l2 load balancer id
+ * @param appId the application id
+ * @return true if release was successful false otherwise
+ */
+ boolean release(L2LbId l2LbId, ApplicationId appId);
+
+ /**
+ * Gets reservation of a l2 load balancer. Only one application
+ * at time can reserve a given l2 load balancer.
+ *
+ * @param l2LbId the l2 load balancer id
+ * @return the id of the application using the l2 load balancer
+ */
+ ApplicationId getReservation(L2LbId l2LbId);
+
+ /**
+ * Gets l2 load balancer reservations. Only one application
+ * at time can reserve a given l2 load balancer.
+ *
+ * @return reservations of the l2 load balancer resources
+ */
+ Map<L2LbId, ApplicationId> getReservations();
+
}
diff --git a/apps/l2lb/src/main/java/org/onosproject/l2lb/app/L2LbManager.java b/apps/l2lb/src/main/java/org/onosproject/l2lb/app/L2LbManager.java
index 1f58960..3a195dc 100644
--- a/apps/l2lb/src/main/java/org/onosproject/l2lb/app/L2LbManager.java
+++ b/apps/l2lb/src/main/java/org/onosproject/l2lb/app/L2LbManager.java
@@ -16,12 +16,15 @@
package org.onosproject.l2lb.app;
-
import com.google.common.collect.Sets;
import org.onlab.util.KryoNamespace;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.l2lb.api.L2Lb;
+import org.onosproject.l2lb.api.L2LbData;
import org.onosproject.l2lb.api.L2LbEvent;
import org.onosproject.l2lb.api.L2LbAdminService;
import org.onosproject.l2lb.api.L2LbId;
@@ -31,6 +34,8 @@
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -94,6 +99,12 @@
private MastershipService mastershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
+ private LeadershipService leadershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ private ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
private DeviceService deviceService;
private static final Logger log = getLogger(L2LbManager.class);
@@ -102,22 +113,33 @@
private ApplicationId appId;
private ConsistentMap<L2LbId, L2Lb> l2LbStore;
private ConsistentMap<L2LbId, Integer> l2LbNextStore;
+ // TODO Evaluate if ResourceService is a better option
+ private ConsistentMap<L2LbId, ApplicationId> l2LbResStore;
private Set<L2LbListener> listeners = Sets.newConcurrentHashSet();
private ExecutorService l2LbEventExecutor;
private ExecutorService l2LbProvExecutor;
+ private ExecutorService deviceEventExecutor;
+
private MapEventListener<L2LbId, L2Lb> l2LbStoreListener;
// TODO build CLI to view and clear the next store
private MapEventListener<L2LbId, Integer> l2LbNextStoreListener;
+ private MapEventListener<L2LbId, ApplicationId> l2LbResStoreListener;
+ private final DeviceListener deviceListener = new InternalDeviceListener();
@Activate
public void activate() {
appId = coreService.registerApplication(APP_NAME);
- l2LbEventExecutor = Executors.newSingleThreadExecutor(groupedThreads("l2lb-event", "%d", log));
- l2LbProvExecutor = Executors.newSingleThreadExecutor(groupedThreads("l2lb-prov", "%d", log));
+ l2LbEventExecutor = Executors.newSingleThreadExecutor(
+ groupedThreads("l2lb-event", "%d", log));
+ l2LbProvExecutor = Executors.newSingleThreadExecutor(
+ groupedThreads("l2lb-prov", "%d", log));
+ deviceEventExecutor = Executors.newSingleThreadScheduledExecutor(
+ groupedThreads("l2lb-dev-event", "%d", log));
l2LbStoreListener = new L2LbStoreListener();
l2LbNextStoreListener = new L2LbNextStoreListener();
+ l2LbResStoreListener = new L2LbResStoreListener();
KryoNamespace serializer = KryoNamespace.newBuilder()
.register(KryoNamespaces.API)
@@ -137,6 +159,14 @@
.withSerializer(Serializer.using(serializer))
.build();
l2LbNextStore.addListener(l2LbNextStoreListener);
+ l2LbResStore = storageService.<L2LbId, ApplicationId>consistentMapBuilder()
+ .withName("onos-l2lb-res-store")
+ .withRelaxedReadConsistency()
+ .withSerializer(Serializer.using(serializer))
+ .build();
+ l2LbResStore.addListener(l2LbResStoreListener);
+
+ deviceService.addListener(deviceListener);
log.info("Started");
}
@@ -147,6 +177,8 @@
l2LbNextStore.removeListener(l2LbNextStoreListener);
l2LbEventExecutor.shutdown();
+ l2LbProvExecutor.shutdown();
+ deviceEventExecutor.shutdown();
log.info("Stopped");
}
@@ -171,8 +203,15 @@
@Override
public L2Lb remove(DeviceId deviceId, int key) {
L2LbId l2LbId = new L2LbId(deviceId, key);
- log.debug("Removing {} from L2 load balancer store", l2LbId);
- return Versioned.valueOrNull(l2LbStore.remove(l2LbId));
+ ApplicationId reservation = Versioned.valueOrNull(l2LbResStore.get(l2LbId));
+ // Remove only if it is not used - otherwise it is necessary to release first
+ if (reservation == null) {
+ log.debug("Removing {} from L2 load balancer store", l2LbId);
+ return Versioned.valueOrNull(l2LbStore.remove(l2LbId));
+ }
+ log.warn("Removal {} from L2 load balancer store was not possible " +
+ "due to a previous reservation", l2LbId);
+ return null;
}
@Override
@@ -195,23 +234,62 @@
return Versioned.valueOrNull(l2LbNextStore.get(new L2LbId(deviceId, key)));
}
+ @Override
+ public boolean reserve(L2LbId l2LbId, ApplicationId appId) {
+ // Check if the resource is available
+ ApplicationId reservation = Versioned.valueOrNull(l2LbResStore.get(l2LbId));
+ L2Lb l2Lb = Versioned.valueOrNull(l2LbStore.get(l2LbId));
+ if (reservation == null && l2Lb != null) {
+ log.debug("Reserving {} -> {} into L2 load balancer reservation store", l2LbId, appId);
+ return l2LbResStore.put(l2LbId, appId) == null;
+ } else if (reservation != null && reservation.equals(appId)) {
+ // App try to reserve the resource a second time
+ log.debug("Already reserved {} -> {} skip reservation", l2LbId, appId);
+ return true;
+ }
+ log.warn("Reservation failed {} -> {}", l2LbId, appId);
+ return false;
+ }
+
+ @Override
+ public boolean release(L2LbId l2LbId, ApplicationId appId) {
+ // Check if the resource is reserved
+ ApplicationId reservation = Versioned.valueOrNull(l2LbResStore.get(l2LbId));
+ if (reservation != null && reservation.equals(appId)) {
+ log.debug("Removing {} -> {} from L2 load balancer reservation store", l2LbId, appId);
+ return l2LbResStore.remove(l2LbId) != null;
+ }
+ log.warn("Release failed {} -> {}", l2LbId, appId);
+ return false;
+ }
+
+ @Override
+ public ApplicationId getReservation(L2LbId l2LbId) {
+ return Versioned.valueOrNull(l2LbResStore.get(l2LbId));
+ }
+
+ @Override
+ public Map<L2LbId, ApplicationId> getReservations() {
+ return l2LbResStore.asJavaMap();
+ }
+
private class L2LbStoreListener implements MapEventListener<L2LbId, L2Lb> {
public void event(MapEvent<L2LbId, L2Lb> event) {
switch (event.type()) {
case INSERT:
log.debug("L2Lb {} insert new={}, old={}", event.key(), event.newValue(), event.oldValue());
- post(new L2LbEvent(L2LbEvent.Type.ADDED, event.newValue().value(), null));
+ post(new L2LbEvent(L2LbEvent.Type.ADDED, event.newValue().value().data(), null));
populateL2Lb(event.newValue().value());
break;
case REMOVE:
log.debug("L2Lb {} remove new={}, old={}", event.key(), event.newValue(), event.oldValue());
- post(new L2LbEvent(L2LbEvent.Type.REMOVED, null, event.oldValue().value()));
+ post(new L2LbEvent(L2LbEvent.Type.REMOVED, null, event.oldValue().value().data()));
revokeL2Lb(event.oldValue().value());
break;
case UPDATE:
log.debug("L2Lb {} update new={}, old={}", event.key(), event.newValue(), event.oldValue());
- post(new L2LbEvent(L2LbEvent.Type.UPDATED, event.newValue().value(),
- event.oldValue().value()));
+ post(new L2LbEvent(L2LbEvent.Type.UPDATED, event.newValue().value().data(),
+ event.oldValue().value().data()));
updateL2Lb(event.newValue().value(), event.oldValue().value());
break;
default:
@@ -238,67 +316,159 @@
}
}
+ private class L2LbResStoreListener implements MapEventListener<L2LbId, ApplicationId> {
+ public void event(MapEvent<L2LbId, ApplicationId> event) {
+ switch (event.type()) {
+ case INSERT:
+ log.debug("L2Lb reservation {} insert new={}, old={}", event.key(), event.newValue(),
+ event.oldValue());
+ break;
+ case REMOVE:
+ log.debug("L2Lb reservation {} remove new={}, old={}", event.key(), event.newValue(),
+ event.oldValue());
+ break;
+ case UPDATE:
+ log.debug("L2Lb reservation {} update new={}, old={}", event.key(), event.newValue(),
+ event.oldValue());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private class InternalDeviceListener implements DeviceListener {
+ // We want to manage only a subset of events and if we are the leader
+ @Override
+ public void event(DeviceEvent event) {
+ deviceEventExecutor.execute(() -> {
+ DeviceId deviceId = event.subject().id();
+ if (!isLocalLeader(deviceId)) {
+ log.debug("Not the leader of {}. Skip event {}", deviceId, event);
+ return;
+ }
+ // Populate or revoke according to the device availability
+ if (deviceService.isAvailable(deviceId)) {
+ init(deviceId);
+ } else {
+ cleanup(deviceId);
+ }
+ });
+ }
+ // Some events related to the devices are skipped
+ @Override
+ public boolean isRelevant(DeviceEvent event) {
+ return event.type() == DeviceEvent.Type.DEVICE_ADDED ||
+ event.type() == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED ||
+ event.type() == DeviceEvent.Type.DEVICE_UPDATED;
+ }
+ }
+
private void post(L2LbEvent l2LbEvent) {
l2LbEventExecutor.execute(() -> {
for (L2LbListener l : listeners) {
- l.event(l2LbEvent);
+ if (l.isRelevant(l2LbEvent)) {
+ l.event(l2LbEvent);
+ }
}
});
}
- // TODO repopulate when device reconnect
+ private void init(DeviceId deviceId) {
+ l2LbStore.entrySet().stream()
+ .filter(l2lbentry -> l2lbentry.getKey().deviceId().equals(deviceId))
+ .forEach(l2lbentry -> populateL2Lb(l2lbentry.getValue().value()));
+ }
+
+ private void cleanup(DeviceId deviceId) {
+ l2LbStore.entrySet().stream()
+ .filter(entry -> entry.getKey().deviceId().equals(deviceId))
+ .forEach(entry -> l2LbNextStore.remove(entry.getKey()));
+ log.debug("{} is removed from l2LbNextObjStore", deviceId);
+ }
+
private void populateL2Lb(L2Lb l2Lb) {
DeviceId deviceId = l2Lb.l2LbId().deviceId();
- if (!mastershipService.isLocalMaster(deviceId)) {
- log.debug("Not the master of {}. Skip populateL2Lb {}", deviceId, l2Lb.l2LbId());
+ if (!isLocalLeader(deviceId)) {
+ log.debug("Not the leader of {}. Skip populateL2Lb {}", deviceId, l2Lb.l2LbId());
return;
}
l2LbProvExecutor.execute(() -> {
- L2LbObjectiveContext context = new L2LbObjectiveContext(l2Lb.l2LbId());
- NextObjective nextObj = nextObjBuilder(l2Lb.l2LbId(), l2Lb.ports()).add(context);
-
- flowObjService.next(deviceId, nextObj);
- l2LbNextStore.put(l2Lb.l2LbId(), nextObj.id());
+ Integer nextid = Versioned.valueOrNull(l2LbNextStore.get(l2Lb.l2LbId()));
+ if (nextid == null) {
+ // Build a new context and new next objective
+ L2LbObjectiveContext context = new L2LbObjectiveContext(l2Lb.l2LbId());
+ NextObjective nextObj = nextObjBuilder(l2Lb.l2LbId(), l2Lb.ports(), nextid).add(context);
+ // Finally submit, store, and register the resource
+ flowObjService.next(deviceId, nextObj);
+ l2LbNextStore.put(l2Lb.l2LbId(), nextObj.id());
+ } else {
+ log.info("NextObj for {} already exists. Skip populateL2Lb", l2Lb.l2LbId());
+ }
});
}
private void revokeL2Lb(L2Lb l2Lb) {
DeviceId deviceId = l2Lb.l2LbId().deviceId();
- if (!mastershipService.isLocalMaster(deviceId)) {
- log.debug("Not the master of {}. Skip revokeL2Lb {}", deviceId, l2Lb.l2LbId());
+ if (!isLocalLeader(deviceId)) {
+ log.debug("Not the leader of {}. Skip revokeL2Lb {}", deviceId, l2Lb.l2LbId());
return;
}
l2LbProvExecutor.execute(() -> {
- l2LbNextStore.remove(l2Lb.l2LbId());
- // NOTE group is not removed and we rely on the garbage collection mechanism
+ Integer nextid = Versioned.valueOrNull(l2LbNextStore.get(l2Lb.l2LbId()));
+ if (nextid != null) {
+ // Build a new context and remove old next objective
+ L2LbObjectiveContext context = new L2LbObjectiveContext(l2Lb.l2LbId());
+ NextObjective nextObj = nextObjBuilder(l2Lb.l2LbId(), l2Lb.ports(), nextid).remove(context);
+ // Finally submit and invalidate the store
+ flowObjService.next(deviceId, nextObj);
+ l2LbNextStore.remove(l2Lb.l2LbId());
+ } else {
+ log.info("NextObj for {} does not exist. Skip revokeL2Lb", l2Lb.l2LbId());
+ }
});
}
private void updateL2Lb(L2Lb newL2Lb, L2Lb oldL2Lb) {
DeviceId deviceId = newL2Lb.l2LbId().deviceId();
- if (!mastershipService.isLocalMaster(deviceId)) {
- log.debug("Not the master of {}. Skip updateL2Lb {}", deviceId, newL2Lb.l2LbId());
+ if (!isLocalLeader(deviceId)) {
+ log.debug("Not the leader of {}. Skip updateL2Lb {}", deviceId, newL2Lb.l2LbId());
return;
}
l2LbProvExecutor.execute(() -> {
- L2LbObjectiveContext context = new L2LbObjectiveContext(newL2Lb.l2LbId());
- Set<PortNumber> portsToBeAdded = Sets.difference(newL2Lb.ports(), oldL2Lb.ports());
- Set<PortNumber> portsToBeRemoved = Sets.difference(oldL2Lb.ports(), newL2Lb.ports());
+ Integer nextid = Versioned.valueOrNull(l2LbNextStore.get(newL2Lb.l2LbId()));
+ if (nextid != null) {
+ // Compute modifications and context
+ L2LbObjectiveContext context = new L2LbObjectiveContext(newL2Lb.l2LbId());
+ Set<PortNumber> portsToBeAdded = Sets.difference(newL2Lb.ports(), oldL2Lb.ports());
+ Set<PortNumber> portsToBeRemoved = Sets.difference(oldL2Lb.ports(), newL2Lb.ports());
+ // and send them to the flowobj subsystem
+ if (!portsToBeAdded.isEmpty()) {
+ flowObjService.next(deviceId, nextObjBuilder(newL2Lb.l2LbId(), portsToBeAdded, nextid)
+ .addToExisting(context));
+ } else {
+ log.debug("NextObj for {} nothing to add", newL2Lb.l2LbId());
- flowObjService.next(deviceId, nextObjBuilder(newL2Lb.l2LbId(), portsToBeAdded).addToExisting(context));
- flowObjService.next(deviceId, nextObjBuilder(newL2Lb.l2LbId(), portsToBeRemoved)
- .removeFromExisting(context));
+ }
+ if (!portsToBeRemoved.isEmpty()) {
+ flowObjService.next(deviceId, nextObjBuilder(newL2Lb.l2LbId(), portsToBeRemoved, nextid)
+ .removeFromExisting(context));
+ } else {
+ log.debug("NextObj for {} nothing to remove", newL2Lb.l2LbId());
+ }
+ } else {
+ log.info("NextObj for {} does not exist. Skip updateL2Lb", newL2Lb.l2LbId());
+ }
});
}
- private NextObjective.Builder nextObjBuilder(L2LbId l2LbId, Set<PortNumber> ports) {
- return nextObjBuilder(l2LbId, ports, flowObjService.allocateNextId());
- }
-
- private NextObjective.Builder nextObjBuilder(L2LbId l2LbId, Set<PortNumber> ports, int nextId) {
+ private NextObjective.Builder nextObjBuilder(L2LbId l2LbId, Set<PortNumber> ports, Integer nextId) {
+ if (nextId == null) {
+ nextId = flowObjService.allocateNextId();
+ }
// TODO replace logical l2lb port
TrafficSelector meta = DefaultTrafficSelector.builder()
.matchInPort(PortNumber.portNumber(l2LbId.key())).build();
@@ -314,6 +484,22 @@
return nextObjBuilder;
}
+ // Custom-built function, when the device is not available we need a fallback mechanism
+ private boolean isLocalLeader(DeviceId deviceId) {
+ if (!mastershipService.isLocalMaster(deviceId)) {
+ // When the device is available we just check the mastership
+ if (deviceService.isAvailable(deviceId)) {
+ return false;
+ }
+ // Fallback with Leadership service - device id is used as topic
+ NodeId leader = leadershipService.runForLeadership(
+ deviceId.toString()).leaderNodeId();
+ // Verify if this node is the leader
+ return clusterService.getLocalNode().id().equals(leader);
+ }
+ return true;
+ }
+
private final class L2LbObjectiveContext implements ObjectiveContext {
private final L2LbId l2LbId;
@@ -324,13 +510,51 @@
@Override
public void onSuccess(Objective objective) {
NextObjective nextObj = (NextObjective) objective;
- log.debug("Added nextobj {} for L2 load balancer {}", nextObj, l2LbId);
+ log.debug("Success {} nextobj {} for L2 load balancer {}", nextObj.op(), nextObj, l2LbId);
+ // Operation done
+ L2LbData oldl2LbData = new L2LbData(l2LbId);
+ L2LbData newl2LbData = new L2LbData(l2LbId);
+ l2LbProvExecutor.execute(() -> {
+ // Other operations will not lead to a generation of an event
+ switch (nextObj.op()) {
+ case ADD:
+ newl2LbData.setNextId(nextObj.id());
+ post(new L2LbEvent(L2LbEvent.Type.INSTALLED, newl2LbData, oldl2LbData));
+ break;
+ case REMOVE:
+ oldl2LbData.setNextId(nextObj.id());
+ post(new L2LbEvent(L2LbEvent.Type.UNINSTALLED, newl2LbData, oldl2LbData));
+ break;
+ default:
+ break;
+ }
+ });
}
@Override
public void onError(Objective objective, ObjectiveError error) {
NextObjective nextObj = (NextObjective) objective;
- log.debug("Failed to add nextobj {} for L2 load balancer {}", nextObj, l2LbId);
+ log.debug("Failed {} nextobj {} for L2 load balancer {} due to {}", nextObj.op(), nextObj,
+ l2LbId, error);
+ l2LbProvExecutor.execute(() -> {
+ // Init the data structure
+ L2LbData l2LbData = new L2LbData(l2LbId);
+ // Update the next id and send the event;
+ switch (nextObj.op()) {
+ case ADD:
+ // If ADD is failing apps do not know the next id; let's update the store
+ l2LbNextStore.remove(l2LbId);
+ post(new L2LbEvent(L2LbEvent.Type.FAILED, l2LbData, l2LbData));
+ break;
+ case REMOVE:
+ // If REMOVE is failing let's send also the info about the next id; no need to update the store
+ l2LbData.setNextId(nextObj.id());
+ post(new L2LbEvent(L2LbEvent.Type.FAILED, l2LbData, l2LbData));
+ break;
+ default:
+ break;
+ }
+ });
}
}
diff --git a/apps/l2lb/src/main/java/org/onosproject/l2lb/cli/L2LbAddCommand.java b/apps/l2lb/src/main/java/org/onosproject/l2lb/cli/L2LbAddCommand.java
index 4afa1fe..f3a593c 100644
--- a/apps/l2lb/src/main/java/org/onosproject/l2lb/cli/L2LbAddCommand.java
+++ b/apps/l2lb/src/main/java/org/onosproject/l2lb/cli/L2LbAddCommand.java
@@ -20,7 +20,9 @@
import org.apache.karaf.shell.api.action.Command;
import org.apache.karaf.shell.api.action.lifecycle.Service;
import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.l2lb.api.L2Lb;
import org.onosproject.l2lb.api.L2LbAdminService;
+import org.onosproject.l2lb.api.L2LbId;
import org.onosproject.l2lb.api.L2LbMode;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
@@ -54,6 +56,10 @@
required = true, multiValued = true)
private String[] portsStr;
+ // Operation constants
+ private static final String CREATE = "Create";
+ private static final String UPDATE = "Update";
+
@Override
protected void doExecute() {
DeviceId deviceId = DeviceId.deviceId(deviceIdStr);
@@ -64,7 +70,8 @@
.map(PortNumber::fromString).collect(Collectors.toSet());
L2LbAdminService l2LbAdminService = get(L2LbAdminService.class);
- l2LbAdminService.createOrUpdate(deviceId, l2LbPort, ports, mode);
+ L2Lb l2Lb = l2LbAdminService.createOrUpdate(deviceId, l2LbPort, ports, mode);
+ print("%s of %s executed", l2Lb == null ? CREATE : UPDATE, new L2LbId(deviceId, l2LbPort));
}
}
diff --git a/apps/l2lb/src/main/java/org/onosproject/l2lb/cli/L2LbListCommand.java b/apps/l2lb/src/main/java/org/onosproject/l2lb/cli/L2LbListCommand.java
index e1504f5..d14a2d0 100644
--- a/apps/l2lb/src/main/java/org/onosproject/l2lb/cli/L2LbListCommand.java
+++ b/apps/l2lb/src/main/java/org/onosproject/l2lb/cli/L2LbListCommand.java
@@ -18,6 +18,7 @@
import org.apache.karaf.shell.api.action.Command;
import org.apache.karaf.shell.api.action.lifecycle.Service;
import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.core.ApplicationId;
import org.onosproject.l2lb.api.L2Lb;
import org.onosproject.l2lb.api.L2LbId;
import org.onosproject.l2lb.api.L2LbService;
@@ -30,11 +31,19 @@
@Service
@Command(scope = "onos", name = "l2lbs", description = "Lists L2 load balancers")
public class L2LbListCommand extends AbstractShellCommand {
+
+ // Operation constant
+ private static final String AVAILABLE = "Available";
+
@Override
public void doExecute() {
L2LbService service = get(L2LbService.class);
+ // Get l2 load balancers and reservations
Map<L2LbId, L2Lb> l2LbStore = service.getL2Lbs();
-
- l2LbStore.forEach((l2LbId, l2Lb) -> print("%s -> %s, %s", l2LbId, l2Lb.ports(), l2Lb.mode()));
+ Map<L2LbId, ApplicationId> l2LbResStore = service.getReservations();
+ // Print id -> ports, mode, reservation
+ l2LbStore.forEach((l2LbId, l2Lb) -> print("%s -> %s, %s, %s", l2LbId, l2Lb.ports(), l2Lb.mode(),
+ l2LbResStore.get(l2LbId) == null ?
+ AVAILABLE : l2LbResStore.get(l2LbId).name()));
}
}
\ No newline at end of file
diff --git a/apps/l2lb/src/main/java/org/onosproject/l2lb/cli/L2LbRemoveCommand.java b/apps/l2lb/src/main/java/org/onosproject/l2lb/cli/L2LbRemoveCommand.java
index 89c4bce..1f7b5c5 100644
--- a/apps/l2lb/src/main/java/org/onosproject/l2lb/cli/L2LbRemoveCommand.java
+++ b/apps/l2lb/src/main/java/org/onosproject/l2lb/cli/L2LbRemoveCommand.java
@@ -19,7 +19,9 @@
import org.apache.karaf.shell.api.action.Command;
import org.apache.karaf.shell.api.action.lifecycle.Service;
import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.l2lb.api.L2Lb;
import org.onosproject.l2lb.api.L2LbAdminService;
+import org.onosproject.l2lb.api.L2LbId;
import org.onosproject.net.DeviceId;
/**
@@ -38,12 +40,17 @@
required = true, multiValued = false)
private String keyStr;
+ // Operation constants
+ private static final String EXECUTED = "Executed";
+ private static final String FAILED = "Failed";
+
@Override
protected void doExecute() {
DeviceId deviceId = DeviceId.deviceId(deviceIdStr);
int l2LbPort = Integer.parseInt(keyStr);
L2LbAdminService l2LbAdminService = get(L2LbAdminService.class);
- l2LbAdminService.remove(deviceId, l2LbPort);
+ L2Lb l2Lb = l2LbAdminService.remove(deviceId, l2LbPort);
+ print("Removal of %s %s", new L2LbId(deviceId, l2LbPort), l2Lb != null ? EXECUTED : FAILED);
}
}