[ONOS-7731] Add implementation of openstack vtap store and criterion
Change-Id: I7f41652f127038af9d3f79b34d427d28ce162d50
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtap.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtap.java
new file mode 100644
index 0000000..7e51be5
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtap.java
@@ -0,0 +1,199 @@
+/*
+ * 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.openstackvtap.impl;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.onosproject.net.AbstractDescription;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.openstackvtap.api.OpenstackVtap;
+import org.onosproject.openstackvtap.api.OpenstackVtapCriterion;
+import org.onosproject.openstackvtap.api.OpenstackVtapId;
+
+import java.util.Set;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Default implementation of an immutable openstack vTap.
+ */
+public final class DefaultOpenstackVtap extends AbstractDescription implements OpenstackVtap {
+
+ private final OpenstackVtapId id;
+ private final Type type;
+ private final OpenstackVtapCriterion vTapCriterion;
+ private final Set<DeviceId> txDeviceIds;
+ private final Set<DeviceId> rxDeviceIds;
+
+ // private constructor not intended to use from external
+ private DefaultOpenstackVtap(OpenstackVtapId id, Type type,
+ OpenstackVtapCriterion vTapCriterion,
+ Set<DeviceId> txDeviceIds, Set<DeviceId> rxDeviceIds,
+ SparseAnnotations... annotations) {
+ super(annotations);
+ this.id = id;
+ this.type = type;
+ this.vTapCriterion = vTapCriterion;
+ this.txDeviceIds = txDeviceIds;
+ this.rxDeviceIds = rxDeviceIds;
+ }
+
+ @Override
+ public OpenstackVtapId id() {
+ return id;
+ }
+
+ @Override
+ public Type type() {
+ return type;
+ }
+
+ @Override
+ public OpenstackVtapCriterion vTapCriterion() {
+ return vTapCriterion;
+ }
+
+ @Override
+ public Set<DeviceId> txDeviceIds() {
+ return ImmutableSet.copyOf(txDeviceIds);
+ }
+
+ @Override
+ public Set<DeviceId> rxDeviceIds() {
+ return ImmutableSet.copyOf(rxDeviceIds);
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("id", id)
+ .add("type", type)
+ .add("vTapCriterion", vTapCriterion)
+ .add("txDeviceIds", txDeviceIds)
+ .add("rxDeviceIds", rxDeviceIds)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(super.hashCode(),
+ id,
+ type,
+ vTapCriterion,
+ txDeviceIds,
+ rxDeviceIds);
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object != null && getClass() == object.getClass()) {
+ if (!super.equals(object)) {
+ return false;
+ }
+ DefaultOpenstackVtap that = (DefaultOpenstackVtap) object;
+ return Objects.equal(this.id, that.id)
+ && Objects.equal(this.type, that.type)
+ && Objects.equal(this.vTapCriterion, that.vTapCriterion)
+ && Objects.equal(this.txDeviceIds, that.txDeviceIds)
+ && Objects.equal(this.rxDeviceIds, that.rxDeviceIds);
+ }
+ return false;
+ }
+
+ /**
+ * Creates a new default openstack vTap builder.
+ *
+ * @return default openstack vTap builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * Builder for DefaultOpenstackVtap object.
+ */
+ public static class Builder implements OpenstackVtap.Builder {
+ private static final SparseAnnotations EMPTY = DefaultAnnotations.builder().build();
+
+ private OpenstackVtapId id;
+ private Type type;
+ private OpenstackVtapCriterion vTapCriterion;
+ private Set<DeviceId> txDeviceIds;
+ private Set<DeviceId> rxDeviceIds;
+ private SparseAnnotations annotations = EMPTY;
+
+ // private constructor not intended to use from external
+ Builder() {
+ }
+
+ @Override
+ public Builder id(OpenstackVtapId id) {
+ this.id = id;
+ return this;
+ }
+
+ @Override
+ public Builder type(Type type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public Builder vTapCriterion(OpenstackVtapCriterion vTapCriterion) {
+ this.vTapCriterion = vTapCriterion;
+ return this;
+ }
+
+ @Override
+ public Builder txDeviceIds(Set<DeviceId> txDeviceIds) {
+ if (txDeviceIds != null) {
+ this.txDeviceIds = ImmutableSet.copyOf(txDeviceIds);
+ } else {
+ this.txDeviceIds = Sets.newHashSet();
+ }
+ return this;
+ }
+
+ @Override
+ public Builder rxDeviceIds(Set<DeviceId> rxDeviceIds) {
+ if (rxDeviceIds != null) {
+ this.rxDeviceIds = ImmutableSet.copyOf(rxDeviceIds);
+ } else {
+ this.rxDeviceIds = Sets.newHashSet();
+ }
+ return this;
+ }
+
+ @Override
+ public Builder annotations(SparseAnnotations... annotations) {
+ checkArgument(annotations.length <= 1,
+ "Only one set of annotations is expected");
+ this.annotations = annotations.length == 1 ? annotations[0] : EMPTY;
+ return this;
+ }
+
+ @Override
+ public DefaultOpenstackVtap build() {
+ return new DefaultOpenstackVtap(id, type, vTapCriterion,
+ txDeviceIds, rxDeviceIds, annotations);
+ }
+ }
+
+}
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapCriterion.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapCriterion.java
new file mode 100644
index 0000000..ef8f08f
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapCriterion.java
@@ -0,0 +1,176 @@
+/*
+ * 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.openstackvtap.impl;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Objects;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.TpPort;
+import org.onosproject.openstackvtap.api.OpenstackVtapCriterion;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Default implementation of an immutable openstack vTap criterion.
+ */
+public final class DefaultOpenstackVtapCriterion implements OpenstackVtapCriterion {
+ private final IpPrefix srcIpPrefix;
+ private final IpPrefix dstIpPrefix;
+ private final byte ipProtocol;
+ private final TpPort srcTpPort;
+ private final TpPort dstTpPort;
+
+ private static final String NOT_NULL_MSG = "Element % cannot be null";
+
+ // private constructor not intended to use from external
+ private DefaultOpenstackVtapCriterion(IpPrefix srcIpPrefix,
+ IpPrefix dstIpPrefix,
+ byte ipProtocol,
+ TpPort srcTpPort,
+ TpPort dstTpPort) {
+ this.srcIpPrefix = srcIpPrefix;
+ this.dstIpPrefix = dstIpPrefix;
+ this.ipProtocol = ipProtocol;
+ this.srcTpPort = srcTpPort;
+ this.dstTpPort = dstTpPort;
+ }
+
+ @Override
+ public IpPrefix srcIpPrefix() {
+ return srcIpPrefix;
+ }
+
+ @Override
+ public IpPrefix dstIpPrefix() {
+ return dstIpPrefix;
+ }
+
+ @Override
+ public byte ipProtocol() {
+ return ipProtocol;
+ }
+
+ @Override
+ public TpPort srcTpPort() {
+ return srcTpPort;
+ }
+
+ @Override
+ public TpPort dstTpPort() {
+ return dstTpPort;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("srcIpPrefix", srcIpPrefix)
+ .add("dstIpPrefix", dstIpPrefix)
+ .add("ipProtocol", ipProtocol)
+ .add("srcTpPort", srcTpPort)
+ .add("dstTpPort", dstTpPort)
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(super.hashCode(),
+ srcIpPrefix,
+ dstIpPrefix,
+ ipProtocol,
+ srcTpPort,
+ dstTpPort);
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object != null && getClass() == object.getClass()) {
+ if (!super.equals(object)) {
+ return false;
+ }
+
+ DefaultOpenstackVtapCriterion that = (DefaultOpenstackVtapCriterion) object;
+ return Objects.equal(this.srcIpPrefix, that.srcIpPrefix) &&
+ Objects.equal(this.dstIpPrefix, that.dstIpPrefix) &&
+ Objects.equal(this.ipProtocol, that.ipProtocol) &&
+ Objects.equal(this.srcTpPort, that.srcTpPort) &&
+ Objects.equal(this.dstTpPort, that.dstTpPort);
+ }
+ return false;
+ }
+
+ /**
+ * Creates a new default openstack vTap criterion builder.
+ *
+ * @return default openstack vTap criterion builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * A builder class for openstack vTap criterion builder.
+ */
+ public static final class Builder implements OpenstackVtapCriterion.Builder {
+ private IpPrefix srcIpPrefix;
+ private IpPrefix dstIpPrefix;
+ private byte ipProtocol;
+ private TpPort srcTpPort;
+ private TpPort dstTpPort;
+
+ // private constructor not intended to use from external
+ Builder() {
+ }
+
+ @Override
+ public DefaultOpenstackVtapCriterion build() {
+ checkArgument(srcIpPrefix != null, NOT_NULL_MSG, "Source IP Prefix");
+ checkArgument(dstIpPrefix != null, NOT_NULL_MSG, "Destination IP Prefix");
+
+ return new DefaultOpenstackVtapCriterion(srcIpPrefix, dstIpPrefix,
+ ipProtocol, srcTpPort, dstTpPort);
+ }
+
+ @Override
+ public Builder srcIpPrefix(IpPrefix srcIpPrefix) {
+ this.srcIpPrefix = srcIpPrefix;
+ return this;
+ }
+
+ @Override
+ public Builder dstIpPrefix(IpPrefix dstIpPrefix) {
+ this.dstIpPrefix = dstIpPrefix;
+ return this;
+ }
+
+ @Override
+ public Builder ipProtocol(byte ipProtocol) {
+ this.ipProtocol = ipProtocol;
+ return this;
+ }
+
+ @Override
+ public Builder srcTpPort(TpPort srcTpPort) {
+ this.srcTpPort = srcTpPort;
+ return this;
+ }
+
+ @Override
+ public Builder dstTpPort(TpPort dstTpPort) {
+ this.dstTpPort = dstTpPort;
+ return this;
+ }
+ }
+}
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DistributedOpenstackVtapStore.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DistributedOpenstackVtapStore.java
new file mode 100644
index 0000000..d6f63eb
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DistributedOpenstackVtapStore.java
@@ -0,0 +1,570 @@
+/*
+ * 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.openstackvtap.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.openstackvtap.api.OpenstackVtap;
+import org.onosproject.openstackvtap.api.OpenstackVtapCriterion;
+import org.onosproject.openstackvtap.api.OpenstackVtapEvent;
+import org.onosproject.openstackvtap.api.OpenstackVtapId;
+import org.onosproject.openstackvtap.api.OpenstackVtapStore;
+import org.onosproject.openstackvtap.api.OpenstackVtapStoreDelegate;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.DistributedPrimitive.Status;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.slf4j.Logger;
+
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.DefaultAnnotations.merge;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Manages the inventory of users using a {@code ConsistentMap}.
+ */
+@Component(immediate = true)
+@Service
+public class DistributedOpenstackVtapStore
+ extends AbstractStore<OpenstackVtapEvent, OpenstackVtapStoreDelegate>
+ implements OpenstackVtapStore {
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ private ConsistentMap<OpenstackVtapId, DefaultOpenstackVtap> vTapConsistentMap;
+ private MapEventListener<OpenstackVtapId, DefaultOpenstackVtap>
+ vTapListener = new VTapEventListener();
+ private Map<OpenstackVtapId, DefaultOpenstackVtap> vTapMap;
+
+ private static final Serializer SERIALIZER = Serializer
+ .using(new KryoNamespace.Builder().register(KryoNamespaces.API)
+ .register(OpenstackVtapId.class)
+ .register(UUID.class)
+ .register(DefaultOpenstackVtap.class)
+ .register(OpenstackVtap.Type.class)
+ .register(DefaultOpenstackVtapCriterion.class)
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
+ .build());
+
+ private Map<DeviceId, Set<OpenstackVtapId>>
+ vTapIdsByTxDeviceId = Maps.newConcurrentMap();
+ private Map<DeviceId, Set<OpenstackVtapId>>
+ vTapIdsByRxDeviceId = Maps.newConcurrentMap();
+
+ private ScheduledExecutorService eventExecutor;
+
+ private Consumer<Status> vTapStatusListener;
+
+ public static final String INVALID_DESCRIPTION = "Invalid create/update parameter";
+
+ @Activate
+ public void activate() {
+ eventExecutor = newSingleThreadScheduledExecutor(
+ groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+
+ vTapConsistentMap = storageService.<OpenstackVtapId, DefaultOpenstackVtap>
+ consistentMapBuilder()
+ .withName("vTapMap")
+ .withSerializer(SERIALIZER)
+ .build();
+
+ vTapMap = vTapConsistentMap.asJavaMap();
+ vTapConsistentMap.addListener(vTapListener);
+
+ vTapStatusListener = status -> {
+ if (status == Status.ACTIVE) {
+ eventExecutor.execute(this::loadVTapIds);
+ }
+ };
+ vTapConsistentMap.addStatusChangeListener(vTapStatusListener);
+
+ log.info("Started {} - {}", this.getClass().getSimpleName());
+ }
+
+ @Deactivate
+ public void deactivate() {
+ vTapConsistentMap.removeStatusChangeListener(vTapStatusListener);
+ vTapConsistentMap.removeListener(vTapListener);
+ eventExecutor.shutdown();
+
+ log.info("Stopped {} - {}", this.getClass().getSimpleName());
+ }
+
+ private void loadVTapIds() {
+ vTapIdsByTxDeviceId.clear();
+ vTapIdsByRxDeviceId.clear();
+ vTapMap.values().forEach(vTap -> refreshDeviceIdsByVtap(null, vTap));
+ }
+
+ private boolean shouldUpdate(DefaultOpenstackVtap existing,
+ OpenstackVtap description,
+ boolean replaceDevices) {
+ if (existing == null) {
+ return true;
+ }
+
+ if ((description.type() != null && !description.type().equals(existing.type()))
+ || (description.vTapCriterion() != null &&
+ !description.vTapCriterion().equals(existing.vTapCriterion()))) {
+ return true;
+ }
+
+ if (description.txDeviceIds() != null) {
+ if (replaceDevices) {
+ if (!existing.txDeviceIds().equals(description.txDeviceIds())) {
+ return true;
+ }
+ } else {
+ if (!existing.txDeviceIds().containsAll(description.txDeviceIds())) {
+ return true;
+ }
+ }
+ }
+
+ if (description.rxDeviceIds() != null) {
+ if (replaceDevices) {
+ if (!existing.rxDeviceIds().equals(description.rxDeviceIds())) {
+ return true;
+ }
+ } else {
+ if (!existing.rxDeviceIds().containsAll(description.rxDeviceIds())) {
+ return true;
+ }
+ }
+ }
+
+ // check to see if any of the annotations provided by vTap
+ // differ from those in the existing vTap
+ return description.annotations().keys().stream()
+ .anyMatch(k -> !Objects.equals(description.annotations().value(k),
+ existing.annotations().value(k)));
+ }
+
+ @Override
+ public OpenstackVtap createOrUpdateVtap(OpenstackVtapId vTapId,
+ OpenstackVtap description,
+ boolean replaceFlag) {
+
+ return vTapMap.compute(vTapId, (id, existing) -> {
+ if (existing == null &&
+ (description.type() == null ||
+ description.vTapCriterion() == null ||
+ description.txDeviceIds() == null ||
+ description.rxDeviceIds() == null)) {
+ checkState(false, INVALID_DESCRIPTION);
+ return null;
+ }
+
+ if (shouldUpdate(existing, description, replaceFlag)) {
+ // Replace items
+ OpenstackVtap.Type type =
+ (description.type() == null ? existing.type() : description.type());
+ OpenstackVtapCriterion vTapCriterion =
+ (description.vTapCriterion() == null ?
+ existing.vTapCriterion() : description.vTapCriterion());
+
+ // Replace or add devices
+ Set<DeviceId> txDeviceIds;
+ if (description.txDeviceIds() == null) {
+ txDeviceIds = existing.txDeviceIds();
+ } else {
+ if (existing == null || replaceFlag) {
+ txDeviceIds = ImmutableSet.copyOf(description.txDeviceIds());
+ } else {
+ txDeviceIds = Sets.newHashSet(existing.txDeviceIds());
+ txDeviceIds.addAll(description.txDeviceIds());
+ }
+ }
+
+ Set<DeviceId> rxDeviceIds;
+ if (description.rxDeviceIds() == null) {
+ rxDeviceIds = existing.rxDeviceIds();
+ } else {
+ if (existing == null || replaceFlag) {
+ rxDeviceIds = ImmutableSet.copyOf(description.rxDeviceIds());
+ } else {
+ rxDeviceIds = Sets.newHashSet(existing.rxDeviceIds());
+ rxDeviceIds.addAll(description.rxDeviceIds());
+ }
+ }
+
+ // Replace or add annotations
+ SparseAnnotations annotations;
+ if (existing != null) {
+ annotations = merge((DefaultAnnotations) existing.annotations(),
+ (SparseAnnotations) description.annotations());
+ } else {
+ annotations = (SparseAnnotations) description.annotations();
+ }
+
+ // Make new changed vTap and return
+ return DefaultOpenstackVtap.builder()
+ .id(vTapId)
+ .type(type)
+ .vTapCriterion(vTapCriterion)
+ .txDeviceIds(txDeviceIds)
+ .rxDeviceIds(rxDeviceIds)
+ .annotations(annotations)
+ .build();
+ }
+ return existing;
+ });
+ }
+
+ @Override
+ public OpenstackVtap removeVtapById(OpenstackVtapId vTapId) {
+ return vTapMap.remove(vTapId);
+ }
+
+ @Override
+ public boolean addDeviceToVtap(OpenstackVtapId vTapId,
+ OpenstackVtap.Type type,
+ DeviceId deviceId) {
+ checkNotNull(vTapId);
+ checkNotNull(deviceId);
+
+ OpenstackVtap vTap = vTapMap.compute(vTapId, (id, existing) -> {
+ if (existing == null) {
+ return null;
+ }
+ if (!existing.type().isValid(type)) {
+ log.error("Not valid OpenstackVtap type {} for requested type {}",
+ existing.type(), type);
+ return existing;
+ }
+
+ Set<DeviceId> txDeviceIds = null;
+ if (type.isValid(OpenstackVtap.Type.VTAP_TX) &&
+ !existing.txDeviceIds().contains(deviceId)) {
+ txDeviceIds = Sets.newHashSet(existing.txDeviceIds());
+ txDeviceIds.add(deviceId);
+ }
+
+ Set<DeviceId> rxDeviceIds = null;
+ if (type.isValid(OpenstackVtap.Type.VTAP_RX) &&
+ !existing.rxDeviceIds().contains(deviceId)) {
+ rxDeviceIds = Sets.newHashSet(existing.rxDeviceIds());
+ rxDeviceIds.add(deviceId);
+ }
+
+ if (txDeviceIds != null || rxDeviceIds != null) {
+ //updateVTapIdFromDeviceId(existing.id(), deviceId); // execute from event listener
+
+ return DefaultOpenstackVtap.builder()
+ .id(vTapId)
+ .type(existing.type())
+ .vTapCriterion(existing.vTapCriterion())
+ .txDeviceIds(txDeviceIds != null ? txDeviceIds : existing.txDeviceIds())
+ .rxDeviceIds(rxDeviceIds != null ? rxDeviceIds : existing.rxDeviceIds())
+ .annotations(existing.annotations())
+ .build();
+ }
+ return existing;
+ });
+ return (vTap != null);
+ }
+
+ @Override
+ public boolean removeDeviceFromVtap(OpenstackVtapId vTapId,
+ OpenstackVtap.Type type,
+ DeviceId deviceId) {
+ checkNotNull(vTapId);
+ checkNotNull(deviceId);
+
+ OpenstackVtap vTap = vTapMap.compute(vTapId, (id, existing) -> {
+ if (existing == null) {
+ return null;
+ }
+ if (!existing.type().isValid(type)) {
+ log.error("Not valid OpenstackVtap type {} for requested type {}",
+ existing.type(), type);
+ return existing;
+ }
+
+ Set<DeviceId> txDeviceIds = null;
+ if (type.isValid(OpenstackVtap.Type.VTAP_TX) &&
+ existing.txDeviceIds().contains(deviceId)) {
+ txDeviceIds = Sets.newHashSet(existing.txDeviceIds());
+ txDeviceIds.remove(deviceId);
+ }
+
+ Set<DeviceId> rxDeviceIds = null;
+ if (type.isValid(OpenstackVtap.Type.VTAP_RX) &&
+ existing.rxDeviceIds().contains(deviceId)) {
+ rxDeviceIds = Sets.newHashSet(existing.rxDeviceIds());
+ rxDeviceIds.remove(deviceId);
+ }
+
+ if (txDeviceIds != null || rxDeviceIds != null) {
+ //removeVTapIdFromDeviceId(existing.id(), deviceId); // execute from event listener
+
+ return DefaultOpenstackVtap.builder()
+ .id(vTapId)
+ .type(existing.type())
+ .vTapCriterion(existing.vTapCriterion())
+ .txDeviceIds(txDeviceIds != null ? txDeviceIds : existing.txDeviceIds())
+ .rxDeviceIds(rxDeviceIds != null ? rxDeviceIds : existing.rxDeviceIds())
+ .annotations(existing.annotations())
+ .build();
+ }
+ return existing;
+ });
+ return (vTap != null);
+ }
+
+ @Override
+ public boolean updateDeviceForVtap(OpenstackVtapId vTapId,
+ Set<DeviceId> txDeviceIds, Set<DeviceId> rxDeviceIds,
+ boolean replaceDevices) {
+ checkNotNull(vTapId);
+ checkNotNull(txDeviceIds);
+ checkNotNull(rxDeviceIds);
+
+ OpenstackVtap vTap = vTapMap.compute(vTapId, (id, existing) -> {
+ if (existing == null) {
+ return null;
+ }
+
+ // Replace or add devices
+ Set<DeviceId> txDS = null;
+ if (replaceDevices) {
+ if (!existing.txDeviceIds().equals(txDeviceIds)) {
+ txDS = ImmutableSet.copyOf(txDeviceIds);
+ }
+ } else {
+ if (!existing.txDeviceIds().containsAll(txDeviceIds)) {
+ txDS = Sets.newHashSet(existing.txDeviceIds());
+ txDS.addAll(txDeviceIds);
+ }
+ }
+
+ Set<DeviceId> rxDS = null;
+ if (replaceDevices) {
+ if (!existing.rxDeviceIds().equals(rxDeviceIds)) {
+ rxDS = ImmutableSet.copyOf(rxDeviceIds);
+ }
+ } else {
+ if (!existing.rxDeviceIds().containsAll(rxDeviceIds)) {
+ rxDS = Sets.newHashSet(existing.rxDeviceIds());
+ rxDS.addAll(rxDeviceIds);
+ }
+ }
+
+ if (txDS != null || rxDS != null) {
+
+ return DefaultOpenstackVtap.builder()
+ .id(vTapId)
+ .type(existing.type())
+ .vTapCriterion(existing.vTapCriterion())
+ .txDeviceIds(txDS != null ? txDS : existing.txDeviceIds())
+ .rxDeviceIds(rxDS != null ? rxDS : existing.rxDeviceIds())
+ .annotations(existing.annotations())
+ .build();
+ }
+ return existing;
+ });
+ return (vTap != null);
+ }
+
+ @Override
+ public int getVtapCount(OpenstackVtap.Type type) {
+ return (int) vTapMap.values().parallelStream()
+ .filter(vTap -> vTap.type().isValid(type))
+ .count();
+ }
+
+ @Override
+ public Set<OpenstackVtap> getVtaps(OpenstackVtap.Type type) {
+ return ImmutableSet.copyOf(
+ vTapMap.values().parallelStream()
+ .filter(vTap -> vTap.type().isValid(type))
+ .collect(Collectors.toSet()));
+ }
+
+ @Override
+ public OpenstackVtap getVtap(OpenstackVtapId vTapId) {
+ return vTapMap.get(vTapId);
+ }
+
+ @Override
+ public Set<OpenstackVtap> getVtapsByDeviceId(OpenstackVtap.Type type,
+ DeviceId deviceId) {
+ Set<OpenstackVtapId> vtapIds = Sets.newHashSet();
+ if (type.isValid(OpenstackVtap.Type.VTAP_TX)) {
+ vtapIds.addAll(vTapIdsByTxDeviceId.get(deviceId));
+ }
+ if (type.isValid(OpenstackVtap.Type.VTAP_RX)) {
+ vtapIds.addAll(vTapIdsByRxDeviceId.get(deviceId));
+ }
+
+ return ImmutableSet.copyOf(
+ vtapIds.parallelStream()
+ .map(vTapId -> vTapMap.get(vTapId))
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet()));
+ }
+
+ private class VTapComparator implements Comparator<OpenstackVtap> {
+ @Override
+ public int compare(OpenstackVtap v1, OpenstackVtap v2) {
+ int diff = (v2.type().compareTo(v1.type()));
+ if (diff == 0) {
+ return (v2.vTapCriterion().ipProtocol() - v1.vTapCriterion().ipProtocol());
+ }
+ return diff;
+ }
+ }
+
+ private static Set<OpenstackVtapId> addVTapIds(OpenstackVtapId vTapId) {
+ Set<OpenstackVtapId> vtapIds = Sets.newConcurrentHashSet();
+ vtapIds.add(vTapId);
+ return vtapIds;
+ }
+
+ private static Set<OpenstackVtapId> updateVTapIds(Set<OpenstackVtapId> existingVtapIds,
+ OpenstackVtapId vTapId) {
+ existingVtapIds.add(vTapId);
+ return existingVtapIds;
+ }
+
+ private static Set<OpenstackVtapId> removeVTapIds(Set<OpenstackVtapId> existingVtapIds,
+ OpenstackVtapId vTapId) {
+ existingVtapIds.remove(vTapId);
+ if (existingVtapIds.isEmpty()) {
+ return null;
+ }
+ return existingVtapIds;
+ }
+
+ private void updateVTapIdFromTxDeviceId(OpenstackVtapId vTapId, DeviceId deviceId) {
+ vTapIdsByTxDeviceId.compute(deviceId, (k, v) -> v == null ?
+ addVTapIds(vTapId) : updateVTapIds(v, vTapId));
+ }
+
+ private void removeVTapIdFromTxDeviceId(OpenstackVtapId vTapId, DeviceId deviceId) {
+ vTapIdsByTxDeviceId.computeIfPresent(deviceId, (k, v) -> removeVTapIds(v, vTapId));
+ }
+
+ private void updateVTapIdFromRxDeviceId(OpenstackVtapId vTapId, DeviceId deviceId) {
+ vTapIdsByRxDeviceId.compute(deviceId, (k, v) -> v == null ?
+ addVTapIds(vTapId) : updateVTapIds(v, vTapId));
+ }
+
+ private void removeVTapIdFromRxDeviceId(OpenstackVtapId vTapId, DeviceId deviceId) {
+ vTapIdsByRxDeviceId.computeIfPresent(deviceId, (k, v) -> removeVTapIds(v, vTapId));
+ }
+
+ private void refreshDeviceIdsByVtap(OpenstackVtap oldOpenstackVtap,
+ OpenstackVtap newOpenstackVtap) {
+ if (oldOpenstackVtap != null) {
+ Set<DeviceId> removeDeviceIds;
+
+ // Remove TX vTap
+ removeDeviceIds = (newOpenstackVtap != null) ?
+ Sets.difference(oldOpenstackVtap.txDeviceIds(),
+ newOpenstackVtap.txDeviceIds()) : oldOpenstackVtap.txDeviceIds();
+ removeDeviceIds.forEach(id -> removeVTapIdFromTxDeviceId(oldOpenstackVtap.id(), id));
+
+ // Remove RX vTap
+ removeDeviceIds = (newOpenstackVtap != null) ?
+ Sets.difference(oldOpenstackVtap.rxDeviceIds(),
+ newOpenstackVtap.rxDeviceIds()) : oldOpenstackVtap.rxDeviceIds();
+ removeDeviceIds.forEach(id -> removeVTapIdFromRxDeviceId(oldOpenstackVtap.id(), id));
+ }
+
+ if (newOpenstackVtap != null) {
+ Set<DeviceId> addDeviceIds;
+
+ // Add TX vTap
+ addDeviceIds = (oldOpenstackVtap != null) ?
+ Sets.difference(newOpenstackVtap.txDeviceIds(),
+ oldOpenstackVtap.txDeviceIds()) : newOpenstackVtap.txDeviceIds();
+ addDeviceIds.forEach(id -> updateVTapIdFromTxDeviceId(newOpenstackVtap.id(), id));
+
+ // Add RX vTap
+ addDeviceIds = (oldOpenstackVtap != null) ?
+ Sets.difference(newOpenstackVtap.rxDeviceIds(),
+ oldOpenstackVtap.rxDeviceIds()) : newOpenstackVtap.rxDeviceIds();
+ addDeviceIds.forEach(id -> updateVTapIdFromRxDeviceId(newOpenstackVtap.id(), id));
+ }
+ }
+
+ private class VTapEventListener
+ implements MapEventListener<OpenstackVtapId, DefaultOpenstackVtap> {
+ @Override
+ public void event(MapEvent<OpenstackVtapId, DefaultOpenstackVtap> event) {
+ DefaultOpenstackVtap newValue =
+ event.newValue() != null ? event.newValue().value() : null;
+ DefaultOpenstackVtap oldValue =
+ event.oldValue() != null ? event.oldValue().value() : null;
+
+ log.debug("VTapEventListener {} -> {}, {}", event.type(), oldValue, newValue);
+ switch (event.type()) {
+ case INSERT:
+ refreshDeviceIdsByVtap(oldValue, newValue);
+ notifyDelegate(new OpenstackVtapEvent(
+ OpenstackVtapEvent.Type.VTAP_ADDED, newValue));
+ break;
+
+ case UPDATE:
+ if (!Objects.equals(newValue, oldValue)) {
+ refreshDeviceIdsByVtap(oldValue, newValue);
+ notifyDelegate(new OpenstackVtapEvent(
+ OpenstackVtapEvent.Type.VTAP_UPDATED, newValue, oldValue));
+ }
+ break;
+
+ case REMOVE:
+ refreshDeviceIdsByVtap(oldValue, newValue);
+ notifyDelegate(new OpenstackVtapEvent(
+ OpenstackVtapEvent.Type.VTAP_REMOVED, oldValue));
+ break;
+
+ default:
+ log.warn("Unknown map event type: {}", event.type());
+ }
+ }
+ }
+}
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/OpenstackVtapManager.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/OpenstackVtapManager.java
index a7c4de7..fec6d18 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/OpenstackVtapManager.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/OpenstackVtapManager.java
@@ -15,8 +15,17 @@
*/
package org.onosproject.openstackvtap.impl;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.VlanId;
+import org.onosproject.event.AbstractListenerManager;
import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
import org.onosproject.openstackvtap.api.OpenstackVtap;
+import org.onosproject.openstackvtap.api.OpenstackVtap.Type;
+import org.onosproject.openstackvtap.api.OpenstackVtapAdminService;
+import org.onosproject.openstackvtap.api.OpenstackVtapCriterion;
+import org.onosproject.openstackvtap.api.OpenstackVtapEvent;
import org.onosproject.openstackvtap.api.OpenstackVtapId;
import org.onosproject.openstackvtap.api.OpenstackVtapListener;
import org.onosproject.openstackvtap.api.OpenstackVtapService;
@@ -24,17 +33,46 @@
import java.util.Set;
/**
- * Implementation of openstack vtap.
+ * Provides basic implementation of the user APIs.
*/
-public class OpenstackVtapManager implements OpenstackVtapService {
+@Component(immediate = true)
+@Service
+public class OpenstackVtapManager
+ extends AbstractListenerManager<OpenstackVtapEvent, OpenstackVtapListener>
+ implements OpenstackVtapService, OpenstackVtapAdminService {
@Override
- public int getVtapCount(OpenstackVtap.Type type) {
+ public OpenstackVtap createVtap(Type type, OpenstackVtapCriterion vTapCriterion) {
+ return null;
+ }
+
+ @Override
+ public OpenstackVtap updateVtap(OpenstackVtapId vTapId, OpenstackVtap vTap) {
+ return null;
+ }
+
+ @Override
+ public OpenstackVtap removeVtap(OpenstackVtapId vTapId) {
+ return null;
+ }
+
+ @Override
+ public void setVtapOutput(DeviceId deviceId, Type type, PortNumber portNumber, VlanId vlanId) {
+
+ }
+
+ @Override
+ public void setVtapOutput(DeviceId deviceId, Type type, PortNumber portNumber, int vni) {
+
+ }
+
+ @Override
+ public int getVtapCount(Type type) {
return 0;
}
@Override
- public Set<OpenstackVtap> getVtaps(OpenstackVtap.Type type) {
+ public Set<OpenstackVtap> getVtaps(Type type) {
return null;
}
@@ -44,15 +82,7 @@
}
@Override
- public Set<OpenstackVtap> getVtapsByDeviceId(OpenstackVtap.Type type, DeviceId deviceId) {
+ public Set<OpenstackVtap> getVtapsByDeviceId(Type type, DeviceId deviceId) {
return null;
}
-
- @Override
- public void addListener(OpenstackVtapListener listener) {
- }
-
- @Override
- public void removeListener(OpenstackVtapListener listener) {
- }
}