diff options
author | Ashlee Young <ashlee@wildernessvoice.com> | 2015-11-03 14:08:10 -0800 |
---|---|---|
committer | Ashlee Young <ashlee@wildernessvoice.com> | 2015-11-03 14:08:10 -0800 |
commit | 643ee33289bd2cb9e6afbfb09b4ed72d467ba1c2 (patch) | |
tree | c2c376a44a359544fe3d4c45eb0cc0e2ec4a7080 /framework/src/onos/core | |
parent | 46eeb79b54345bdafb6055b8ee4bad4ce8b01274 (diff) |
This updates ONOS src tree to commit id
03fa5e571cabbd001ddb1598847e1150b11c7333
Change-Id: I13b554026d6f902933e35887d29bd5fdb669c0bd
Signed-off-by: Ashlee Young <ashlee@wildernessvoice.com>
Diffstat (limited to 'framework/src/onos/core')
100 files changed, 4498 insertions, 972 deletions
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterAdminService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterAdminService.java index 5f2b5fff..47944874 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterAdminService.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterAdminService.java @@ -30,9 +30,8 @@ public interface ClusterAdminService { * instance. * * @param nodes set of nodes that form the cluster - * @param ipPrefix IP address prefix, e.g. 10.0.1.* */ - void formCluster(Set<ControllerNode> nodes, String ipPrefix); + void formCluster(Set<ControllerNode> nodes); /** * Adds a new controller node to the cluster. diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterDefinitionService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterDefinitionService.java deleted file mode 100644 index 1ee78b15..00000000 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterDefinitionService.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2015 Open Networking Laboratory - * - * 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.cluster; - -import java.util.Set; - -/** - * Service for obtaining the static definition of a controller cluster. - */ -public interface ClusterDefinitionService { - - /** - * Returns the local controller node. - * @return local controller node - */ - ControllerNode localNode(); - - /** - * Returns the set of seed nodes that should be used for discovering other members - * of the cluster. - * @return set of seed controller nodes - */ - Set<ControllerNode> seedNodes(); - - /** - * Forms cluster configuration based on the specified set of node - * information. Assumes subsequent restart for the new configuration to - * take hold. - * - * @param nodes set of nodes that form the cluster - * @param ipPrefix IP address prefix, e.g. 10.0.1.* - */ - void formCluster(Set<ControllerNode> nodes, String ipPrefix); -}
\ No newline at end of file diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadata.java b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadata.java new file mode 100644 index 00000000..e1eacfee --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadata.java @@ -0,0 +1,185 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.cluster; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Verify.verifyNotNull; +import static com.google.common.base.Verify.verify; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; + +/** + * Cluster metadata. + * <p> + * Metadata specifies the attributes that define a ONOS cluster and comprises the collection + * of {@link org.onosproject.cluster.ControllerNode nodes} and the collection of data + * {@link org.onosproject.cluster.Partition partitions}. + */ +public final class ClusterMetadata { + + private String name; + private Set<ControllerNode> nodes; + private Set<Partition> partitions; + + /** + * Returns a new cluster metadata builder. + * @return The cluster metadata builder. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Returns the name of the cluster. + * + * @return cluster name + */ + public String getName() { + return this.name; + } + + /** + * Returns the collection of {@link org.onosproject.cluster.ControllerNode nodes} that make up the cluster. + * @return cluster nodes + */ + public Collection<ControllerNode> getNodes() { + return this.nodes; + } + + /** + * Returns the collection of data {@link org.onosproject.cluster.Partition partitions} that make up the cluster. + * @return collection of partitions. + */ + public Collection<Partition> getPartitions() { + return this.partitions; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(ClusterMetadata.class) + .add("name", name) + .add("nodes", nodes) + .add("partitions", partitions) + .toString(); + } + + @Override + public int hashCode() { + return Arrays.deepHashCode(new Object[] {name, nodes, partitions}); + } + + /* + * Provide a deep quality check of the meta data (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object object) { + + if (!ClusterMetadata.class.isInstance(object)) { + return false; + } + ClusterMetadata that = (ClusterMetadata) object; + + if (!this.name.equals(that.name) || this.nodes.size() != that.nodes.size() + || this.partitions.size() != that.partitions.size()) { + return false; + } + + return Sets.symmetricDifference(this.nodes, that.nodes).isEmpty() + && Sets.symmetricDifference(this.partitions, that.partitions).isEmpty(); + } + + /** + * Builder for a {@link ClusterMetadata} instance. + */ + public static class Builder { + + private final ClusterMetadata metadata; + + public Builder() { + metadata = new ClusterMetadata(); + } + + /** + * Sets the cluster name, returning the cluster metadata builder for method chaining. + * @param name cluster name + * @return this cluster metadata builder + */ + public Builder withName(String name) { + metadata.name = checkNotNull(name); + return this; + } + + /** + * Sets the collection of cluster nodes, returning the cluster metadata builder for method chaining. + * @param controllerNodes collection of cluster nodes + * @return this cluster metadata builder + */ + public Builder withControllerNodes(Collection<ControllerNode> controllerNodes) { + metadata.nodes = ImmutableSet.copyOf(checkNotNull(controllerNodes)); + return this; + } + + /** + * Sets the collection of data partitions, returning the cluster metadata builder for method chaining. + * @param partitions collection of partitions + * @return this cluster metadata builder + */ + public Builder withPartitions(Collection<Partition> partitions) { + metadata.partitions = ImmutableSet.copyOf(checkNotNull(partitions)); + return this; + } + + /** + * Builds the cluster metadata. + * @return cluster metadata + * @throws com.google.common.base.VerifyException VerifyException if the metadata is misconfigured + */ + public ClusterMetadata build() { + verifyMetadata(); + return metadata; + } + + /** + * Validates the constructed metadata for semantic correctness. + * @throws VerifyException if the metadata is misconfigured. + */ + private void verifyMetadata() { + verifyNotNull(metadata.getName(), "Cluster name must be specified"); + verifyNotNull(metadata.getNodes(), "Cluster nodes must be specified"); + verifyNotNull(metadata.getPartitions(), "Cluster partitions must be specified"); + verify(!metadata.getNodes().isEmpty(), "Cluster nodes must not be empty"); + verify(!metadata.getPartitions().isEmpty(), "Cluster nodes must not be empty"); + + // verify that partitions are constituted from valid cluster nodes. + boolean validPartitions = Collections2.transform(metadata.getNodes(), ControllerNode::id) + .containsAll(metadata.getPartitions() + .stream() + .flatMap(r -> r.getMembers().stream()) + .collect(Collectors.toSet())); + verify(validPartitions, "Partition locations must be valid cluster nodes"); + } + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataEvent.java b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataEvent.java new file mode 100644 index 00000000..a0f461c4 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataEvent.java @@ -0,0 +1,56 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.cluster; + +import org.onosproject.event.AbstractEvent; + +/** + * Describes a cluster metadata event. + */ +public class ClusterMetadataEvent extends AbstractEvent<ClusterMetadataEvent.Type, ClusterMetadata> { + + /** + * Type of cluster metadata events. + */ + public enum Type { + /** + * Signifies that the cluster metadata has changed. + */ + METADATA_CHANGED, + } + + /** + * Creates an event of a given type and for the specified metadata and the + * current time. + * + * @param type cluster metadata event type + * @param metadata cluster metadata subject + */ + public ClusterMetadataEvent(Type type, ClusterMetadata metadata) { + super(type, metadata); + } + + /** + * Creates an event of a given type and for the specified metadata and time. + * + * @param type cluster metadata event type + * @param metadata cluster metadata subject + * @param time occurrence time + */ + public ClusterMetadataEvent(Type type, ClusterMetadata metadata, long time) { + super(type, metadata, time); + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataEventListener.java b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataEventListener.java new file mode 100644 index 00000000..fdfaeed0 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataEventListener.java @@ -0,0 +1,24 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.cluster; + +import org.onosproject.event.EventListener; + +/** + * Entity capable of receiving cluster metadata related events. + */ +public interface ClusterMetadataEventListener extends EventListener<ClusterMetadataEvent> { +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataService.java new file mode 100644 index 00000000..25a6df63 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataService.java @@ -0,0 +1,40 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.cluster; + +/** + * Service for obtaining metadata information about the cluster. + */ +public interface ClusterMetadataService { + + /** + * Returns the current cluster metadata. + * @return cluster metadata + */ + ClusterMetadata getClusterMetadata(); + + /** + * Updates the cluster metadata. + * @param metadata new metadata + */ + void setClusterMetadata(ClusterMetadata metadata); + + /** + * Returns the local controller node representing this instance. + * @return local controller node + */ + ControllerNode getLocalNode(); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataStore.java b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataStore.java new file mode 100644 index 00000000..7e83b5b5 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataStore.java @@ -0,0 +1,77 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.cluster; + +import java.util.Collection; + +import org.onosproject.store.Store; +import org.onosproject.store.service.Versioned; + +/** + * Manages persistence of cluster metadata; not intended for direct use. + */ +public interface ClusterMetadataStore extends Store<ClusterMetadataEvent, ClusterMetadataStoreDelegate> { + + /** + * Returns the cluster metadata. + * <p> + * The returned metadata is versioned to aid determining if a metadata instance is more recent than another. + * @return cluster metadata + */ + Versioned<ClusterMetadata> getClusterMetadata(); + + /** + * Updates the cluster metadata. + * @param metadata new metadata value + */ + void setClusterMetadata(ClusterMetadata metadata); + + // TODO: The below methods should move to a separate store interface that is responsible for + // tracking cluster partition operational state. + + /** + * Sets a controller node as an active member of a partition. + * <p> + * Active members are those replicas that are up to speed with the rest of the system and are + * usually capable of participating in the replica state management activities in accordance with + * the data consistency and replication protocol in use. + * @param partitionId partition identifier + * @param nodeId id of controller node + */ + void setActiveReplica(String partitionId, NodeId nodeId); + + /** + * Removes a controller node as an active member for a partition. + * <p> + * Active members are those replicas that are up to speed with the rest of the system and are + * usually capable of participating in the replica state management activities in accordance with + * the data consistency and replication protocol in use. + * @param partitionId partition identifier + * @param nodeId id of controller node + */ + void unsetActiveReplica(String partitionId, NodeId nodeId); + + /** + * Returns the collection of controller nodes that are the active replicas for a partition. + * <p> + * Active members are those replicas that are up to speed with the rest of the system and are + * usually capable of participating in the replica state management activities in accordance with + * the data consistency and replication protocol in use. + * @param partitionId partition identifier + * @return identifiers of controller nodes that are the active replicas + */ + Collection<NodeId> getActiveReplicas(String partitionId); +}
\ No newline at end of file diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataStoreDelegate.java b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataStoreDelegate.java new file mode 100644 index 00000000..b56b7a24 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/ClusterMetadataStoreDelegate.java @@ -0,0 +1,24 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.cluster; + +import org.onosproject.store.StoreDelegate; + +/** + * Cluster metadata store delegate abstraction. + */ +public interface ClusterMetadataStoreDelegate extends StoreDelegate<ClusterMetadataEvent> { +}
\ No newline at end of file diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/NodeId.java b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/NodeId.java index 68b490f2..6cfb42c7 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/NodeId.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/NodeId.java @@ -20,7 +20,7 @@ import java.util.Objects; /** * Controller cluster identity. */ -public class NodeId { +public class NodeId implements Comparable<NodeId> { private final String id; @@ -55,4 +55,9 @@ public class NodeId { return id; } + @Override + public int compareTo(NodeId that) { + return this.id.compareTo(that.id); + } + } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/Partition.java b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/Partition.java new file mode 100644 index 00000000..1eca4aeb --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/cluster/Partition.java @@ -0,0 +1,91 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.cluster; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Set; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A data partition. + * <p> + * Partition represents a slice of the data space and is made up of a collection + * of {@link org.onosproject.cluster.ControllerNode nodes} + * that all maintain copies of this data. + */ +public class Partition { + + private final String name; + private final Set<NodeId> members; + + private Partition() { + name = null; + members = null; + } + + public Partition(String name, Collection<NodeId> members) { + this.name = checkNotNull(name); + this.members = ImmutableSet.copyOf(checkNotNull(members)); + } + + /** + * Returns the partition name. + * <p> + * Each partition is identified by a unique name. + * @return partition name + */ + public String getName() { + return this.name; + } + + /** + * Returns the collection of controller node identifiers that make up this partition. + * @return collection of controller node identifiers + */ + public Collection<NodeId> getMembers() { + return this.members; + } + + @Override + public int hashCode() { + return Arrays.deepHashCode(new Object[] {name, members}); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (other == null || !Partition.class.isInstance(other)) { + return false; + } + + Partition that = (Partition) other; + + if (!this.name.equals(that.name) || (this.members == null && that.members != null) + || (this.members != null && that.members == null) || this.members.size() != that.members.size()) { + return false; + } + + return Sets.symmetricDifference(this.members, that.members).isEmpty(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/BridgeConfig.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/BridgeConfig.java index 7f157e95..e3d6993c 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/BridgeConfig.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/BridgeConfig.java @@ -36,6 +36,17 @@ public interface BridgeConfig extends HandlerBehaviour { void addBridge(BridgeName bridgeName); /** + * Adds a bridge with given bridge name and dpid, and sets the controller + * of the bridge with given controllers. + * + * @param bridgeName bridge name + * @param dpid dpid + * @param controllers list of controller + * @return true if succeeds, fail otherwise + */ + boolean addBridge(BridgeName bridgeName, String dpid, List<ControllerInfo> controllers); + + /** * Remove a bridge. * * @param bridgeName bridge name diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/ExtensionResolver.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/ExtensionResolver.java new file mode 100644 index 00000000..54cbc7ac --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/ExtensionResolver.java @@ -0,0 +1,40 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.net.behaviour; + +import com.google.common.annotations.Beta; +import org.onosproject.net.driver.HandlerBehaviour; +import org.onosproject.net.flow.instructions.ExtensionInstruction; +import org.onosproject.net.flow.instructions.ExtensionType; + +/** + * Provides access to the extension implemented by this driver. + */ +@Beta +public interface ExtensionResolver extends HandlerBehaviour { + + /** + * Gets an extension instruction instance of the specified type, if supported + * by the driver. + * + * @param type type of extension to get + * @return extension instruction + * @throws UnsupportedOperationException if the extension type is not + * supported by this driver + */ + ExtensionInstruction getExtensionInstruction(ExtensionType type); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/TunnelConfig.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/TunnelConfig.java index 7e79a57e..e3b4c198 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/TunnelConfig.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/TunnelConfig.java @@ -32,6 +32,15 @@ public interface TunnelConfig extends HandlerBehaviour { void createTunnel(TunnelDescription tunnel); /** + * Creates a tunnel interface on a given bridge of this device. + * + * @param bridgeName bridge name + * @param tunnel tunnel description + * @return true if succeeds, false otherwise + */ + boolean createTunnelInterface(BridgeName bridgeName, TunnelDescription tunnel); + + /** * Removes a tunnel on this device. * * @param tunnel tunnel descriptor diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java index 4416456c..453a7648 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java @@ -15,8 +15,14 @@ */ package org.onosproject.net.flow; -import com.google.common.base.MoreObjects; -import com.google.common.collect.ImmutableSet; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.TreeSet; + import org.onlab.packet.Ip6Address; import org.onlab.packet.IpPrefix; import org.onlab.packet.MacAddress; @@ -27,13 +33,8 @@ import org.onosproject.net.PortNumber; import org.onosproject.net.flow.criteria.Criteria; import org.onosproject.net.flow.criteria.Criterion; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.TreeSet; +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableSet; /** * Default traffic selector implementation. diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java index 6174cef6..6beeecc9 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java @@ -15,9 +15,11 @@ */ package org.onosproject.net.flow; -import com.google.common.base.MoreObjects; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.List; +import java.util.Objects; + import org.onlab.packet.EthType; import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; @@ -25,16 +27,17 @@ import org.onlab.packet.MplsLabel; import org.onlab.packet.TpPort; import org.onlab.packet.VlanId; import org.onosproject.core.GroupId; +import org.onosproject.net.DeviceId; import org.onosproject.net.IndexedLambda; import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.instructions.ExtensionInstruction; import org.onosproject.net.flow.instructions.Instruction; import org.onosproject.net.flow.instructions.Instructions; import org.onosproject.net.meter.MeterId; -import java.util.List; -import java.util.Objects; - -import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; /** * Default traffic treatment implementation. @@ -239,9 +242,11 @@ public final class DefaultTrafficTreatment implements TrafficTreatment { case GROUP: case QUEUE: case L0MODIFICATION: + case L1MODIFICATION: case L2MODIFICATION: case L3MODIFICATION: case L4MODIFICATION: + case EXTENSION: current.add(instruction); break; case TABLE: @@ -479,6 +484,12 @@ public final class DefaultTrafficTreatment implements TrafficTreatment { } @Override + public TrafficTreatment.Builder extension(ExtensionInstruction extension, + DeviceId deviceId) { + return add(Instructions.extension(extension, deviceId)); + } + + @Override public TrafficTreatment build() { if (deferred.size() == 0 && immediate.size() == 0 && table == null && !clear) { diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java index c7fe8b85..b14ab99c 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java @@ -15,6 +15,8 @@ */ package org.onosproject.net.flow; +import java.util.List; + import org.onlab.packet.EthType; import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; @@ -22,13 +24,13 @@ import org.onlab.packet.MplsLabel; import org.onlab.packet.TpPort; import org.onlab.packet.VlanId; import org.onosproject.core.GroupId; +import org.onosproject.net.DeviceId; import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.instructions.ExtensionInstruction; import org.onosproject.net.flow.instructions.Instruction; import org.onosproject.net.flow.instructions.Instructions; import org.onosproject.net.meter.MeterId; -import java.util.List; - /** * Abstraction of network traffic treatment. */ @@ -413,6 +415,15 @@ public interface TrafficTreatment { Builder setUdpDst(TpPort port); /** + * Uses an extension treatment. + * + * @param extension extension treatment + * @param deviceId device ID + * @return a treatment builder + */ + Builder extension(ExtensionInstruction extension, DeviceId deviceId); + + /** * Builds an immutable traffic treatment descriptor. * <p> * If the treatment is empty when build() is called, it will add a default diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/AbstractExtensionInstruction.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/AbstractExtensionInstruction.java new file mode 100644 index 00000000..9f22f888 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/AbstractExtensionInstruction.java @@ -0,0 +1,71 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.net.flow.instructions; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +/** + * Abstract implementation of the set/get property methods of ExtensionInstruction. + */ +public abstract class AbstractExtensionInstruction implements ExtensionInstruction { + + private static final String INVALID_KEY = "Invalid property key: "; + private static final String INVALID_TYPE = "Given type does not match field type: "; + + @Override + public <T> void setPropertyValue(String key, T value) throws ExtensionPropertyException { + Class<?> clazz = this.getClass(); + try { + Field field = clazz.getDeclaredField(key); + field.setAccessible(true); + field.set(this, value); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new ExtensionPropertyException(INVALID_KEY + key); + } + } + + @Override + public <T> T getPropertyValue(String key) throws ExtensionPropertyException { + Class<?> clazz = this.getClass(); + try { + Field field = clazz.getDeclaredField(key); + field.setAccessible(true); + @SuppressWarnings("unchecked") + T result = (T) field.get(this); + return result; + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new ExtensionPropertyException(INVALID_KEY + key); + } catch (ClassCastException e) { + throw new ExtensionPropertyException(INVALID_TYPE + key); + } + } + + @Override + public List<String> getProperties() { + Class<?> clazz = this.getClass(); + + List<String> fields = new ArrayList<>(); + + for (Field field : clazz.getDeclaredFields()) { + fields.add(field.getName()); + } + + return fields; + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionInstruction.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionInstruction.java new file mode 100644 index 00000000..89e0cc5e --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionInstruction.java @@ -0,0 +1,78 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.net.flow.instructions; + +import java.util.List; + +/** + * An extensible instruction type. + */ +public interface ExtensionInstruction { + + /** + * Gets the type of the extension instruction. + * + * @return type + */ + ExtensionType type(); + + /** + * Sets a property on the extension instruction. + * + * @param key property key + * @param value value to set for the given key + * @param <T> class of the value + * @throws ExtensionPropertyException if the given key is not a valid + * property on this extension instruction + */ + <T> void setPropertyValue(String key, T value) throws ExtensionPropertyException; + + /** + * Gets a property value of an extension instruction. + * + * @param key property key + * @param <T> class of the value + * @return value of the property + * @throws ExtensionPropertyException if the given key is not a valid + * property on this extension instruction + */ + <T> T getPropertyValue(String key) throws ExtensionPropertyException; + + /** + * Gets a list of all properties on the extension instruction. + * + * @return list of properties + */ + List<String> getProperties(); + + /** + * Serialize the extension instruction to a byte array. + * + * @return byte array + */ + byte[] serialize(); + + /** + * Deserialize the extension instruction from a byte array. The properties + * of this object will be overwritten with the data in the byte array. + * + * @param data input byte array + */ + void deserialize(byte[] data); + + +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/Treatment.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionPropertyException.java index a77079ce..5750d09e 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/Treatment.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionPropertyException.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 Open Networking Laboratory + * Copyright 2015 Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,24 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.onosproject.net.flow; -import org.onosproject.net.PortNumber; +package org.onosproject.net.flow.instructions; /** - * Abstraction of different kinds of treatment that can be applied to an - * outbound packet. + * Exception indicating there was an error while setting/getting an extension + * instruction property. */ -public interface Treatment { +public class ExtensionPropertyException extends Exception { - // TODO: implement these later: modifications, group - // TODO: elsewhere provide factory methods for some default treatments - - /** - * Returns the port number where the packet should be emitted. - * - * @return output port number - */ - PortNumber output(); + public ExtensionPropertyException(String message) { + super(message); + } + public ExtensionPropertyException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionType.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionType.java new file mode 100644 index 00000000..747a85b5 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/ExtensionType.java @@ -0,0 +1,92 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.net.flow.instructions; + +import com.google.common.annotations.Beta; +import com.google.common.base.MoreObjects; + +import java.util.Objects; + +/** + * Type of extension instructions. + */ +@Beta +public final class ExtensionType { + + /** + * A list of well-known named extension instruction type codes. + */ + public enum ExtensionTypes { + // TODO fix type numbers to include experimenter id + NICIRA_SET_TUNNEL_DST(31); + + private ExtensionType type; + + /** + * Creates a new named extension instruction type. + * + * @param type type code + */ + ExtensionTypes(int type) { + this.type = new ExtensionType(type); + } + + /** + * Gets the extension type object for this named type code. + * + * @return extension type object + */ + public ExtensionType type() { + return type; + } + } + + private final int type; + + /** + * Creates an extension type with the given int type code. + * + * @param type type code + */ + public ExtensionType(int type) { + this.type = type; + } + + @Override + public int hashCode() { + return Objects.hash(type); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof ExtensionType) { + final ExtensionType that = (ExtensionType) obj; + return this.type == that.type; + } + return false; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(ExtensionType.class) + .add("type", type) + .toString(); + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java index 2f6a1cc1..31ad80c5 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java @@ -92,7 +92,12 @@ public interface Instruction { /** * Signifies that the traffic should be modified in L4 way. */ - L4MODIFICATION + L4MODIFICATION, + + /** + * Signifies that an extension instruction will be used. + */ + EXTENSION } /** diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java index 8868bf7c..aad407c8 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java @@ -22,6 +22,7 @@ import org.onlab.packet.MplsLabel; import org.onlab.packet.TpPort; import org.onlab.packet.VlanId; import org.onosproject.core.GroupId; +import org.onosproject.net.DeviceId; import org.onosproject.net.IndexedLambda; import org.onosproject.net.Lambda; import org.onosproject.net.OchSignal; @@ -480,6 +481,20 @@ public final class Instructions { } /** + * Creates an extension instruction. + * + * @param extension extension instruction + * @param deviceId device ID + * @return extension instruction + */ + public static ExtensionInstructionWrapper extension(ExtensionInstruction extension, + DeviceId deviceId) { + checkNotNull(extension, "Extension instruction cannot be null"); + checkNotNull(deviceId, "Device ID cannot be null"); + return new ExtensionInstructionWrapper(extension, deviceId); + } + + /** * Drop instruction. */ @Deprecated @@ -820,6 +835,59 @@ public final class Instructions { } } + /** + * Extension instruction. + */ + public static class ExtensionInstructionWrapper implements Instruction { + private final ExtensionInstruction extensionInstruction; + private final DeviceId deviceId; + + ExtensionInstructionWrapper(ExtensionInstruction extension, DeviceId deviceId) { + extensionInstruction = extension; + this.deviceId = deviceId; + } + + public ExtensionInstruction extensionInstruction() { + return extensionInstruction; + } + + public DeviceId deviceId() { + return deviceId; + } + + @Override + public Type type() { + return Type.EXTENSION; + } + + @Override + public String toString() { + return toStringHelper(type().toString()) + .add("extension", extensionInstruction) + .add("deviceId", deviceId) + .toString(); + } + + @Override + public int hashCode() { + return Objects.hash(type().ordinal(), extensionInstruction, deviceId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof ExtensionInstructionWrapper) { + ExtensionInstructionWrapper that = (ExtensionInstructionWrapper) obj; + return Objects.equals(extensionInstruction, that.extensionInstruction) + && Objects.equals(deviceId, that.deviceId); + + } + return false; + } + } + } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flowobjective/DefaultFilteringObjective.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flowobjective/DefaultFilteringObjective.java index 7b5924fb..06305bf7 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flowobjective/DefaultFilteringObjective.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flowobjective/DefaultFilteringObjective.java @@ -18,6 +18,7 @@ package org.onosproject.net.flowobjective; import com.google.common.annotations.Beta; import com.google.common.collect.ImmutableList; import org.onosproject.core.ApplicationId; +import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.flow.criteria.Criteria; import org.onosproject.net.flow.criteria.Criterion; @@ -46,6 +47,7 @@ public final class DefaultFilteringObjective implements FilteringObjective { private final int id; private final Operation op; private final Optional<ObjectiveContext> context; + private final TrafficTreatment meta; private DefaultFilteringObjective(Builder builder) { this.key = builder.key; @@ -57,6 +59,7 @@ public final class DefaultFilteringObjective implements FilteringObjective { this.conditions = builder.conditions; this.op = builder.op; this.context = Optional.ofNullable(builder.context); + this.meta = builder.meta; this.id = Objects.hash(type, key, conditions, permanent, timeout, appId, priority); @@ -83,6 +86,12 @@ public final class DefaultFilteringObjective implements FilteringObjective { } @Override + public TrafficTreatment meta() { + return meta; + } + + + @Override public int priority() { return priority; } @@ -135,6 +144,7 @@ public final class DefaultFilteringObjective implements FilteringObjective { private List<Criterion> conditions; private Operation op; private ObjectiveContext context; + private TrafficTreatment meta; @Override public Builder withKey(Criterion key) { @@ -186,6 +196,12 @@ public final class DefaultFilteringObjective implements FilteringObjective { } @Override + public Builder setMeta(TrafficTreatment treatment) { + this.meta = treatment; + return this; + } + + @Override public FilteringObjective add() { conditions = listBuilder.build(); op = Operation.ADD; diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flowobjective/FilteringObjective.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flowobjective/FilteringObjective.java index 58304571..29257c61 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flowobjective/FilteringObjective.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flowobjective/FilteringObjective.java @@ -17,49 +17,54 @@ package org.onosproject.net.flowobjective; import com.google.common.annotations.Beta; import org.onosproject.core.ApplicationId; +import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.flow.criteria.Criterion; import java.util.Collection; /** * Represents a filtering flow objective. Each filtering flow objective - * is made up of a key (criterion) to a set of criteria. Using this information - * a pipeline aware driver will decide how this objective should be mapped - * to the specific device pipeline. For example, consider the following - * filtering objective: - * - * portX -> {MAC1, IP1, MAC2} - * - * The driver could decide to pass L3 packet to the L3 table and L2 packets to - * the L2 table for packets arriving on portX. - * - * Filtering objectives do not only represent what should be permitted into the - * pipeline but can also be used to deny or drop unwanted packets by specifying - * the appropriate type of filtering objective. It is also important to note - * that submitting a filtering objective does not necessarily result in rules - * programmed at the switch, the driver is free to decide when these rules are - * programmed. For example, a filtering rule may only be programmed once a - * corresponding forwarding objective has been received. + * is made up of a key (typically a PortCriterion) mapped to a set of criteria. + * Using this information, a pipeline aware driver will decide how this objective + * should be mapped to the device specific pipeline-tables in order to satisfy the + * filtering condition. For example, consider the following PERMIT filtering + * objective: + * <p> + * portX -> {MAC1, VLAN1} + * <p> + * The driver could decide to pass packets to the MAC table or VLAN or PORT + * tables to ensure that only those packets arriving with the correct dst MAC + * and VLAN ids from Port X are allowed into the pipeline. + * <p> + * Filtering objectives of type PERMIT allow packets that match the key:criteria + * to enter the pipeline. As a result, the implication is that packets that don't + * match are automatically denied (dropped). + * <p> + * Filtering objectives of type DENY, are used to deny packets that would + * otherwise be permitted and forwarded through the pipeline (ie. those packets + * that make it through the PERMIT filters). */ @Beta public interface FilteringObjective extends Objective { enum Type { /** - * Enables the filtering condition. + * Permits packets that match the filtering condition to be processed + * by the rest of the pipeline. Automatically denies packets that don't + * match the criteria. */ PERMIT, /** - * Disables the filtering condition. + * Denies packets that make it through the permit filters. */ DENY } /** - * Obtain the key for this filter. + * Obtain the key for this filter. The filter may or may not require a key. * - * @return a criterion + * @return a criterion, which could be null if no key was provided. */ Criterion key(); @@ -78,6 +83,16 @@ public interface FilteringObjective extends Objective { Collection<Criterion> conditions(); /** + * Auxiliary optional information provided to the device-driver.Typically + * conveys information about changes (treatments) to packets that are + * permitted into the pipeline by the PERMIT filtering condition. + * + * @return a treatment on the packets that make it through the PERMIT filters. + * Value may be null if no meta information is provided. + */ + TrafficTreatment meta(); + + /** * Builder of Filtering objective entities. */ interface Builder extends Objective.Builder { @@ -113,11 +128,20 @@ public interface FilteringObjective extends Objective { Builder deny(); /** + * Set meta information about this filtering condition set. + * + * @param treatment traffic treatment to use + * @return a filtering builder + */ + Builder setMeta(TrafficTreatment treatment); + + /** * Assigns an application id. * * @param appId an application id * @return a filtering builder */ + @Override Builder fromApp(ApplicationId appId); /** diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/group/DefaultGroupBucket.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/group/DefaultGroupBucket.java index 6efd3e79..9d942ee4 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/group/DefaultGroupBucket.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/group/DefaultGroupBucket.java @@ -139,6 +139,20 @@ public final class DefaultGroupBucket implements GroupBucket, StoredGroupBucketE watchGroup); } + /** + * Creates all group bucket. + * + * @param treatment traffic treatment associated with group bucket + * @return all group bucket object + */ + public static GroupBucket createAllGroupBucket(TrafficTreatment treatment) { + return new DefaultGroupBucket(GroupDescription.Type.ALL, + treatment, + (short) -1, + null, + null); + } + @Override public GroupDescription.Type type() { return this.type; diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/HostToHostIntent.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/HostToHostIntent.java index bd4219ad..c1467241 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/HostToHostIntent.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/HostToHostIntent.java @@ -17,11 +17,15 @@ package org.onosproject.net.intent; import com.google.common.annotations.Beta; import com.google.common.base.MoreObjects; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; + import org.onosproject.core.ApplicationId; import org.onosproject.net.HostId; +import org.onosproject.net.Link; import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.intent.constraint.LinkTypeConstraint; import java.util.List; @@ -33,6 +37,8 @@ import static com.google.common.base.Preconditions.checkNotNull; @Beta public final class HostToHostIntent extends ConnectivityIntent { + static final LinkTypeConstraint NOT_OPTICAL = new LinkTypeConstraint(false, Link.Type.OPTICAL); + private final HostId one; private final HostId two; @@ -115,6 +121,15 @@ public final class HostToHostIntent extends ConnectivityIntent { */ public HostToHostIntent build() { + List<Constraint> theConstraints = constraints; + // If not-OPTICAL constraint hasn't been specified, add them + if (!constraints.contains(NOT_OPTICAL)) { + theConstraints = ImmutableList.<Constraint>builder() + .add(NOT_OPTICAL) + .addAll(constraints) + .build(); + } + return new HostToHostIntent( appId, key, @@ -122,7 +137,7 @@ public final class HostToHostIntent extends ConnectivityIntent { two, selector, treatment, - constraints, + theConstraints, priority ); } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/Key.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/Key.java index 18baafc8..0344acbf 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/Key.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/Key.java @@ -28,7 +28,7 @@ import java.util.Objects; */ // TODO maybe pull this up to utils @Beta -public abstract class Key { +public abstract class Key implements Comparable<Key> { //TODO consider making this a HashCode object (worry about performance) private final long hash; @@ -117,6 +117,12 @@ public abstract class Key { Objects.equals(this.appId, other.appId) && Objects.equals(this.key, other.key); } + + @Override + public int compareTo(Key o) { + StringKey sk = (StringKey) o; + return this.key.compareTo(sk.key); + } } private static final class LongKey extends Key { @@ -157,6 +163,13 @@ public abstract class Key { this.key == other.key && Objects.equals(this.appId, other.appId); } + + @Override + public int compareTo(Key o) { + Long myKey = key; + Long otherKey = ((LongKey) o).key; + return myKey.compareTo(otherKey); + } } } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/constraint/BandwidthConstraint.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/constraint/BandwidthConstraint.java index 43b8e4b1..20ccb55d 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/constraint/BandwidthConstraint.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/constraint/BandwidthConstraint.java @@ -16,6 +16,8 @@ package org.onosproject.net.intent.constraint; import com.google.common.annotations.Beta; + +import org.onlab.util.DataRateUnit; import org.onosproject.net.Link; import org.onosproject.net.resource.link.BandwidthResource; import org.onosproject.net.resource.link.BandwidthResourceRequest; @@ -32,7 +34,7 @@ import static com.google.common.base.Preconditions.checkNotNull; * Constraint that evaluates links based on available bandwidths. */ @Beta -public class BandwidthConstraint extends BooleanConstraint { +public final class BandwidthConstraint extends BooleanConstraint { private final BandwidthResource bandwidth; @@ -45,6 +47,17 @@ public class BandwidthConstraint extends BooleanConstraint { this.bandwidth = checkNotNull(bandwidth, "Bandwidth cannot be null"); } + /** + * Creates a new bandwidth constraint. + * + * @param v required amount of bandwidth + * @param unit {@link DataRateUnit} of {@code v} + * @return {@link BandwidthConstraint} instance with given bandwidth requirement + */ + public static BandwidthConstraint of(double v, DataRateUnit unit) { + return new BandwidthConstraint(BandwidthResource.of(v, unit)); + } + // Constructor for serialization private BandwidthConstraint() { this.bandwidth = null; diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/newresource/ResourceService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/newresource/ResourceService.java index 82d84743..ad684c8c 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/newresource/ResourceService.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/newresource/ResourceService.java @@ -153,6 +153,14 @@ public interface ResourceService { Collection<ResourceAllocation> getResourceAllocations(ResourceConsumer consumer); /** + * Returns resource paths that point available child resources under the specified resource path. + * + * @param parent parent resource path + * @return available resource paths under the specified resource path + */ + Collection<ResourcePath> getAvailableResources(ResourcePath parent); + + /** * Returns the availability of the specified resource. * * @param resource resource to check the availability diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/newresource/ResourceStore.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/newresource/ResourceStore.java index 5a034b4d..2cab9d4b 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/newresource/ResourceStore.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/newresource/ResourceStore.java @@ -92,6 +92,14 @@ public interface ResourceStore { Collection<ResourcePath> getResources(ResourceConsumer consumer); /** + * Returns a collection of the child resources of the specified parent. + * + * @param parent parent of the resource to be returned + * @return a collection of the child resources of the specified resource + */ + Collection<ResourcePath> getChildResources(ResourcePath parent); + + /** * Returns a collection of the resources which are children of the specified parent and * whose type is the specified class. * diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/BandwidthResource.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/BandwidthResource.java index fe21e042..0bfb3795 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/BandwidthResource.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/BandwidthResource.java @@ -16,7 +16,7 @@ package org.onosproject.net.resource.link; import org.onlab.util.Bandwidth; - +import org.onlab.util.DataRateUnit; import java.util.Objects; import static com.google.common.base.Preconditions.checkNotNull; @@ -43,6 +43,17 @@ public final class BandwidthResource implements LinkResource { } /** + * Creates a new bandwidth resource. + * + * @param v amount of bandwidth to request + * @param unit {@link DataRateUnit} of {@code v} + * @return {@link BandwidthResource} instance with given bandwidth + */ + public static BandwidthResource of(double v, DataRateUnit unit) { + return new BandwidthResource(Bandwidth.of(v, unit)); + } + + /** * Returns bandwidth as a double value. * * @return bandwidth as a double value diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/DefaultLinkResourceRequest.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/DefaultLinkResourceRequest.java index 5153aebf..f8e143a4 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/DefaultLinkResourceRequest.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/DefaultLinkResourceRequest.java @@ -16,48 +16,47 @@ package org.onosproject.net.resource.link; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; import java.util.Objects; +import java.util.stream.Collectors; +import com.google.common.annotations.Beta; +import com.google.common.collect.ImmutableMap; import org.onlab.util.Bandwidth; import org.onosproject.net.Link; import org.onosproject.net.intent.Constraint; import org.onosproject.net.intent.IntentId; -import com.google.common.collect.ImmutableSet; - import org.onosproject.net.intent.constraint.BandwidthConstraint; import org.onosproject.net.intent.constraint.LambdaConstraint; import org.onosproject.net.resource.ResourceRequest; import org.onosproject.net.resource.ResourceType; +import static com.google.common.base.Preconditions.checkNotNull; + /** * Implementation of {@link LinkResourceRequest}. */ public final class DefaultLinkResourceRequest implements LinkResourceRequest { private final IntentId intentId; - private final Collection<Link> links; - private final Set<ResourceRequest> resources; + protected final Map<Link, Set<ResourceRequest>> requests; /** - * Creates a new link resource request with the given ID, links, and - * resource requests. + * Creates a new link resource request with the specified Intent ID, + * and resource requests over links. * - * @param intentId intent ID related to this request - * @param links a set of links for the request - * @param resources a set of resources to be requested + * @param intentId intent ID associated with this request + * @param requests resource requests over links */ - private DefaultLinkResourceRequest(IntentId intentId, - Collection<Link> links, - Set<ResourceRequest> resources) { - this.intentId = intentId; - this.links = ImmutableSet.copyOf(links); - this.resources = ImmutableSet.copyOf(resources); + private DefaultLinkResourceRequest(IntentId intentId, Map<Link, Set<ResourceRequest>> requests) { + this.intentId = checkNotNull(intentId); + this.requests = checkNotNull(ImmutableMap.copyOf(requests)); } - @Override public ResourceType type() { return null; @@ -70,12 +69,19 @@ public final class DefaultLinkResourceRequest implements LinkResourceRequest { @Override public Collection<Link> links() { - return links; + return requests.keySet(); } @Override public Set<ResourceRequest> resources() { - return resources; + return requests.values().stream() + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + } + + @Override + public Set<ResourceRequest> resources(Link link) { + return requests.get(link); } /** @@ -95,8 +101,7 @@ public final class DefaultLinkResourceRequest implements LinkResourceRequest { */ public static final class Builder implements LinkResourceRequest.Builder { private IntentId intentId; - private Collection<Link> links; - private Set<ResourceRequest> resources; + private Map<Link, Set<ResourceRequest>> requests; /** * Creates a new link resource request. @@ -106,18 +111,33 @@ public final class DefaultLinkResourceRequest implements LinkResourceRequest { */ private Builder(IntentId intentId, Collection<Link> links) { this.intentId = intentId; - this.links = links; - this.resources = new HashSet<>(); + this.requests = new HashMap<>(); + for (Link link : links) { + requests.put(link, new HashSet<>()); + } } /** * Adds lambda request. * * @return self + * @deprecated in Emu Release */ + @Deprecated @Override public Builder addLambdaRequest() { - resources.add(new LambdaResourceRequest()); + for (Link link : requests.keySet()) { + requests.get(link).add(new LambdaResourceRequest()); + } + return this; + } + + @Beta + @Override + public LinkResourceRequest.Builder addLambdaRequest(LambdaResource lambda) { + for (Link link : requests.keySet()) { + requests.get(link).add(new LambdaResourceRequest(lambda)); + } return this; } @@ -125,10 +145,36 @@ public final class DefaultLinkResourceRequest implements LinkResourceRequest { * Adds Mpls request. * * @return self + * @deprecated in Emu Release */ + @Deprecated @Override public Builder addMplsRequest() { - resources.add(new MplsLabelResourceRequest()); + for (Link link : requests.keySet()) { + requests.get(link).add(new MplsLabelResourceRequest()); + } + return this; + } + + @Beta + @Override + public Builder addMplsRequest(MplsLabel label) { + for (Link link : requests.keySet()) { + requests.get(link).add(new MplsLabelResourceRequest(label)); + } + return this; + } + + @Beta + @Override + public LinkResourceRequest.Builder addMplsRequest(Map<Link, MplsLabel> labels) { + for (Link link : labels.keySet()) { + if (!requests.containsKey(link)) { + requests.put(link, new HashSet<>()); + } + requests.get(link).add(new MplsLabelResourceRequest(labels.get(link))); + } + return this; } @@ -140,7 +186,9 @@ public final class DefaultLinkResourceRequest implements LinkResourceRequest { */ @Override public Builder addBandwidthRequest(double bandwidth) { - resources.add(new BandwidthResourceRequest(new BandwidthResource(Bandwidth.bps(bandwidth)))); + for (Link link : requests.keySet()) { + requests.get(link).add(new BandwidthResourceRequest(new BandwidthResource(Bandwidth.bps(bandwidth)))); + } return this; } @@ -162,13 +210,13 @@ public final class DefaultLinkResourceRequest implements LinkResourceRequest { */ @Override public LinkResourceRequest build() { - return new DefaultLinkResourceRequest(intentId, links, resources); + return new DefaultLinkResourceRequest(intentId, requests); } } @Override public int hashCode() { - return Objects.hash(intentId, links); + return Objects.hash(intentId, links()); } @Override @@ -181,6 +229,6 @@ public final class DefaultLinkResourceRequest implements LinkResourceRequest { } final DefaultLinkResourceRequest other = (DefaultLinkResourceRequest) obj; return Objects.equals(this.intentId, other.intentId) - && Objects.equals(this.links, other.links); + && Objects.equals(this.links(), other.links()); } } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/LambdaResourceRequest.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/LambdaResourceRequest.java index b0391f5a..d264d5e5 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/LambdaResourceRequest.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/LambdaResourceRequest.java @@ -15,15 +15,50 @@ */ package org.onosproject.net.resource.link; +import com.google.common.annotations.Beta; import com.google.common.base.MoreObjects; import org.onosproject.net.resource.ResourceRequest; import org.onosproject.net.resource.ResourceType; +import static com.google.common.base.Preconditions.checkNotNull; + /** * Representation of a request for lambda resource. */ public class LambdaResourceRequest implements ResourceRequest { + private final LambdaResource lambda; + + /** + * Constructs a request specifying the given lambda. + * + * @param lambda lambda to be requested + */ + @Beta + public LambdaResourceRequest(LambdaResource lambda) { + this.lambda = checkNotNull(lambda); + } + + /** + * Constructs a request asking an arbitrary available lambda. + * + * @deprecated in Emu Release + */ + @Deprecated + public LambdaResourceRequest() { + this.lambda = null; + } + + /** + * Returns the lambda this request expects. + * + * @return the lambda this request expects + */ + @Beta + public LambdaResource lambda() { + return lambda; + } + @Override public ResourceType type() { return ResourceType.LAMBDA; @@ -32,6 +67,7 @@ public class LambdaResourceRequest implements ResourceRequest { @Override public String toString() { return MoreObjects.toStringHelper(this) + .add("lambda", lambda) .toString(); } } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/LinkResourceRequest.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/LinkResourceRequest.java index 8023a92e..37622e79 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/LinkResourceRequest.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/LinkResourceRequest.java @@ -16,8 +16,10 @@ package org.onosproject.net.resource.link; import java.util.Collection; +import java.util.Map; import java.util.Set; +import com.google.common.annotations.Beta; import org.onosproject.net.Link; import org.onosproject.net.intent.Constraint; import org.onosproject.net.intent.IntentId; @@ -50,22 +52,62 @@ public interface LinkResourceRequest extends ResourceRequest { Set<ResourceRequest> resources(); /** + * Returns the set of resource request against the specified link. + * + * @param link link whose associated resource request is to be returned + * @return set of resource request against the specified link + */ + @Beta + Set<ResourceRequest> resources(Link link); + + /** * Builder of link resource request. */ interface Builder { - /** + /** * Adds lambda request. * * @return self + * @deprecated in Emu Release */ + @Deprecated Builder addLambdaRequest(); /** - * Adds MPLS request. - * - * @return self - */ - Builder addMplsRequest(); + * Adds lambda request. + * + * @param lambda lambda to be requested + * @return self + */ + @Beta + Builder addLambdaRequest(LambdaResource lambda); + + /** + * Adds MPLS request. + * + * @return self + * @deprecated in Emu Release + */ + @Deprecated + Builder addMplsRequest(); + + /** + * Adds MPLS request. + * + * @param label MPLS label to be requested + * @return self + */ + @Beta + Builder addMplsRequest(MplsLabel label); + + /** + * Adds MPLS request against the specified links. + * + * @param labels MPLS labels to be requested against links + * @return self + */ + @Beta + Builder addMplsRequest(Map<Link, MplsLabel> labels); /** * Adds bandwidth request with bandwidth value. diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/LinkResourceService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/LinkResourceService.java index 6dc04dfc..71ea7e1a 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/LinkResourceService.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/LinkResourceService.java @@ -22,7 +22,10 @@ import org.onosproject.net.resource.ResourceRequest; /** * Service for providing link resource allocation. + * + * @deprecated in Emu Release */ +@Deprecated public interface LinkResourceService extends ListenerService<LinkResourceEvent, LinkResourceListener> { @@ -31,14 +34,18 @@ public interface LinkResourceService * * @param req resources to be allocated * @return allocated resources + * @deprecated in Emu Release */ + @Deprecated LinkResourceAllocations requestResources(LinkResourceRequest req); /** * Releases resources. * * @param allocations resources to be released + * @deprecated in Emu Release */ + @Deprecated void releaseResources(LinkResourceAllocations allocations); /** @@ -47,7 +54,9 @@ public interface LinkResourceService * @param req updated resource request * @param oldAllocations old resource allocations * @return new resource allocations + * @deprecated in Emu Release */ + @Deprecated LinkResourceAllocations updateResources(LinkResourceRequest req, LinkResourceAllocations oldAllocations); @@ -55,7 +64,9 @@ public interface LinkResourceService * Returns all allocated resources. * * @return allocated resources + * @deprecated in Emu Release */ + @Deprecated Iterable<LinkResourceAllocations> getAllocations(); /** @@ -63,7 +74,9 @@ public interface LinkResourceService * * @param link a target link * @return allocated resources + * @deprecated in Emu Release */ + @Deprecated Iterable<LinkResourceAllocations> getAllocations(Link link); /** @@ -71,7 +84,9 @@ public interface LinkResourceService * * @param intentId the target Intent's id * @return allocated resources for Intent + * @deprecated in Emu Release */ + @Deprecated LinkResourceAllocations getAllocations(IntentId intentId); /** @@ -79,7 +94,9 @@ public interface LinkResourceService * * @param link a target link * @return available resources for the target link + * @deprecated in Emu Release */ + @Deprecated Iterable<ResourceRequest> getAvailableResources(Link link); /** @@ -88,7 +105,9 @@ public interface LinkResourceService * @param link a target link * @param allocations allocations to be included as available * @return available resources for the target link + * @deprecated in Emu Release */ + @Deprecated Iterable<ResourceRequest> getAvailableResources(Link link, LinkResourceAllocations allocations); diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/MplsLabelResourceRequest.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/MplsLabelResourceRequest.java index 0a03f450..01a048b7 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/MplsLabelResourceRequest.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/MplsLabelResourceRequest.java @@ -15,15 +15,50 @@ */ package org.onosproject.net.resource.link; +import com.google.common.annotations.Beta; import com.google.common.base.MoreObjects; import org.onosproject.net.resource.ResourceRequest; import org.onosproject.net.resource.ResourceType; +import static com.google.common.base.Preconditions.checkNotNull; + /** * Representation of a request for lambda resource. */ public class MplsLabelResourceRequest implements ResourceRequest { + private final MplsLabel mplsLabel; + + /** + * Constructs a request specifying the given MPLS label. + * + * @param mplsLabel MPLS label to be requested + */ + @Beta + public MplsLabelResourceRequest(MplsLabel mplsLabel) { + this.mplsLabel = checkNotNull(mplsLabel); + } + + /** + * Constructs a request asking an arbitrary available MPLS label. + * + * @deprecated in Emu Release + */ + @Deprecated + public MplsLabelResourceRequest() { + this.mplsLabel = null; + } + + /** + * Returns the MPLS label this request expects. + * + * @return the MPLS label this request expects + */ + @Beta + public MplsLabel mplsLabel() { + return mplsLabel; + } + @Override public ResourceType type() { return ResourceType.MPLS_LABEL; @@ -32,6 +67,7 @@ public class MplsLabelResourceRequest implements ResourceRequest { @Override public String toString() { return MoreObjects.toStringHelper(this) + .add("mplsLabel", mplsLabel) .toString(); } } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/persistence/PersistenceService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/persistence/PersistenceService.java new file mode 100644 index 00000000..09065dec --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/persistence/PersistenceService.java @@ -0,0 +1,40 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.persistence; + +/** + * Service that allows for the creation of local disk backed map for instance specific values that persist across + * restarts. Empty maps and sets are deleted on shutdown. + */ +public interface PersistenceService { + /** + * A builder for the creation of local persistent maps backed by disk. + * + * @param <K> the type of keys in this map + * @param <V> the type of values in this map + * @return a persistent map builder + */ + <K, V> PersistentMapBuilder<K, V> persistentMapBuilder(); + + /** + * A builder for the creation of local persistent sets backed by disk. + * + * @param <E> the type of the elements + * @return a persistent set builder + */ + <E> PersistentSetBuilder<E> persistentSetBuilder(); +}
\ No newline at end of file diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/persistence/PersistentMapBuilder.java b/framework/src/onos/core/api/src/main/java/org/onosproject/persistence/PersistentMapBuilder.java new file mode 100644 index 00000000..c3c855e1 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/persistence/PersistentMapBuilder.java @@ -0,0 +1,49 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.persistence; + + +import org.onosproject.store.service.Serializer; + +import java.util.Map; + +/** + * The interface for a persistent map builder for use with mapDB. + */ +public interface PersistentMapBuilder<K, V> { + + /** + * Sets the name of this map. + * @param name the string name of this map + * @return a persistent map builder with the name option now set + */ + PersistentMapBuilder<K, V> withName(String name); + + /** + * Sets the key serializer to be used to serialize this map, this is a required parameter. + * @param serializer the serializer to be used for keys + * @return a persistent map builder with the key serializer set + */ + PersistentMapBuilder<K, V> withSerializer(Serializer serializer); + + /** + * Validates the map settings and then builds this map in the database. Throws an exception if invalid settings + * are found. + * @return The map that was created + */ + Map<K, V> build(); +}
\ No newline at end of file diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/persistence/PersistentSetBuilder.java b/framework/src/onos/core/api/src/main/java/org/onosproject/persistence/PersistentSetBuilder.java new file mode 100644 index 00000000..851872cf --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/persistence/PersistentSetBuilder.java @@ -0,0 +1,48 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.persistence; + +import org.onosproject.store.service.Serializer; + +import java.util.Set; + +/** + * The default interface for the persistent set builder for use with mapDB. + */ +public interface PersistentSetBuilder<E> { + + /** + * Sets the name of this set. + * @param name the string name of this set + * @return a persistent set builder with the name option now set + */ + PersistentSetBuilder<E> withName(String name); + + /** + * Sets the serializer to be used to serialize this set, this is a required parameter. + * @param serializer the serializer to be used + * @return a persistent set builder with the serializer set + */ + PersistentSetBuilder<E> withSerializer(Serializer serializer); + + /** + * Validates the set settings and then builds this map in the database. Throws an exception if invalid settings + * are found. + * @return The set that was created + */ + Set<E> build(); +}
\ No newline at end of file diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/persistence/package-info.java b/framework/src/onos/core/api/src/main/java/org/onosproject/persistence/package-info.java new file mode 100644 index 00000000..6e11a5e1 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/persistence/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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. + */ + +/** + * Persistence service and builders. + */ +package org.onosproject.persistence; diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/TransactionContext.java b/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/TransactionContext.java index 94942e20..ef972536 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/TransactionContext.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/TransactionContext.java @@ -56,9 +56,9 @@ public interface TransactionContext { * Commits a transaction that was previously started thereby making its changes permanent * and externally visible. * - * @throws TransactionException if transaction fails to commit + * @return true if this transaction succeeded, otherwise false. */ - void commit(); + boolean commit(); /** * Aborts any changes made in this transaction context and discarding all locally cached updates. diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiTopoOverlay.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiTopoOverlay.java index e0d7d239..88796de7 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiTopoOverlay.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/UiTopoOverlay.java @@ -16,6 +16,8 @@ package org.onosproject.ui; +import org.onosproject.net.DeviceId; +import org.onosproject.net.HostId; import org.onosproject.ui.topo.PropertyPanel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -105,8 +107,9 @@ public class UiTopoOverlay { * This default implementation does nothing. * * @param pp property panel model of summary data + * @param deviceId device id */ - public void modifyDeviceDetails(PropertyPanel pp) { + public void modifyDeviceDetails(PropertyPanel pp, DeviceId deviceId) { } /** @@ -115,7 +118,8 @@ public class UiTopoOverlay { * This default implementation does nothing. * * @param pp property panel model of summary data + * @param hostId host id */ - public void modifyHostDetails(PropertyPanel pp) { + public void modifyHostDetails(PropertyPanel pp, HostId hostId) { } } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/AppIdFormatter.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/AppIdFormatter.java index 0e1c248b..f7947a75 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/AppIdFormatter.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/AppIdFormatter.java @@ -27,6 +27,7 @@ public final class AppIdFormatter extends AbstractCellFormatter { // non-instantiable private AppIdFormatter() { } + // NOTE: do not change this format; we parse it on the client side. @Override protected String nonNullFormat(Object value) { ApplicationId appId = (ApplicationId) value; diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeBadge.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeBadge.java index 7b517111..4edb6712 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeBadge.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeBadge.java @@ -40,7 +40,7 @@ public final class NodeBadge { return "{" + code + "}"; } - /** Returns the status code in string form. */ + /* Returns the status code in string form. */ public String code() { return code; } diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/net/intent/HostToHostIntentTest.java b/framework/src/onos/core/api/src/test/java/org/onosproject/net/intent/HostToHostIntentTest.java index 3f7650e4..c3a95473 100644 --- a/framework/src/onos/core/api/src/test/java/org/onosproject/net/intent/HostToHostIntentTest.java +++ b/framework/src/onos/core/api/src/test/java/org/onosproject/net/intent/HostToHostIntentTest.java @@ -16,15 +16,18 @@ package org.onosproject.net.intent; import org.junit.Test; +import org.onlab.util.DataRateUnit; import org.onosproject.TestApplicationId; import org.onosproject.core.ApplicationId; import org.onosproject.net.HostId; import org.onosproject.net.flow.TrafficSelector; - +import org.onosproject.net.intent.constraint.BandwidthConstraint; +import com.google.common.collect.ImmutableList; import com.google.common.testing.EqualsTester; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.is; import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable; import static org.onosproject.net.NetTestTools.hid; @@ -102,6 +105,55 @@ public class HostToHostIntentTest extends IntentTest { .testEquals(); } + @Test + public void testImplicitConstraintsAreAdded() { + final Constraint other = BandwidthConstraint.of(1, DataRateUnit.GBPS); + final HostToHostIntent intent = HostToHostIntent.builder() + .appId(APPID) + .one(id1) + .two(id2) + .selector(selector) + .treatment(treatment) + .constraints(ImmutableList.of(other)) + .build(); + + assertThat(intent.constraints(), hasItem(HostToHostIntent.NOT_OPTICAL)); + } + + @Test + public void testImplicitConstraints() { + final HostToHostIntent implicit = HostToHostIntent.builder() + .appId(APPID) + .one(id1) + .two(id2) + .selector(selector) + .treatment(treatment) + .build(); + final HostToHostIntent empty = HostToHostIntent.builder() + .appId(APPID) + .one(id1) + .two(id2) + .selector(selector) + .treatment(treatment) + .constraints(ImmutableList.of()) + .build(); + final HostToHostIntent exact = HostToHostIntent.builder() + .appId(APPID) + .one(id1) + .two(id2) + .selector(selector) + .treatment(treatment) + .constraints(ImmutableList.of(HostToHostIntent.NOT_OPTICAL)) + .build(); + + new EqualsTester() + .addEqualityGroup(implicit.constraints(), + empty.constraints(), + exact.constraints()) + .testEquals(); + + } + @Override protected Intent createOne() { return HostToHostIntent.builder() diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/net/packet/DefaultPacketRequestTest.java b/framework/src/onos/core/api/src/test/java/org/onosproject/net/packet/DefaultPacketRequestTest.java new file mode 100644 index 00000000..592cd983 --- /dev/null +++ b/framework/src/onos/core/api/src/test/java/org/onosproject/net/packet/DefaultPacketRequestTest.java @@ -0,0 +1,93 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.net.packet; + +import org.junit.Test; +import org.onosproject.core.DefaultApplicationId; +import org.onosproject.net.NetTestTools; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.TrafficSelector; + +import com.google.common.testing.EqualsTester; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable; + +/** + * Unit tests for the DefaultPacketRequest class. + */ +public class DefaultPacketRequestTest { + + private final TrafficSelector selector = DefaultTrafficSelector + .builder() + .matchIcmpCode((byte) 1) + .build(); + + private final DefaultPacketRequest packetRequest1 = + new DefaultPacketRequest(DefaultTrafficSelector.emptySelector(), + PacketPriority.CONTROL, + NetTestTools.APP_ID); + private final DefaultPacketRequest sameAsacketRequest1 = + new DefaultPacketRequest(DefaultTrafficSelector.emptySelector(), + PacketPriority.CONTROL, + NetTestTools.APP_ID); + private final DefaultPacketRequest packetRequest2 = + new DefaultPacketRequest(selector, + PacketPriority.CONTROL, + NetTestTools.APP_ID); + private final DefaultPacketRequest packetRequest3 = + new DefaultPacketRequest(DefaultTrafficSelector.emptySelector(), + PacketPriority.REACTIVE, + NetTestTools.APP_ID); + private final DefaultPacketRequest packetRequest4 = + new DefaultPacketRequest(DefaultTrafficSelector.emptySelector(), + PacketPriority.CONTROL, + new DefaultApplicationId(1, "foo")); + + /** + * Tests the operation of the equals(), toAstring() and hashCode() methods. + */ + @Test + public void testEquals() { + new EqualsTester() + .addEqualityGroup(packetRequest1, sameAsacketRequest1) + .addEqualityGroup(packetRequest2) + .addEqualityGroup(packetRequest3) + .addEqualityGroup(packetRequest4) + .testEquals(); + } + + /** + * Tests that building and fetching from a DefaultPacketRequest is correct. + */ + @Test + public void testConstruction() { + assertThat(packetRequest1.priority(), is(PacketPriority.CONTROL)); + assertThat(packetRequest1.priority().priorityValue(), + is(PacketPriority.CONTROL.priorityValue())); + assertThat(packetRequest1.selector(), is(DefaultTrafficSelector.emptySelector())); + assertThat(packetRequest1.appId(), is(NetTestTools.APP_ID)); + } + + /** + * Checks that the DefaultPacketRequest class is immutable. + */ + @Test + public void testImmutability() { + assertThatClassIsImmutable(DefaultPacketRequest.class); + } +} diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/net/packet/PacketEventTest.java b/framework/src/onos/core/api/src/test/java/org/onosproject/net/packet/PacketEventTest.java new file mode 100644 index 00000000..f0d45f0c --- /dev/null +++ b/framework/src/onos/core/api/src/test/java/org/onosproject/net/packet/PacketEventTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.net.packet; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.is; + +/** + * Unit tests for PacketEvent class. + */ +public class PacketEventTest { + + OutboundPacket packet; + + @Test + public void testConstruction1() { + long time = System.currentTimeMillis(); + PacketEvent event = new PacketEvent(PacketEvent.Type.EMIT, packet); + + assertThat(event.type(), is(PacketEvent.Type.EMIT)); + assertThat(event.subject(), is(packet)); + assertThat(event.time(), greaterThanOrEqualTo(time)); + } + + @Test + public void testConstruction2() { + long time = 12345678; + PacketEvent event = new PacketEvent(PacketEvent.Type.EMIT, packet, time); + + assertThat(event.type(), is(PacketEvent.Type.EMIT)); + assertThat(event.subject(), is(packet)); + assertThat(event.time(), is(time)); + } + +} diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/net/packet/PacketProcessorTest.java b/framework/src/onos/core/api/src/test/java/org/onosproject/net/packet/PacketProcessorTest.java new file mode 100644 index 00000000..7b2ef541 --- /dev/null +++ b/framework/src/onos/core/api/src/test/java/org/onosproject/net/packet/PacketProcessorTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.net.packet; + +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.lessThan; + +/** + * Unit tests for static APIs in the packet processor class. + */ +public class PacketProcessorTest { + + /** + * Tests a priority in the advisor range. + */ + @Test + public void testAdvisorPriorities() { + int advisorPriority = PacketProcessor.advisor(3); + assertThat(advisorPriority, lessThan(PacketProcessor.ADVISOR_MAX)); + assertThat(advisorPriority, greaterThanOrEqualTo(0)); + } + + /** + * Tests a priority in the director range. + */ + @Test + public void testDirectorPriorities() { + int directorPriority = PacketProcessor.director(3); + assertThat(directorPriority, lessThan(PacketProcessor.DIRECTOR_MAX)); + assertThat(directorPriority, greaterThanOrEqualTo(PacketProcessor.ADVISOR_MAX)); + } + + /** + * Tests a priority in the observer range. + */ + @Test + public void testObserverPriorities() { + int observerPriority = PacketProcessor.observer(3); + assertThat(observerPriority, lessThan(PacketProcessor.OBSERVER_MAX)); + assertThat(observerPriority, greaterThanOrEqualTo(PacketProcessor.DIRECTOR_MAX)); + } + +} diff --git a/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java index 4e0f2bd9..69c5e791 100644 --- a/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java +++ b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/DecodeCriterionCodecHelper.java @@ -15,6 +15,8 @@ */ package org.onosproject.codec.impl; +import static org.onlab.util.Tools.nullIsIllegal; + import java.util.HashMap; import java.util.Map; @@ -24,9 +26,13 @@ import org.onlab.packet.MacAddress; import org.onlab.packet.MplsLabel; import org.onlab.packet.TpPort; import org.onlab.packet.VlanId; +import org.onlab.util.HexString; import org.onosproject.net.ChannelSpacing; import org.onosproject.net.GridType; import org.onosproject.net.Lambda; +import org.onosproject.net.OchSignalType; +import org.onosproject.net.OduSignalId; +import org.onosproject.net.OduSignalType; import org.onosproject.net.PortNumber; import org.onosproject.net.flow.criteria.Criteria; import org.onosproject.net.flow.criteria.Criterion; @@ -34,8 +40,6 @@ import org.onosproject.net.flow.criteria.Criterion; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import static org.onlab.util.Tools.nullIsIllegal; - /** * Decode portion of the criterion codec. */ @@ -95,6 +99,8 @@ public final class DecodeCriterionCodecHelper { decoderMap.put(Criterion.Type.OCH_SIGID.name(), new OchSigIdDecoder()); decoderMap.put(Criterion.Type.OCH_SIGTYPE.name(), new OchSigTypeDecoder()); decoderMap.put(Criterion.Type.TUNNEL_ID.name(), new TunnelIdDecoder()); + decoderMap.put(Criterion.Type.ODU_SIGID.name(), new OduSigIdDecoder()); + decoderMap.put(Criterion.Type.ODU_SIGTYPE.name(), new OduSigTypeDecoder()); } private class EthTypeDecoder implements CriterionDecoder { @@ -415,7 +421,9 @@ public final class DecodeCriterionCodecHelper { private class OchSigTypeDecoder implements CriterionDecoder { @Override public Criterion decodeCriterion(ObjectNode json) { - return null; + OchSignalType ochSignalType = OchSignalType.valueOf(nullIsIllegal(json.get(CriterionCodec.OCH_SIGNAL_TYPE), + CriterionCodec.OCH_SIGNAL_TYPE + MISSING_MEMBER_MESSAGE).asText()); + return Criteria.matchOchSignalType(ochSignalType); } } @@ -428,6 +436,34 @@ public final class DecodeCriterionCodecHelper { } } + private class OduSigIdDecoder implements CriterionDecoder { + @Override + public Criterion decodeCriterion(ObjectNode json) { + JsonNode oduSignalId = nullIsIllegal(json.get(CriterionCodec.ODU_SIGNAL_ID), + CriterionCodec.TRIBUTARY_PORT_NUMBER + MISSING_MEMBER_MESSAGE); + + int tributaryPortNumber = nullIsIllegal(oduSignalId.get(CriterionCodec.TRIBUTARY_PORT_NUMBER), + CriterionCodec.TRIBUTARY_PORT_NUMBER + MISSING_MEMBER_MESSAGE).asInt(); + int tributarySlotLen = nullIsIllegal(oduSignalId.get(CriterionCodec.TRIBUTARY_SLOT_LEN), + CriterionCodec.TRIBUTARY_SLOT_LEN + MISSING_MEMBER_MESSAGE).asInt(); + byte[] tributarySlotBitmap = HexString.fromHexString( + nullIsIllegal(oduSignalId.get(CriterionCodec.TRIBUTARY_SLOT_BITMAP), + CriterionCodec.TRIBUTARY_SLOT_BITMAP + MISSING_MEMBER_MESSAGE).asText()); + + return Criteria.matchOduSignalId( + OduSignalId.oduSignalId(tributaryPortNumber, tributarySlotLen, tributarySlotBitmap)); + } + } + + private class OduSigTypeDecoder implements CriterionDecoder { + @Override + public Criterion decodeCriterion(ObjectNode json) { + OduSignalType oduSignalType = OduSignalType.valueOf(nullIsIllegal(json.get(CriterionCodec.ODU_SIGNAL_TYPE), + CriterionCodec.ODU_SIGNAL_TYPE + MISSING_MEMBER_MESSAGE).asText()); + return Criteria.matchOduSignalType(oduSignalType); + } + } + /** * Decodes the JSON into a criterion object. * diff --git a/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java index 6a97a076..14555b3d 100644 --- a/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java +++ b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/DecodeInstructionCodecHelper.java @@ -15,26 +15,29 @@ */ package org.onosproject.codec.impl; +import static org.onlab.util.Tools.nullIsIllegal; + import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; import org.onlab.packet.MplsLabel; import org.onlab.packet.TpPort; import org.onlab.packet.VlanId; +import org.onlab.util.HexString; import org.onosproject.net.ChannelSpacing; import org.onosproject.net.GridType; import org.onosproject.net.Lambda; import org.onosproject.net.OchSignal; +import org.onosproject.net.OduSignalId; import org.onosproject.net.PortNumber; - -import com.fasterxml.jackson.databind.node.ObjectNode; import org.onosproject.net.flow.instructions.Instruction; import org.onosproject.net.flow.instructions.Instructions; import org.onosproject.net.flow.instructions.L0ModificationInstruction; +import org.onosproject.net.flow.instructions.L1ModificationInstruction; import org.onosproject.net.flow.instructions.L2ModificationInstruction; import org.onosproject.net.flow.instructions.L3ModificationInstruction; import org.onosproject.net.flow.instructions.L4ModificationInstruction; -import static org.onlab.util.Tools.nullIsIllegal; +import com.fasterxml.jackson.databind.node.ObjectNode; /** * Decoding portion of the instruction codec. @@ -174,6 +177,30 @@ public final class DecodeInstructionCodecHelper { } /** + * Decodes a Layer 1 instruction. + * + * @return instruction object decoded from the JSON + * @throws IllegalArgumentException if the JSON is invalid + */ + private Instruction decodeL1() { + String subType = json.get(InstructionCodec.SUBTYPE).asText(); + if (subType.equals(L1ModificationInstruction.L1SubType.ODU_SIGID.name())) { + int tributaryPortNumber = nullIsIllegal(json.get(InstructionCodec.TRIBUTARY_PORT_NUMBER), + InstructionCodec.TRIBUTARY_PORT_NUMBER + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt(); + int tributarySlotLen = nullIsIllegal(json.get(InstructionCodec.TRIBUTARY_SLOT_LEN), + InstructionCodec.TRIBUTARY_SLOT_LEN + InstructionCodec.MISSING_MEMBER_MESSAGE).asInt(); + byte[] tributarySlotBitmap = null; + tributarySlotBitmap = HexString.fromHexString( + nullIsIllegal(json.get(InstructionCodec.TRIBUTARY_SLOT_BITMAP), + InstructionCodec.TRIBUTARY_SLOT_BITMAP + InstructionCodec.MISSING_MEMBER_MESSAGE).asText()); + return Instructions.modL1OduSignalId(OduSignalId.oduSignalId(tributaryPortNumber, tributarySlotLen, + tributarySlotBitmap)); + } + throw new IllegalArgumentException("L1 Instruction subtype " + + subType + " is not supported"); + } + + /** * Decodes a Layer 4 instruction. * * @return instruction object decoded from the JSON @@ -221,6 +248,8 @@ public final class DecodeInstructionCodecHelper { return Instructions.createDrop(); } else if (type.equals(Instruction.Type.L0MODIFICATION.name())) { return decodeL0(); + } else if (type.equals(Instruction.Type.L1MODIFICATION.name())) { + return decodeL1(); } else if (type.equals(Instruction.Type.L2MODIFICATION.name())) { return decodeL2(); } else if (type.equals(Instruction.Type.L3MODIFICATION.name())) { diff --git a/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java index f7af736e..8fc6bbcf 100644 --- a/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java +++ b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/EncodeCriterionCodecHelper.java @@ -17,8 +17,10 @@ package org.onosproject.codec.impl; import java.util.EnumMap; +import org.onlab.util.HexString; import org.onosproject.codec.CodecContext; import org.onosproject.net.OchSignal; +import org.onosproject.net.OduSignalId; import org.onosproject.net.flow.criteria.Criterion; import org.onosproject.net.flow.criteria.EthCriterion; import org.onosproject.net.flow.criteria.EthTypeCriterion; @@ -370,12 +372,18 @@ public final class EncodeCriterionCodecHelper { private static class FormatOduSignalId implements CriterionTypeFormatter { @Override public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) { - final OduSignalIdCriterion oduSignalIdCriterion = - (OduSignalIdCriterion) criterion; - return root.put(CriterionCodec.ODU_SIGNAL_ID, oduSignalIdCriterion.oduSignalId().toString()); + OduSignalId oduSignalId = ((OduSignalIdCriterion) criterion).oduSignalId(); + ObjectNode child = root.putObject(CriterionCodec.ODU_SIGNAL_ID); + + child.put(CriterionCodec.TRIBUTARY_PORT_NUMBER, oduSignalId.tributaryPortNumber()); + child.put(CriterionCodec.TRIBUTARY_SLOT_LEN, oduSignalId.tributarySlotLength()); + child.put(CriterionCodec.TRIBUTARY_SLOT_BITMAP, HexString.toHexString(oduSignalId.tributarySlotBitmap())); + + return root; } } + private static class FormatOduSignalType implements CriterionTypeFormatter { @Override public ObjectNode encodeCriterion(ObjectNode root, Criterion criterion) { diff --git a/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java index d12e4ad8..2ec301db 100644 --- a/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java +++ b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java @@ -15,11 +15,14 @@ */ package org.onosproject.codec.impl; +import org.onlab.util.HexString; import org.onosproject.codec.CodecContext; import org.onosproject.net.OchSignal; +import org.onosproject.net.OduSignalId; import org.onosproject.net.flow.instructions.Instruction; import org.onosproject.net.flow.instructions.Instructions; import org.onosproject.net.flow.instructions.L0ModificationInstruction; +import org.onosproject.net.flow.instructions.L1ModificationInstruction; import org.onosproject.net.flow.instructions.L2ModificationInstruction; import org.onosproject.net.flow.instructions.L3ModificationInstruction; import org.onosproject.net.flow.instructions.L4ModificationInstruction; @@ -81,6 +84,36 @@ public final class EncodeInstructionCodecHelper { } /** + * Encode an L1 modification instruction. + * + * @param result json node that the instruction attributes are added to + * @param instruction The L1 instruction + * @param context context of the request + */ + private void encodeL1(ObjectNode result) { + L1ModificationInstruction instruction = + (L1ModificationInstruction) this.instruction; + result.put(InstructionCodec.SUBTYPE, instruction.subtype().name()); + + switch (instruction.subtype()) { + case ODU_SIGID: + final L1ModificationInstruction.ModOduSignalIdInstruction oduSignalIdInstruction = + (L1ModificationInstruction.ModOduSignalIdInstruction) instruction; + OduSignalId oduSignalId = oduSignalIdInstruction.oduSignalId(); + + ObjectNode child = result.putObject("oduSignalId"); + + child.put(InstructionCodec.TRIBUTARY_PORT_NUMBER, oduSignalId.tributaryPortNumber()); + child.put(InstructionCodec.TRIBUTARY_SLOT_LEN, oduSignalId.tributarySlotLength()); + child.put(InstructionCodec.TRIBUTARY_SLOT_BITMAP, HexString.toHexString(oduSignalId.tributarySlotBitmap())); + break; + default: + log.info("Cannot convert L1 subtype of {}", instruction.subtype()); + break; + } + } + + /** * Encode an L2 modification instruction. * * @param result json node that the instruction attributes are added to @@ -222,6 +255,10 @@ public final class EncodeInstructionCodecHelper { encodeL0(result); break; + case L1MODIFICATION: + encodeL1(result); + break; + case L2MODIFICATION: encodeL2(result); break; diff --git a/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java index f4d5008a..d7307ad3 100644 --- a/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java +++ b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/InstructionCodec.java @@ -50,6 +50,9 @@ public final class InstructionCodec extends JsonCodec<Instruction> { protected static final String TUNNEL_ID = "tunnelId"; protected static final String TCP_PORT = "tcpPort"; protected static final String UDP_PORT = "udpPort"; + protected static final String TRIBUTARY_PORT_NUMBER = "tributaryPortNumber"; + protected static final String TRIBUTARY_SLOT_LEN = "tributarySlotLength"; + protected static final String TRIBUTARY_SLOT_BITMAP = "tributarySlotBitmap"; protected static final String MISSING_MEMBER_MESSAGE = " member is required in Instruction"; diff --git a/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java b/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java index 54e1146b..86374f81 100644 --- a/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java +++ b/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java @@ -31,6 +31,8 @@ import org.onosproject.net.ChannelSpacing; import org.onosproject.net.GridType; import org.onosproject.net.Lambda; import org.onosproject.net.OchSignalType; +import org.onosproject.net.OduSignalId; +import org.onosproject.net.OduSignalType; import org.onosproject.net.PortNumber; import org.onosproject.net.flow.criteria.Criteria; import org.onosproject.net.flow.criteria.Criterion; @@ -54,6 +56,10 @@ public class CriterionCodecTest { final IpPrefix ipPrefix6 = IpPrefix.valueOf("fe80::/64"); final MacAddress mac1 = MacAddress.valueOf("00:00:11:00:00:01"); final TpPort tpPort = TpPort.tpPort(40000); + final int tributaryPortNumber = 11; + final int tributarySlotLen = 80; + final byte[] tributarySlotBitmap = new byte[] {1, 2, 3, 4, 2, 3, 4, 2, 3, 4}; + /** * Sets up for each test. Creates a context and fetches the criterion @@ -427,4 +433,31 @@ public class CriterionCodecTest { ObjectNode result = criterionCodec.encode(criterion, context); assertThat(result, matchesCriterion(criterion)); } + + /** + * Tests Odu Signal ID criterion. + */ + @Test + public void matchOduSignalIdTest() { + + OduSignalId oduSignalId = OduSignalId.oduSignalId(tributaryPortNumber, tributarySlotLen, tributarySlotBitmap); + + Criterion criterion = Criteria.matchOduSignalId(oduSignalId); + ObjectNode result = criterionCodec.encode(criterion, context); + assertThat(result, matchesCriterion(criterion)); + } + + /** + * Tests Odu Signal Type criterion. + */ + @Test + public void matchOduSignalTypeTest() { + + OduSignalType signalType = OduSignalType.ODU2; + + Criterion criterion = Criteria.matchOduSignalType(signalType); + ObjectNode result = criterionCodec.encode(criterion, context); + assertThat(result, matchesCriterion(criterion)); + } + } diff --git a/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/CriterionJsonMatcher.java b/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/CriterionJsonMatcher.java index bb3acad5..b00632c3 100644 --- a/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/CriterionJsonMatcher.java +++ b/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/CriterionJsonMatcher.java @@ -15,13 +15,14 @@ */ package org.onosproject.codec.impl; -import com.google.common.base.Joiner; +import java.util.Objects; + import org.hamcrest.Description; import org.hamcrest.TypeSafeDiagnosingMatcher; +import org.onlab.util.HexString; import org.onosproject.net.OchSignal; +import org.onosproject.net.OduSignalId; import org.onosproject.net.flow.criteria.Criterion; - -import com.fasterxml.jackson.databind.JsonNode; import org.onosproject.net.flow.criteria.EthCriterion; import org.onosproject.net.flow.criteria.EthTypeCriterion; import org.onosproject.net.flow.criteria.IPCriterion; @@ -40,6 +41,8 @@ import org.onosproject.net.flow.criteria.MetadataCriterion; import org.onosproject.net.flow.criteria.MplsCriterion; import org.onosproject.net.flow.criteria.OchSignalCriterion; import org.onosproject.net.flow.criteria.OchSignalTypeCriterion; +import org.onosproject.net.flow.criteria.OduSignalIdCriterion; +import org.onosproject.net.flow.criteria.OduSignalTypeCriterion; import org.onosproject.net.flow.criteria.PortCriterion; import org.onosproject.net.flow.criteria.SctpPortCriterion; import org.onosproject.net.flow.criteria.TcpPortCriterion; @@ -47,7 +50,8 @@ import org.onosproject.net.flow.criteria.UdpPortCriterion; import org.onosproject.net.flow.criteria.VlanIdCriterion; import org.onosproject.net.flow.criteria.VlanPcpCriterion; -import java.util.Objects; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.base.Joiner; /** * Hamcrest matcher for criterion objects. @@ -496,6 +500,44 @@ public final class CriterionJsonMatcher extends return true; } + /** + * Matches an ODU signal ID criterion object. + * + * @param criterion criterion to match + * @return true if the JSON matches the criterion, false otherwise. + */ + private boolean matchCriterion(OduSignalIdCriterion criterion) { + final OduSignalId oduSignal = criterion.oduSignalId(); + final JsonNode jsonOduSignal = jsonCriterion.get(CriterionCodec.ODU_SIGNAL_ID); + int jsonTpn = jsonOduSignal.get(CriterionCodec.TRIBUTARY_PORT_NUMBER).intValue(); + int jsonTsLen = jsonOduSignal.get(CriterionCodec.TRIBUTARY_SLOT_LEN).intValue(); + byte[] jsonTributaryBitMap = HexString.fromHexString( + jsonOduSignal.get(CriterionCodec.TRIBUTARY_SLOT_BITMAP).asText()); + OduSignalId jsonOduSignalId = OduSignalId.oduSignalId(jsonTpn, jsonTsLen, jsonTributaryBitMap); + if (!oduSignal.equals(jsonOduSignalId)) { + description.appendText("oduSignalId was " + criterion); + return false; + } + return true; + } + + /** + * Matches an ODU signal Type criterion object. + * + * @param criterion criterion to match + * @return true if the JSON matches the criterion, false otherwise. + */ + private boolean matchCriterion(OduSignalTypeCriterion criterion) { + final String signalType = criterion.signalType().name(); + final String jsonOduSignalType = jsonCriterion.get("oduSignalType").textValue(); + if (!signalType.equals(jsonOduSignalType)) { + description.appendText("signalType was " + signalType); + return false; + } + return true; + } + + @Override public boolean matchesSafely(JsonNode jsonCriterion, Description description) { @@ -594,6 +636,12 @@ public final class CriterionJsonMatcher extends case OCH_SIGTYPE: return matchCriterion((OchSignalTypeCriterion) criterion); + case ODU_SIGID: + return matchCriterion((OduSignalIdCriterion) criterion); + + case ODU_SIGTYPE: + return matchCriterion((OduSignalTypeCriterion) criterion); + default: // Don't know how to format this type description.appendText("unknown criterion type " + diff --git a/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/FlowRuleCodecTest.java b/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/FlowRuleCodecTest.java index 6c88ac1e..f6a92131 100644 --- a/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/FlowRuleCodecTest.java +++ b/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/FlowRuleCodecTest.java @@ -15,6 +15,15 @@ */ package org.onosproject.codec.impl; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.onosproject.net.NetTestTools.APP_ID; + import java.io.IOException; import java.io.InputStream; import java.util.SortedMap; @@ -35,6 +44,8 @@ import org.onosproject.net.ChannelSpacing; import org.onosproject.net.GridType; import org.onosproject.net.Lambda; import org.onosproject.net.OchSignal; +import org.onosproject.net.OchSignalType; +import org.onosproject.net.OduSignalType; import org.onosproject.net.PortNumber; import org.onosproject.net.flow.FlowRule; import org.onosproject.net.flow.criteria.Criterion; @@ -55,6 +66,9 @@ import org.onosproject.net.flow.criteria.Icmpv6TypeCriterion; import org.onosproject.net.flow.criteria.IndexedLambdaCriterion; import org.onosproject.net.flow.criteria.MplsCriterion; import org.onosproject.net.flow.criteria.OchSignalCriterion; +import org.onosproject.net.flow.criteria.OchSignalTypeCriterion; +import org.onosproject.net.flow.criteria.OduSignalIdCriterion; +import org.onosproject.net.flow.criteria.OduSignalTypeCriterion; import org.onosproject.net.flow.criteria.PortCriterion; import org.onosproject.net.flow.criteria.SctpPortCriterion; import org.onosproject.net.flow.criteria.TcpPortCriterion; @@ -62,9 +76,6 @@ import org.onosproject.net.flow.criteria.TunnelIdCriterion; import org.onosproject.net.flow.criteria.UdpPortCriterion; import org.onosproject.net.flow.criteria.VlanIdCriterion; import org.onosproject.net.flow.criteria.VlanPcpCriterion; - -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import org.onosproject.net.flow.instructions.Instruction; import org.onosproject.net.flow.instructions.Instructions; import org.onosproject.net.flow.instructions.L0ModificationInstruction; @@ -72,14 +83,8 @@ import org.onosproject.net.flow.instructions.L2ModificationInstruction; import org.onosproject.net.flow.instructions.L3ModificationInstruction; import org.onosproject.net.flow.instructions.L4ModificationInstruction; -import static org.easymock.EasyMock.createMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.onosproject.net.NetTestTools.APP_ID; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; /** * Flow rule codec unit tests. @@ -382,7 +387,7 @@ public class FlowRuleCodecTest { checkCommonData(rule); - assertThat(rule.selector().criteria().size(), is(33)); + assertThat(rule.selector().criteria().size(), is(36)); rule.selector().criteria() .stream() @@ -518,6 +523,25 @@ public class FlowRuleCodecTest { criterion = getCriterion(Criterion.Type.TUNNEL_ID); assertThat(((TunnelIdCriterion) criterion).tunnelId(), is(100L)); + + criterion = getCriterion(Criterion.Type.OCH_SIGTYPE); + assertThat(((OchSignalTypeCriterion) criterion).signalType(), + is(OchSignalType.FIXED_GRID)); + + criterion = getCriterion(Criterion.Type.ODU_SIGTYPE); + assertThat(((OduSignalTypeCriterion) criterion).signalType(), + is(OduSignalType.ODU4)); + + + criterion = getCriterion(Criterion.Type.ODU_SIGID); + assertThat(((OduSignalIdCriterion) criterion).oduSignalId().tributaryPortNumber(), + is(1)); + + assertThat(((OduSignalIdCriterion) criterion).oduSignalId().tributarySlotLength(), + is(80)); + + assertThat(((OduSignalIdCriterion) criterion).oduSignalId().tributarySlotBitmap(), + is(new byte [] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1})); } /** diff --git a/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/InstructionCodecTest.java b/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/InstructionCodecTest.java index bafbc0f1..f7b0261e 100644 --- a/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/InstructionCodecTest.java +++ b/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/InstructionCodecTest.java @@ -15,6 +15,10 @@ */ package org.onosproject.codec.impl; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.notNullValue; +import static org.onosproject.codec.impl.InstructionJsonMatcher.matchesInstruction; + import org.junit.Before; import org.junit.Test; import org.onlab.packet.Ip4Address; @@ -28,26 +32,23 @@ import org.onosproject.net.ChannelSpacing; import org.onosproject.net.GridType; import org.onosproject.net.IndexedLambda; import org.onosproject.net.Lambda; +import org.onosproject.net.OduSignalId; import org.onosproject.net.PortNumber; import org.onosproject.net.flow.instructions.Instruction; import org.onosproject.net.flow.instructions.Instructions; import org.onosproject.net.flow.instructions.L0ModificationInstruction; +import org.onosproject.net.flow.instructions.L1ModificationInstruction; import org.onosproject.net.flow.instructions.L2ModificationInstruction; import org.onosproject.net.flow.instructions.L3ModificationInstruction; import com.fasterxml.jackson.databind.node.ObjectNode; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.notNullValue; -import static org.onosproject.codec.impl.InstructionJsonMatcher.matchesInstruction; - /** * Unit tests for Instruction codec. */ public class InstructionCodecTest { CodecContext context; JsonCodec<Instruction> instructionCodec; - /** * Sets up for each test. Creates a context and fetches the instruction * codec. @@ -122,6 +123,20 @@ public class InstructionCodecTest { } /** + * Tests the encoding of mod ODU signal ID instructions. + */ + @Test + public void modOduSignalIdInstructionTest() { + OduSignalId oduSignalId = OduSignalId.oduSignalId(1, 8, new byte [] {8, 0, 0, 0, 0, 0, 0, 0, 0, 0}); + L1ModificationInstruction.ModOduSignalIdInstruction instruction = + (L1ModificationInstruction.ModOduSignalIdInstruction) + Instructions.modL1OduSignalId(oduSignalId); + ObjectNode instructionJson = + instructionCodec.encode(instruction, context); + assertThat(instructionJson, matchesInstruction(instruction)); + } + + /** * Tests the encoding of mod ether instructions. */ @Test diff --git a/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java b/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java index c3cdca0f..9ffb3c3a 100644 --- a/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java +++ b/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java @@ -17,15 +17,25 @@ package org.onosproject.codec.impl; import org.hamcrest.Description; import org.hamcrest.TypeSafeDiagnosingMatcher; +import org.onlab.util.HexString; +import org.onosproject.net.OduSignalId; import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flow.instructions.Instructions.DropInstruction; +import org.onosproject.net.flow.instructions.Instructions.NoActionInstruction; +import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; +import org.onosproject.net.flow.instructions.L0ModificationInstruction.ModLambdaInstruction; +import org.onosproject.net.flow.instructions.L0ModificationInstruction.ModOchSignalInstruction; +import org.onosproject.net.flow.instructions.L1ModificationInstruction.ModOduSignalIdInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanPcpInstruction; +import org.onosproject.net.flow.instructions.L2ModificationInstruction.PushHeaderInstructions; +import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction; +import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction; import com.fasterxml.jackson.databind.JsonNode; -import static org.onosproject.net.flow.instructions.Instructions.*; -import static org.onosproject.net.flow.instructions.L0ModificationInstruction.*; -import static org.onosproject.net.flow.instructions.L2ModificationInstruction.*; -import static org.onosproject.net.flow.instructions.L3ModificationInstruction.*; - /** * Hamcrest matcher for instructions. */ @@ -133,7 +143,7 @@ public final class InstructionJsonMatcher extends TypeSafeDiagnosingMatcher<Json } /** - * Matches teh contents of a mod OCh singal instruction. + * Matches the contents of a mod OCh singal instruction. * * @param instructionJson JSON instruction to match * @param description Description object used for recording errors @@ -184,6 +194,40 @@ public final class InstructionJsonMatcher extends TypeSafeDiagnosingMatcher<Json } /** + * Matches the contents of a mod ODU singal Id instruction. + * + * @param instructionJson JSON instruction to match + * @param description Description object used for recording errors + * @return true if contents matches, false otherwise + */ + private boolean matchModOduSingalIdInstruction(JsonNode instructionJson, + Description description) { + ModOduSignalIdInstruction instructionToMatch = + (ModOduSignalIdInstruction) instruction; + String jsonSubType = instructionJson.get("subtype").textValue(); + if (!instructionToMatch.subtype().name().equals(jsonSubType)) { + description.appendText("subtype was " + jsonSubType); + return false; + } + String jsonType = instructionJson.get("type").textValue(); + if (!instructionToMatch.type().name().equals(jsonType)) { + description.appendText("type was " + jsonType); + return false; + } + final JsonNode jsonOduSignal = instructionJson.get("oduSignalId"); + int jsonTpn = jsonOduSignal.get("tributaryPortNumber").intValue(); + int jsonTsLen = jsonOduSignal.get("tributarySlotLength").intValue(); + byte [] tributaryBitMap = HexString.fromHexString(jsonOduSignal.get("tributarySlotBitmap").asText()); + OduSignalId jsonOduSignalId = OduSignalId.oduSignalId(jsonTpn, jsonTsLen, tributaryBitMap); + if (!instructionToMatch.oduSignalId().equals(jsonOduSignalId)) { + description.appendText("oduSignalId was " + instructionToMatch); + return false; + } + return true; + } + + + /** * Matches the contents of a mod Ethernet instruction. * * @param instructionJson JSON instruction to match @@ -416,6 +460,8 @@ public final class InstructionJsonMatcher extends TypeSafeDiagnosingMatcher<Json description); } else if (instruction instanceof ModMplsLabelInstruction) { return matchModMplsLabelInstruction(jsonInstruction, description); + } else if (instruction instanceof ModOduSignalIdInstruction) { + return matchModOduSingalIdInstruction(jsonInstruction, description); } else if (instruction instanceof NoActionInstruction) { return true; } diff --git a/framework/src/onos/core/common/src/test/resources/org/onosproject/codec/impl/criteria-flow.json b/framework/src/onos/core/common/src/test/resources/org/onosproject/codec/impl/criteria-flow.json index 1a96e92f..ccb2e161 100644 --- a/framework/src/onos/core/common/src/test/resources/org/onosproject/codec/impl/criteria-flow.json +++ b/framework/src/onos/core/common/src/test/resources/org/onosproject/codec/impl/criteria-flow.json @@ -38,7 +38,10 @@ {"type":"MPLS_LABEL", "label":123}, {"type":"IPV6_EXTHDR", "exthdrFlags":99}, {"type":"OCH_SIGID", "lambda":122}, - {"type":"TUNNEL_ID", "tunnelId":100} + {"type":"TUNNEL_ID", "tunnelId":100}, + {"type":"OCH_SIGTYPE", "ochSignalType":"FIXED_GRID"}, + {"type":"ODU_SIGTYPE", "oduSignalType":"ODU4"}, + {"type":"ODU_SIGID", "oduSignalId" : {"tributaryPortNumber":1, "tributarySlotLen":80, "tributarySlotBitmap":"01:01:01:01:01:01:01:01:01:01"}} ] } } diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterManager.java index 04d1dfdf..7ddac0ce 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterManager.java @@ -25,17 +25,26 @@ import org.apache.karaf.system.SystemService; import org.joda.time.DateTime; import org.onlab.packet.IpAddress; import org.onosproject.cluster.ClusterAdminService; -import org.onosproject.cluster.ClusterDefinitionService; import org.onosproject.cluster.ClusterEvent; import org.onosproject.cluster.ClusterEventListener; +import org.onosproject.cluster.ClusterMetadata; +import org.onosproject.cluster.ClusterMetadataService; import org.onosproject.cluster.ClusterService; import org.onosproject.cluster.ClusterStore; import org.onosproject.cluster.ClusterStoreDelegate; import org.onosproject.cluster.ControllerNode; import org.onosproject.cluster.NodeId; +import org.onosproject.cluster.Partition; import org.onosproject.event.AbstractListenerManager; import org.slf4j.Logger; +import com.google.common.collect.Lists; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; @@ -44,8 +53,6 @@ import static org.onosproject.security.AppGuard.checkPermission; import static org.slf4j.LoggerFactory.getLogger; import static org.onosproject.security.AppPermission.Type.*; - - /** * Implementation of the cluster service. */ @@ -61,7 +68,7 @@ public class ClusterManager private ClusterStoreDelegate delegate = new InternalStoreDelegate(); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected ClusterDefinitionService clusterDefinitionService; + protected ClusterMetadataService clusterMetadataService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected ClusterStore store; @@ -73,8 +80,9 @@ public class ClusterManager public void activate() { store.setDelegate(delegate); eventDispatcher.addSink(ClusterEvent.class, listenerRegistry); - clusterDefinitionService.seedNodes() - .forEach(node -> store.addNode(node.id(), node.ip(), node.tcpPort())); + clusterMetadataService.getClusterMetadata() + .getNodes() + .forEach(node -> store.addNode(node.id(), node.ip(), node.tcpPort())); log.info("Started"); } @@ -119,11 +127,16 @@ public class ClusterManager } @Override - public void formCluster(Set<ControllerNode> nodes, String ipPrefix) { + public void formCluster(Set<ControllerNode> nodes) { checkNotNull(nodes, "Nodes cannot be null"); checkArgument(!nodes.isEmpty(), "Nodes cannot be empty"); - checkNotNull(ipPrefix, "IP prefix cannot be null"); - clusterDefinitionService.formCluster(nodes, ipPrefix); + + ClusterMetadata metadata = ClusterMetadata.builder() + .withName("default") + .withControllerNodes(nodes) + .withPartitions(buildDefaultPartitions(nodes)) + .build(); + clusterMetadataService.setClusterMetadata(metadata); try { log.warn("Shutting down container for cluster reconfiguration!"); systemService.reboot("now", SystemService.Swipe.NONE); @@ -153,4 +166,21 @@ public class ClusterManager post(event); } } + + private static Collection<Partition> buildDefaultPartitions(Collection<ControllerNode> nodes) { + List<ControllerNode> sorted = new ArrayList<>(nodes); + Collections.sort(sorted, (o1, o2) -> o1.id().toString().compareTo(o2.id().toString())); + Collection<Partition> partitions = Lists.newArrayList(); + + int length = nodes.size(); + int count = 3; + for (int i = 0; i < length; i++) { + Set<NodeId> set = new HashSet<>(count); + for (int j = 0; j < count; j++) { + set.add(sorted.get((i + j) % length).id()); + } + partitions.add(new Partition("p" + (i + 1), set)); + } + return partitions; + } } diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterMetadataManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterMetadataManager.java new file mode 100644 index 00000000..a0f7a833 --- /dev/null +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterMetadataManager.java @@ -0,0 +1,116 @@ +package org.onosproject.cluster.impl; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.slf4j.LoggerFactory.getLogger; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Collection; +import java.util.Enumeration; + +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.packet.IpAddress; +import org.onosproject.cluster.ClusterMetadata; +import org.onosproject.cluster.ClusterMetadataEvent; +import org.onosproject.cluster.ClusterMetadataEventListener; +import org.onosproject.cluster.ClusterMetadataService; +import org.onosproject.cluster.ClusterMetadataStore; +import org.onosproject.cluster.ClusterMetadataStoreDelegate; +import org.onosproject.cluster.ControllerNode; +import org.onosproject.event.AbstractListenerManager; +import org.onosproject.store.service.Versioned; +import org.slf4j.Logger; + +/** + * Implementation of ClusterMetadataService. + */ +@Component(immediate = true) +@Service +public class ClusterMetadataManager + extends AbstractListenerManager<ClusterMetadataEvent, ClusterMetadataEventListener> + implements ClusterMetadataService { + + private ControllerNode localNode; + private final Logger log = getLogger(getClass()); + + private ClusterMetadataStoreDelegate delegate = new InternalStoreDelegate(); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ClusterMetadataStore store; + + @Activate + public void activate() { + store.setDelegate(delegate); + eventDispatcher.addSink(ClusterMetadataEvent.class, listenerRegistry); + establishSelfIdentity(); + log.info("Started"); + } + + @Deactivate + public void deactivate() { + store.unsetDelegate(delegate); + eventDispatcher.removeSink(ClusterMetadataEvent.class); + log.info("Stopped"); + } + + @Override + public ClusterMetadata getClusterMetadata() { + return Versioned.valueOrElse(store.getClusterMetadata(), null); + } + + @Override + public ControllerNode getLocalNode() { + return localNode; + } + + @Override + public void setClusterMetadata(ClusterMetadata metadata) { + checkNotNull(metadata, "Cluster metadata cannot be null"); + store.setClusterMetadata(metadata); + } + + // Store delegate to re-post events emitted from the store. + private class InternalStoreDelegate implements ClusterMetadataStoreDelegate { + @Override + public void notify(ClusterMetadataEvent event) { + post(event); + } + } + + private IpAddress findLocalIp(Collection<ControllerNode> controllerNodes) throws SocketException { + Enumeration<NetworkInterface> interfaces = + NetworkInterface.getNetworkInterfaces(); + while (interfaces.hasMoreElements()) { + NetworkInterface iface = interfaces.nextElement(); + Enumeration<InetAddress> inetAddresses = iface.getInetAddresses(); + while (inetAddresses.hasMoreElements()) { + IpAddress ip = IpAddress.valueOf(inetAddresses.nextElement()); + if (controllerNodes.stream() + .map(ControllerNode::ip) + .anyMatch(nodeIp -> ip.equals(nodeIp))) { + return ip; + } + } + } + throw new IllegalStateException("Unable to determine local ip"); + } + + private void establishSelfIdentity() { + try { + IpAddress ip = findLocalIp(getClusterMetadata().getNodes()); + localNode = getClusterMetadata().getNodes() + .stream() + .filter(node -> node.ip().equals(ip)) + .findFirst() + .get(); + } catch (SocketException e) { + throw new IllegalStateException("Cannot determine local IP", e); + } + } +}
\ No newline at end of file diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java index 19377cf6..8f601497 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java @@ -112,10 +112,10 @@ public final class OpticalPortOperator implements ConfigOperator { return new OduCltPortDescription(port, odu.isEnabled(), odu.signalType(), sa); case PACKET: case FIBER: + case COPPER: return new DefaultPortDescription(port, descr.isEnabled(), descr.type(), descr.portSpeed(), sa); default: - // this includes copper ports. log.warn("Unsupported optical port type {} - can't update", descr.type()); return descr; } diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java index 137aca1e..0a1af6f5 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java @@ -29,9 +29,11 @@ import org.onosproject.net.flow.criteria.VlanPcpCriterion; import org.onosproject.net.flow.criteria.MplsCriterion; import org.onosproject.net.flow.criteria.IPCriterion; import org.onosproject.net.flow.criteria.IPv6FlowLabelCriterion; +import org.onosproject.net.flow.criteria.OduSignalIdCriterion; import org.onosproject.net.flow.criteria.Criteria; import org.onosproject.net.flow.instructions.Instruction; import org.onosproject.net.flow.instructions.L0ModificationInstruction; +import org.onosproject.net.flow.instructions.L1ModificationInstruction; import org.onosproject.net.flow.instructions.L2ModificationInstruction; import org.onosproject.net.flow.instructions.L3ModificationInstruction; import org.onosproject.net.flowobjective.DefaultForwardingObjective; @@ -155,6 +157,7 @@ public final class FlowObjectiveCompositionUtil { return treatmentBuilder.build(); } + //CHECKSTYLE:OFF public static TrafficSelector revertTreatmentSelector(TrafficTreatment trafficTreatment, TrafficSelector trafficSelector) { @@ -195,14 +198,30 @@ public final class FlowObjectiveCompositionUtil { } else { return null; } - } else { - break; } default: break; } break; } + case L1MODIFICATION: { + L1ModificationInstruction l1 = (L1ModificationInstruction) instruction; + switch (l1.subtype()) { + case ODU_SIGID: + if (criterionMap.containsKey(Criterion.Type.ODU_SIGID)) { + if (((OduSignalIdCriterion) criterionMap.get((Criterion.Type.ODU_SIGID))).oduSignalId() + .equals(((L1ModificationInstruction.ModOduSignalIdInstruction) l1) + .oduSignalId())) { + criterionMap.remove(Criterion.Type.ODU_SIGID); + } else { + return null; + } + } + default: + break; + } + break; + } case L2MODIFICATION: { L2ModificationInstruction l2 = (L2ModificationInstruction) instruction; switch (l2.subtype()) { @@ -344,6 +363,7 @@ public final class FlowObjectiveCompositionUtil { return selectorBuilder.build(); } + //CHECKSTYLE:ON public static Set<Criterion.Type> getTypeSet(TrafficSelector trafficSelector) { Set<Criterion.Type> typeSet = new HashSet<>(); diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java index 5fd1c85d..acc5a5d5 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java @@ -24,12 +24,14 @@ import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.onlab.packet.EthType; import org.onlab.packet.Ethernet; +import org.onlab.packet.MplsLabel; import org.onlab.packet.VlanId; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; import org.onosproject.net.Link; +import org.onosproject.net.LinkKey; import org.onosproject.net.PortNumber; import org.onosproject.net.flow.DefaultFlowRule; import org.onosproject.net.flow.DefaultTrafficSelector; @@ -46,24 +48,23 @@ import org.onosproject.net.intent.Intent; import org.onosproject.net.intent.IntentCompiler; import org.onosproject.net.intent.IntentExtensionService; import org.onosproject.net.intent.MplsPathIntent; -import org.onosproject.net.link.LinkStore; -import org.onosproject.net.resource.link.DefaultLinkResourceRequest; +import org.onosproject.net.newresource.ResourcePath; +import org.onosproject.net.newresource.ResourceService; import org.onosproject.net.resource.link.LinkResourceAllocations; -import org.onosproject.net.resource.link.LinkResourceRequest; -import org.onosproject.net.resource.link.LinkResourceService; -import org.onosproject.net.resource.link.MplsLabel; -import org.onosproject.net.resource.link.MplsLabelResourceAllocation; -import org.onosproject.net.resource.ResourceAllocation; -import org.onosproject.net.resource.ResourceType; import org.slf4j.Logger; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkNotNull; +import static org.onosproject.net.LinkKey.linkKey; import static org.slf4j.LoggerFactory.getLogger; @Component(immediate = true) @@ -78,18 +79,15 @@ public class MplsPathIntentCompiler implements IntentCompiler<MplsPathIntent> { protected CoreService coreService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected LinkResourceService resourceService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected LinkStore linkStore; + protected ResourceService resourceService; protected ApplicationId appId; @Override public List<Intent> compile(MplsPathIntent intent, List<Intent> installable, Set<LinkResourceAllocations> resources) { - LinkResourceAllocations allocations = assignMplsLabel(intent); - List<FlowRule> rules = generateRules(intent, allocations); + Map<LinkKey, MplsLabel> labels = assignMplsLabel(intent); + List<FlowRule> rules = generateRules(intent, labels); return Collections.singletonList(new FlowRuleIntent(appId, rules, intent.resources())); } @@ -105,39 +103,60 @@ public class MplsPathIntentCompiler implements IntentCompiler<MplsPathIntent> { intentExtensionService.unregisterCompiler(MplsPathIntent.class); } - private LinkResourceAllocations assignMplsLabel(MplsPathIntent intent) { + private Map<LinkKey, MplsLabel> assignMplsLabel(MplsPathIntent intent) { // TODO: do it better... Suggestions? - Set<Link> linkRequest = Sets.newHashSetWithExpectedSize(intent.path() + Set<LinkKey> linkRequest = Sets.newHashSetWithExpectedSize(intent.path() .links().size() - 2); for (int i = 1; i <= intent.path().links().size() - 2; i++) { - Link link = intent.path().links().get(i); + LinkKey link = linkKey(intent.path().links().get(i)); linkRequest.add(link); // add the inverse link. I want that the label is reserved both for // the direct and inverse link - linkRequest.add(linkStore.getLink(link.dst(), link.src())); + linkRequest.add(linkKey(link.dst(), link.src())); } - LinkResourceRequest.Builder request = DefaultLinkResourceRequest - .builder(intent.id(), linkRequest).addMplsRequest(); - LinkResourceAllocations reqMpls = resourceService - .requestResources(request.build()); - return reqMpls; - } + Map<LinkKey, MplsLabel> labels = findMplsLabels(linkRequest); + if (labels.isEmpty()) { + return Collections.emptyMap(); + } + + List<ResourcePath> resources = labels.entrySet().stream() + .map(x -> new ResourcePath(linkKey(x.getKey().src(), x.getKey().src()), x.getValue())) + .collect(Collectors.toList()); + List<org.onosproject.net.newresource.ResourceAllocation> allocations = + resourceService.allocate(intent.id(), resources); + if (allocations.isEmpty()) { + Collections.emptyMap(); + } - private MplsLabel getMplsLabel(LinkResourceAllocations allocations, Link link) { - for (ResourceAllocation allocation : allocations - .getResourceAllocation(link)) { - if (allocation.type() == ResourceType.MPLS_LABEL) { - return ((MplsLabelResourceAllocation) allocation).mplsLabel(); + return labels; + } + private Map<LinkKey, MplsLabel> findMplsLabels(Set<LinkKey> links) { + Map<LinkKey, MplsLabel> labels = new HashMap<>(); + for (LinkKey link : links) { + Optional<MplsLabel> label = findMplsLabel(link); + if (label.isPresent()) { + labels.put(link, label.get()); } } - log.warn("MPLS label was not assigned successfully"); - return null; + + return labels; + } + + private Optional<MplsLabel> findMplsLabel(LinkKey link) { + return resourceService.getAvailableResources(new ResourcePath(link)).stream() + .filter(x -> x.lastComponent() instanceof MplsLabel) + .map(x -> (MplsLabel) x.lastComponent()) + .findFirst(); + } + + private MplsLabel getMplsLabel(Map<LinkKey, MplsLabel> labels, LinkKey link) { + return labels.get(link); } private List<FlowRule> generateRules(MplsPathIntent intent, - LinkResourceAllocations allocations) { + Map<LinkKey, MplsLabel> labels) { Iterator<Link> links = intent.path().links().iterator(); Link srcLink = links.next(); @@ -149,7 +168,7 @@ public class MplsPathIntentCompiler implements IntentCompiler<MplsPathIntent> { // Ingress traffic // Get the new MPLS label - MplsLabel mpls = getMplsLabel(allocations, link); + MplsLabel mpls = getMplsLabel(labels, linkKey(link)); checkNotNull(mpls); MplsLabel prevLabel = mpls; rules.add(ingressFlow(prev.port(), link, intent, mpls)); @@ -163,7 +182,7 @@ public class MplsPathIntentCompiler implements IntentCompiler<MplsPathIntent> { if (links.hasNext()) { // Transit traffic // Get the new MPLS label - mpls = getMplsLabel(allocations, link); + mpls = getMplsLabel(labels, linkKey(link)); checkNotNull(mpls); rules.add(transitFlow(prev.port(), link, intent, prevLabel, mpls)); @@ -181,7 +200,8 @@ public class MplsPathIntentCompiler implements IntentCompiler<MplsPathIntent> { } private FlowRule ingressFlow(PortNumber inPort, Link link, - MplsPathIntent intent, MplsLabel label) { + MplsPathIntent intent, + MplsLabel label) { TrafficSelector.Builder ingressSelector = DefaultTrafficSelector .builder(intent.selector()); @@ -193,10 +213,10 @@ public class MplsPathIntentCompiler implements IntentCompiler<MplsPathIntent> { .matchMplsLabel(intent.ingressLabel().get()); // Swap the MPLS label - treat.setMpls(label.label()); + treat.setMpls(label); } else { // Push and set the MPLS label - treat.pushMpls().setMpls(label.label()); + treat.pushMpls().setMpls(label); } // Add the output action treat.setOutput(link.src().port()); @@ -205,21 +225,21 @@ public class MplsPathIntentCompiler implements IntentCompiler<MplsPathIntent> { } private FlowRule transitFlow(PortNumber inPort, Link link, - MplsPathIntent intent, - MplsLabel prevLabel, - MplsLabel outLabel) { + MplsPathIntent intent, + MplsLabel prevLabel, + MplsLabel outLabel) { // Ignore the ingress Traffic Selector and use only the MPLS label // assigned in the previous link TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); selector.matchInPort(inPort).matchEthType(Ethernet.MPLS_UNICAST) - .matchMplsLabel(prevLabel.label()); + .matchMplsLabel(prevLabel); TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder(); // Set the new label only if the label on the packet is // different if (!prevLabel.equals(outLabel)) { - treat.setMpls(outLabel.label()); + treat.setMpls(outLabel); } treat.setOutput(link.src().port()); @@ -227,14 +247,14 @@ public class MplsPathIntentCompiler implements IntentCompiler<MplsPathIntent> { } private FlowRule egressFlow(PortNumber inPort, Link link, - MplsPathIntent intent, - MplsLabel prevLabel) { + MplsPathIntent intent, + MplsLabel prevLabel) { // egress point: either set the egress MPLS label or pop the // MPLS label based on the intent annotations TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); selector.matchInPort(inPort).matchEthType(Ethernet.MPLS_UNICAST) - .matchMplsLabel(prevLabel.label()); + .matchMplsLabel(prevLabel); // apply the intent's treatments TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder(intent diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java index c6eb7c5a..fce8498c 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java @@ -17,6 +17,7 @@ package org.onosproject.net.intent.impl.compiler; import org.apache.commons.lang3.tuple.Pair; 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.Modified; import org.apache.felix.scr.annotations.Property; @@ -70,7 +71,7 @@ import static com.google.common.base.Preconditions.checkArgument; * An intent compiler for {@link org.onosproject.net.intent.OpticalCircuitIntent}. */ // For now, remove component designation until dependency on the new resource manager is available. -// @Component(immediate = true) +@Component(immediate = true) public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircuitIntent> { private static final Logger log = LoggerFactory.getLogger(OpticalCircuitIntentCompiler.class); diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java index eb5b4af8..d6725b7c 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java @@ -16,7 +16,11 @@ package org.onosproject.net.intent.impl.compiler; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +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; @@ -24,6 +28,7 @@ import org.onlab.util.Frequency; import org.onosproject.net.AnnotationKeys; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; +import org.onosproject.net.IndexedLambda; import org.onosproject.net.Link; import org.onosproject.net.OchPort; import org.onosproject.net.OchSignal; @@ -38,32 +43,29 @@ import org.onosproject.net.intent.IntentExtensionService; import org.onosproject.net.intent.OpticalConnectivityIntent; import org.onosproject.net.intent.OpticalPathIntent; import org.onosproject.net.intent.impl.IntentCompilationException; +import org.onosproject.net.newresource.ResourceAllocation; import org.onosproject.net.newresource.ResourcePath; import org.onosproject.net.newresource.ResourceService; -import org.onosproject.net.resource.ResourceType; -import org.onosproject.net.resource.link.DefaultLinkResourceRequest; -import org.onosproject.net.resource.link.LambdaResource; -import org.onosproject.net.resource.link.LambdaResourceAllocation; import org.onosproject.net.resource.link.LinkResourceAllocations; -import org.onosproject.net.resource.link.LinkResourceRequest; -import org.onosproject.net.resource.link.LinkResourceService; import org.onosproject.net.topology.LinkWeight; import org.onosproject.net.topology.Topology; import org.onosproject.net.topology.TopologyService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; +import static org.onosproject.net.LinkKey.linkKey; /** * An intent compiler for {@link org.onosproject.net.intent.OpticalConnectivityIntent}. */ // For now, remove component designation until dependency on the new resource manager is available. -// @Component(immediate = true) +@Component(immediate = true) public class OpticalConnectivityIntentCompiler implements IntentCompiler<OpticalConnectivityIntent> { protected static final Logger log = LoggerFactory.getLogger(OpticalConnectivityIntentCompiler.class); @@ -80,9 +82,6 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected ResourceService resourceService; - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected LinkResourceService linkResourceService; - @Activate public void activate() { intentManager.registerCompiler(OpticalConnectivityIntent.class, this); @@ -138,13 +137,12 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical ochSignal = srcOchPort.lambda(); } else { // Request and reserve lambda on path - LinkResourceAllocations linkAllocs = assignWavelength(intent, path); - if (linkAllocs == null) { + IndexedLambda lambda = assignWavelength(intent, path); + if (lambda == null) { continue; } - LambdaResourceAllocation lambdaAlloc = getWavelength(path, linkAllocs); OmsPort omsPort = (OmsPort) deviceService.getPort(path.src().deviceId(), path.src().port()); - ochSignal = new OchSignal(lambdaAlloc.lambda().toInt(), omsPort.maxFrequency(), omsPort.grid()); + ochSignal = new OchSignal((int) lambda.index(), omsPort.maxFrequency(), omsPort.grid()); } // Create installable optical path intent @@ -171,72 +169,46 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical } /** - * Find the lambda allocated to the path. - * - * @param path the path - * @param linkAllocs the link allocations - * @return lambda allocated to the given path - */ - private LambdaResourceAllocation getWavelength(Path path, LinkResourceAllocations linkAllocs) { - return path.links().stream() - .flatMap(x -> linkAllocs.getResourceAllocation(x).stream()) - .filter(x -> x.type() == ResourceType.LAMBDA) - .findFirst() - .map(x -> (LambdaResourceAllocation) x) - .orElse(null); - } - - /** * Request and reserve first available wavelength across path. * * @param path path in WDM topology - * @return first available lambda resource allocation + * @return first available lambda allocated */ - private LinkResourceAllocations assignWavelength(Intent intent, Path path) { - LinkResourceRequest.Builder request = - DefaultLinkResourceRequest.builder(intent.id(), path.links()) - .addLambdaRequest(); - - LinkResourceAllocations allocations = linkResourceService.requestResources(request.build()); - - if (!checkWavelengthContinuity(allocations, path)) { - linkResourceService.releaseResources(allocations); + private IndexedLambda assignWavelength(Intent intent, Path path) { + Set<IndexedLambda> lambdas = findCommonLambdasOverLinks(path.links()); + if (lambdas.isEmpty()) { return null; } - return allocations; - } + IndexedLambda minLambda = findFirstLambda(lambdas); + List<ResourcePath> lambdaResources = path.links().stream() + .map(x -> new ResourcePath(linkKey(x.src(), x.dst()))) + .map(x -> ResourcePath.child(x, minLambda)) + .collect(Collectors.toList()); - /** - * Checks wavelength continuity constraint across path, i.e., an identical lambda is used on all links. - * @return true if wavelength continuity is met, false otherwise - */ - private boolean checkWavelengthContinuity(LinkResourceAllocations allocations, Path path) { - if (allocations == null) { - return false; + List<ResourceAllocation> allocations = resourceService.allocate(intent.id(), lambdaResources); + if (allocations.isEmpty()) { + log.info("Resource allocation for {} failed (resource request: {})", intent, lambdaResources); } - List<LambdaResource> lambdas = path.links().stream() - .flatMap(x -> allocations.getResourceAllocation(x).stream()) - .filter(x -> x.type() == ResourceType.LAMBDA) - .map(x -> ((LambdaResourceAllocation) x).lambda()) - .collect(Collectors.toList()); + return minLambda; + } - LambdaResource lambda = null; - for (LambdaResource nextLambda: lambdas) { - if (nextLambda == null) { - return false; - } - if (lambda == null) { - lambda = nextLambda; - continue; - } - if (!lambda.equals(nextLambda)) { - return false; - } - } + private Set<IndexedLambda> findCommonLambdasOverLinks(List<Link> links) { + return links.stream() + .map(x -> new ResourcePath(linkKey(x.src(), x.dst()))) + .map(resourceService::getAvailableResources) + .map(x -> Iterables.filter(x, r -> r.lastComponent() instanceof IndexedLambda)) + .map(x -> Iterables.transform(x, r -> (IndexedLambda) r.lastComponent())) + .map(x -> (Set<IndexedLambda>) ImmutableSet.copyOf(x)) + .reduce(Sets::intersection) + .orElse(Collections.emptySet()); + } - return true; + private IndexedLambda findFirstLambda(Set<IndexedLambda> lambdas) { + return lambdas.stream() + .findFirst() + .get(); } private ConnectPoint staticPort(ConnectPoint connectPoint) { diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java index 5226967f..10fe75ea 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java @@ -41,7 +41,7 @@ import static com.google.common.base.Preconditions.checkNotNull; /** * An implementation of ResourceService. */ -@Component(immediate = true, enabled = false) +@Component(immediate = true) @Service @Beta public final class ResourceManager implements ResourceService, ResourceAdminService { @@ -127,6 +127,17 @@ public final class ResourceManager implements ResourceService, ResourceAdminServ } @Override + public Collection<ResourcePath> getAvailableResources(ResourcePath parent) { + checkNotNull(parent); + + Collection<ResourcePath> children = store.getChildResources(parent); + return children.stream() + // We access store twice in this method, then the store may be updated by others + .filter(x -> !store.getConsumer(x).isPresent()) + .collect(Collectors.toList()); + } + + @Override public boolean isAvailable(ResourcePath resource) { checkNotNull(resource); diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/LinkResourceManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/LinkResourceManager.java index 8b9952ed..7eb189e5 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/LinkResourceManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/LinkResourceManager.java @@ -31,7 +31,6 @@ import org.onosproject.net.resource.ResourceType; import org.onosproject.net.resource.link.BandwidthResourceAllocation; import org.onosproject.net.resource.link.BandwidthResourceRequest; import org.onosproject.net.resource.link.DefaultLinkResourceAllocations; -import org.onosproject.net.resource.link.LambdaResource; import org.onosproject.net.resource.link.LambdaResourceAllocation; import org.onosproject.net.resource.link.LambdaResourceRequest; import org.onosproject.net.resource.link.LinkResourceAllocations; @@ -46,15 +45,12 @@ import org.onosproject.net.resource.link.MplsLabelResourceAllocation; import org.onosproject.net.resource.link.MplsLabelResourceRequest; import org.slf4j.Logger; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.Map; +import java.util.Optional; import java.util.Set; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; import static org.onosproject.security.AppGuard.checkPermission; import static org.slf4j.LoggerFactory.getLogger; import static org.onosproject.security.AppPermission.Type.*; @@ -86,67 +82,6 @@ public class LinkResourceManager log.info("Stopped"); } - /** - * Returns available lambdas on specified link. - * - * @param link the link - * @return available lambdas on specified link - */ - private Set<LambdaResource> getAvailableLambdas(Link link) { - checkNotNull(link); - Set<ResourceAllocation> resAllocs = store.getFreeResources(link); - if (resAllocs == null) { - return Collections.emptySet(); - } - Set<LambdaResource> lambdas = new HashSet<>(); - for (ResourceAllocation res : resAllocs) { - if (res.type() == ResourceType.LAMBDA) { - lambdas.add(((LambdaResourceAllocation) res).lambda()); - } - } - return lambdas; - } - - - /** - * Returns available lambdas on specified links. - * - * @param links the links - * @return available lambdas on specified links - */ - private Iterable<LambdaResource> getAvailableLambdas(Iterable<Link> links) { - checkNotNull(links); - Iterator<Link> i = links.iterator(); - checkArgument(i.hasNext()); - Set<LambdaResource> lambdas = new HashSet<>(getAvailableLambdas(i.next())); - while (i.hasNext()) { - lambdas.retainAll(getAvailableLambdas(i.next())); - } - return lambdas; - } - - - /** - * Returns available MPLS label on specified link. - * - * @param link the link - * @return available MPLS labels on specified link - */ - private Iterable<MplsLabel> getAvailableMplsLabels(Link link) { - Set<ResourceAllocation> resAllocs = store.getFreeResources(link); - if (resAllocs == null) { - return Collections.emptySet(); - } - Set<MplsLabel> mplsLabels = new HashSet<>(); - for (ResourceAllocation res : resAllocs) { - if (res.type() == ResourceType.MPLS_LABEL) { - - mplsLabels.add(((MplsLabelResourceAllocation) res).mplsLabel()); - } - } - - return mplsLabels; - } @Override public LinkResourceAllocations requestResources(LinkResourceRequest req) { @@ -164,26 +99,23 @@ public class LinkResourceManager allocs.add(new BandwidthResourceAllocation(br.bandwidth())); break; case LAMBDA: - Iterator<LambdaResource> lambdaIterator = - getAvailableLambdas(req.links()).iterator(); - if (lambdaIterator.hasNext()) { - allocs.add(new LambdaResourceAllocation(lambdaIterator.next())); - } else { - log.info("Failed to allocate lambda resource."); - return null; - } + LambdaResourceRequest lr = (LambdaResourceRequest) r; + allocs.add(new LambdaResourceAllocation(lr.lambda())); break; case MPLS_LABEL: for (Link link : req.links()) { if (allocsPerLink.get(link) == null) { allocsPerLink.put(link, new HashSet<>()); } - Iterator<MplsLabel> mplsIter = getAvailableMplsLabels(link) - .iterator(); - if (mplsIter.hasNext()) { - allocsPerLink.get(link) - .add(new MplsLabelResourceAllocation(mplsIter - .next())); + + Optional<MplsLabel> label = req.resources(link).stream() + .filter(x -> x.type() == ResourceType.MPLS_LABEL) + .map(x -> (MplsLabelResourceRequest) x) + .map(MplsLabelResourceRequest::mplsLabel) + .findFirst(); + + if (label.isPresent()) { + allocsPerLink.get(link).add(new MplsLabelResourceAllocation(label.get())); } else { log.info("Failed to allocate MPLS resource."); break; @@ -258,10 +190,12 @@ public class LinkResourceManager ((BandwidthResourceAllocation) alloc).bandwidth())); break; case LAMBDA: - result.add(new LambdaResourceRequest()); + result.add(new LambdaResourceRequest( + ((LambdaResourceAllocation) alloc).lambda())); break; case MPLS_LABEL: - result.add(new MplsLabelResourceRequest()); + result.add(new MplsLabelResourceRequest( + ((MplsLabelResourceAllocation) alloc).mplsLabel())); break; default: break; diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MockResourceService.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MockResourceService.java new file mode 100644 index 00000000..06b2c81e --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MockResourceService.java @@ -0,0 +1,100 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.net.intent.impl.compiler; + +import com.google.common.collect.ImmutableList; +import org.onlab.packet.MplsLabel; +import org.onosproject.net.newresource.ResourceAllocation; +import org.onosproject.net.newresource.ResourceConsumer; +import org.onosproject.net.newresource.ResourcePath; +import org.onosproject.net.newresource.ResourceService; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +class MockResourceService implements ResourceService { + + private final Map<ResourcePath, ResourceConsumer> assignment = new HashMap<>(); + + @Override + public List<ResourceAllocation> allocate(ResourceConsumer consumer, List<ResourcePath> resources) { + assignment.putAll( + resources.stream().collect(Collectors.toMap(x -> x, x -> consumer)) + ); + + return resources.stream() + .map(x -> new ResourceAllocation(x, consumer)) + .collect(Collectors.toList()); + } + + @Override + public boolean release(List<ResourceAllocation> allocations) { + allocations.forEach(x -> assignment.remove(x.resource())); + + return true; + } + + @Override + public boolean release(ResourceConsumer consumer) { + List<ResourcePath> resources = assignment.entrySet().stream() + .filter(x -> x.getValue().equals(consumer)) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + List<ResourceAllocation> allocations = resources.stream() + .map(x -> new ResourceAllocation(x, consumer)) + .collect(Collectors.toList()); + + return release(allocations); + } + + @Override + public Optional<ResourceAllocation> getResourceAllocation(ResourcePath resource) { + return Optional.ofNullable(assignment.get(resource)) + .map(x -> new ResourceAllocation(resource, x)); + } + + @Override + public <T> Collection<ResourceAllocation> getResourceAllocations(ResourcePath parent, Class<T> cls) { + return assignment.entrySet().stream() + .filter(x -> x.getKey().parent().isPresent()) + .filter(x -> x.getKey().parent().get().equals(parent)) + .map(x -> new ResourceAllocation(x.getKey(), x.getValue())) + .collect(Collectors.toList()); + } + + @Override + public Collection<ResourceAllocation> getResourceAllocations(ResourceConsumer consumer) { + return assignment.entrySet().stream() + .filter(x -> x.getValue().equals(consumer)) + .map(x -> new ResourceAllocation(x.getKey(), x.getValue())) + .collect(Collectors.toList()); + } + + @Override + public Collection<ResourcePath> getAvailableResources(ResourcePath parent) { + ResourcePath resource = ResourcePath.child(parent, MplsLabel.mplsLabel(10)); + return ImmutableList.of(resource); + } + + @Override + public boolean isAvailable(ResourcePath resource) { + return true; + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompilerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompilerTest.java index 771a9883..6cceee12 100644 --- a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompilerTest.java +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompilerTest.java @@ -41,10 +41,8 @@ import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.intent.FlowRuleIntent; import org.onosproject.net.intent.Intent; import org.onosproject.net.intent.IntentExtensionService; -import org.onosproject.net.intent.IntentTestsMocks; import org.onosproject.net.intent.MockIdGenerator; import org.onosproject.net.intent.MplsPathIntent; -import org.onosproject.store.trivial.SimpleLinkStore; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; @@ -52,6 +50,7 @@ import static org.easymock.EasyMock.replay; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; +import static org.onosproject.net.DefaultEdgeLink.createEdgeLink; import static org.onosproject.net.Link.Type.DIRECT; import static org.onosproject.net.NetTestTools.APP_ID; import static org.onosproject.net.NetTestTools.PID; @@ -61,10 +60,12 @@ public class MplsPathIntentCompilerTest { private final ApplicationId appId = new TestApplicationId("test"); + private final ConnectPoint d1pi = connectPoint("s1", 100); private final ConnectPoint d1p1 = connectPoint("s1", 0); private final ConnectPoint d2p0 = connectPoint("s2", 0); private final ConnectPoint d2p1 = connectPoint("s2", 1); private final ConnectPoint d3p1 = connectPoint("s3", 1); + private final ConnectPoint d3pe = connectPoint("s3", 100); private final TrafficSelector selector = DefaultTrafficSelector.builder().build(); private final TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); @@ -75,8 +76,10 @@ public class MplsPathIntentCompilerTest { Optional.of(MplsLabel.mplsLabel(20)); private final List<Link> links = Arrays.asList( + createEdgeLink(d1pi, true), new DefaultLink(PID, d1p1, d2p0, DIRECT), - new DefaultLink(PID, d2p1, d3p1, DIRECT) + new DefaultLink(PID, d2p1, d3p1, DIRECT), + createEdgeLink(d3pe, false) ); private IdGenerator idGenerator = new MockIdGenerator(); @@ -92,8 +95,7 @@ public class MplsPathIntentCompilerTest { expect(coreService.registerApplication("org.onosproject.net.intent")) .andReturn(appId); sut.coreService = coreService; - sut.linkStore = new SimpleLinkStore(); - sut.resourceService = new IntentTestsMocks.MockResourceService(); + sut.resourceService = new MockResourceService(); Intent.bindIdGenerator(idGenerator); @@ -128,7 +130,7 @@ public class MplsPathIntentCompilerTest { assertThat(compiled, hasSize(1)); Collection<FlowRule> rules = ((FlowRuleIntent) compiled.get(0)).flowRules(); - assertThat(rules, hasSize(1)); + assertThat(rules, hasSize(3)); FlowRule rule = rules.stream() .filter(x -> x.deviceId().equals(d2p0.deviceId())) @@ -139,4 +141,5 @@ public class MplsPathIntentCompilerTest { sut.deactivate(); } + } diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java index 1a160d98..3e806a73 100644 --- a/framework/src/onos/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java @@ -21,11 +21,19 @@ import org.junit.Before; import org.junit.Test; import org.onlab.packet.ARP; import org.onlab.packet.Ethernet; +import org.onlab.packet.ICMP6; +import org.onlab.packet.IPacket; +import org.onlab.packet.IPv6; import org.onlab.packet.Ip4Address; import org.onlab.packet.Ip4Prefix; +import org.onlab.packet.Ip6Address; +import org.onlab.packet.Ip6Prefix; import org.onlab.packet.IpPrefix; import org.onlab.packet.MacAddress; import org.onlab.packet.VlanId; +import org.onlab.packet.ndp.NeighborAdvertisement; +import org.onlab.packet.ndp.NeighborDiscoveryOptions; +import org.onlab.packet.ndp.NeighborSolicitation; import org.onosproject.incubator.net.intf.Interface; import org.onosproject.incubator.net.intf.InterfaceService; import org.onosproject.net.ConnectPoint; @@ -66,9 +74,13 @@ import static org.easymock.EasyMock.anyObject; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; +import static org.hamcrest.Matchers.anyOf; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** @@ -83,6 +95,8 @@ public class ProxyArpManagerTest { private static final Ip4Address IP1 = Ip4Address.valueOf("192.168.1.1"); private static final Ip4Address IP2 = Ip4Address.valueOf("192.168.1.2"); + private static final Ip6Address IP3 = Ip6Address.valueOf("1000::1"); + private static final Ip6Address IP4 = Ip6Address.valueOf("1000::2"); private static final ProviderId PID = new ProviderId("of", "foo"); @@ -90,8 +104,14 @@ public class ProxyArpManagerTest { private static final VlanId VLAN2 = VlanId.vlanId((short) 2); private static final MacAddress MAC1 = MacAddress.valueOf("00:00:11:00:00:01"); private static final MacAddress MAC2 = MacAddress.valueOf("00:00:22:00:00:02"); + private static final MacAddress MAC3 = MacAddress.valueOf("00:00:33:00:00:03"); + private static final MacAddress MAC4 = MacAddress.valueOf("00:00:44:00:00:04"); + private static final MacAddress SOLICITED_MAC3 = MacAddress.valueOf("33:33:FF:00:00:01"); private static final HostId HID1 = HostId.hostId(MAC1, VLAN1); private static final HostId HID2 = HostId.hostId(MAC2, VLAN1); + private static final HostId HID3 = HostId.hostId(MAC3, VLAN1); + private static final HostId HID4 = HostId.hostId(MAC4, VLAN1); + private static final HostId SOLICITED_HID3 = HostId.hostId(SOLICITED_MAC3, VLAN1); private static final DeviceId DID1 = getDeviceId(1); private static final DeviceId DID2 = getDeviceId(2); @@ -222,21 +242,29 @@ public class ProxyArpManagerTest { for (int i = 1; i <= NUM_ADDRESS_PORTS; i++) { ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1); - Ip4Prefix prefix1 = - Ip4Prefix.valueOf("10.0." + (2 * i - 1) + ".0/24"); - Ip4Address addr1 = - Ip4Address.valueOf("10.0." + (2 * i - 1) + ".1"); + + // Interface address for IPv4 + Ip4Prefix prefix1 = Ip4Prefix.valueOf("10.0." + (2 * i - 1) + ".0/24"); + Ip4Address addr1 = Ip4Address.valueOf("10.0." + (2 * i - 1) + ".1"); Ip4Prefix prefix2 = Ip4Prefix.valueOf("10.0." + (2 * i) + ".0/24"); Ip4Address addr2 = Ip4Address.valueOf("10.0." + (2 * i) + ".1"); InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1); InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2); - Interface intf1 = new Interface(cp, Sets.newHashSet(ia1), + + // Interface address for IPv6 + Ip6Prefix prefix3 = Ip6Prefix.valueOf((2 * i - 1) + "000::0/64"); + Ip6Address addr3 = Ip6Address.valueOf((2 * i - 1) + "000::1"); + Ip6Prefix prefix4 = Ip6Prefix.valueOf((2 * i) + "000::0/64"); + Ip6Address addr4 = Ip6Address.valueOf((2 * i) + "000::1"); + InterfaceIpAddress ia3 = new InterfaceIpAddress(addr3, prefix3); + InterfaceIpAddress ia4 = new InterfaceIpAddress(addr4, prefix4); + + Interface intf1 = new Interface(cp, Sets.newHashSet(ia1, ia3), MacAddress.valueOf(2 * i - 1), VlanId.vlanId((short) 1)); - Interface intf2 = new Interface(cp, Sets.newHashSet(ia2), + Interface intf2 = new Interface(cp, Sets.newHashSet(ia2, ia4), MacAddress.valueOf(2 * i), VlanId.NONE); - interfaces.add(intf1); interfaces.add(intf2); @@ -321,6 +349,41 @@ public class ProxyArpManagerTest { /** * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the + * destination host is known. + * Verifies the correct NDP reply is sent out the correct port. + */ + @Test + public void testReplyKnownIpv6() { + //Set the return value of isEdgePoint from the edgemanager. + isEdgePointReturn = true; + + Host replyer = new DefaultHost(PID, HID3, MAC3, VLAN1, getLocation(4), + Collections.singleton(IP3)); + + Host requestor = new DefaultHost(PID, HID4, MAC4, VLAN1, getLocation(5), + Collections.singleton(IP4)); + + expect(hostService.getHostsByIp(IP3)) + .andReturn(Collections.singleton(replyer)); + expect(hostService.getHost(HID4)).andReturn(requestor); + + replay(hostService); + replay(interfaceService); + + Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC4, SOLICITED_MAC3, + IP4, IP3); + + proxyArp.reply(ndpRequest, getLocation(5)); + + assertEquals(1, packetService.packets.size()); + Ethernet ndpReply = buildNDP(ICMP6.NEIGHBOR_ADVERTISEMENT, + MAC3, MAC4, IP3, IP4); + verifyPacketOut(ndpReply, getLocation(5), packetService.packets.get(0)); + } + + /** + * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the * destination host is not known. * Verifies the ARP request is flooded out the correct edge ports. */ @@ -337,7 +400,6 @@ public class ProxyArpManagerTest { .andReturn(Collections.emptySet()); expect(hostService.getHost(HID2)).andReturn(requestor); - replay(hostService); replay(interfaceService); @@ -355,6 +417,41 @@ public class ProxyArpManagerTest { /** * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the + * destination host is not known. + * Verifies the NDP request is flooded out the correct edge ports. + */ + @Test + public void testReplyUnknownIpv6() { + isEdgePointReturn = true; + + Host requestor = new DefaultHost(PID, HID4, MAC4, VLAN1, getLocation(5), + Collections.singleton(IP4)); + + expect(hostService.getHostsByIp(IP3)) + .andReturn(Collections.emptySet()); + expect(interfaceService.getInterfacesByIp(IP4)) + .andReturn(Collections.emptySet()); + expect(hostService.getHost(HID4)).andReturn(requestor); + + replay(hostService); + replay(interfaceService); + + Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC4, SOLICITED_MAC3, + IP4, IP3); + + //Setup the set of edge ports to be used in the reply method + getEdgePointsNoArg = Lists.newLinkedList(); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("5"), PortNumber.portNumber(1))); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("4"), PortNumber.portNumber(1))); + + proxyArp.reply(ndpRequest, getLocation(6)); + + verifyFlood(ndpRequest); + } + + /** + * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the * destination host is known for that IP address, but is not on the same * VLAN as the source host. * Verifies the ARP request is flooded out the correct edge ports. @@ -388,6 +485,46 @@ public class ProxyArpManagerTest { verifyFlood(arpRequest); } + /** + * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the + * destination host is known for that IP address, but is not on the same + * VLAN as the source host. + * Verifies the NDP request is flooded out the correct edge ports. + */ + @Test + public void testReplyDifferentVlanIpv6() { + + Host replyer = new DefaultHost(PID, HID3, MAC3, VLAN2, getLocation(4), + Collections.singleton(IP3)); + + Host requestor = new DefaultHost(PID, HID4, MAC4, VLAN1, getLocation(5), + Collections.singleton(IP4)); + + expect(hostService.getHostsByIp(IP3)) + .andReturn(Collections.singleton(replyer)); + expect(interfaceService.getInterfacesByIp(IP4)) + .andReturn(Collections.emptySet()); + expect(hostService.getHost(HID4)).andReturn(requestor); + + replay(hostService); + replay(interfaceService); + + Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC4, SOLICITED_MAC3, + IP4, IP3); + + //Setup for flood test + getEdgePointsNoArg = Lists.newLinkedList(); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("5"), PortNumber.portNumber(1))); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("4"), PortNumber.portNumber(1))); + proxyArp.reply(ndpRequest, getLocation(6)); + + verifyFlood(ndpRequest); + } + + /** + * Test ARP request from external network to an internal host. + */ @Test public void testReplyToRequestForUs() { Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254"); @@ -422,6 +559,63 @@ public class ProxyArpManagerTest { verifyPacketOut(arpReply, LOC1, packetService.packets.get(0)); } + /** + * Test NDP request from external network to an internal host. + */ + @Test + public void testReplyToRequestForUsIpv6() { + Ip6Address theirIp = Ip6Address.valueOf("1000::ffff"); + Ip6Address ourFirstIp = Ip6Address.valueOf("1000::1"); + Ip6Address ourSecondIp = Ip6Address.valueOf("2000::1"); + MacAddress firstMac = MacAddress.valueOf(1L); + MacAddress secondMac = MacAddress.valueOf(2L); + + Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1, + Collections.singleton(theirIp)); + + expect(hostService.getHost(HID2)).andReturn(requestor); + expect(hostService.getHostsByIp(ourFirstIp)) + .andReturn(Collections.singleton(requestor)); + replay(hostService); + replay(interfaceService); + + Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC2, + MacAddress.valueOf("33:33:ff:00:00:01"), + theirIp, + ourFirstIp); + isEdgePointReturn = true; + proxyArp.reply(ndpRequest, LOC1); + assertEquals(1, packetService.packets.size()); + + Ethernet ndpReply = buildNDP(ICMP6.NEIGHBOR_ADVERTISEMENT, + firstMac, + MAC2, + ourFirstIp, + theirIp); + verifyPacketOut(ndpReply, LOC1, packetService.packets.get(0)); + + // Test a request for the second address on that port + packetService.packets.clear(); + ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC2, + MacAddress.valueOf("33:33:ff:00:00:01"), + theirIp, + ourSecondIp); + proxyArp.reply(ndpRequest, LOC1); + assertEquals(1, packetService.packets.size()); + + ndpReply = buildNDP(ICMP6.NEIGHBOR_ADVERTISEMENT, + secondMac, + MAC2, + ourSecondIp, + theirIp); + verifyPacketOut(ndpReply, LOC1, packetService.packets.get(0)); + } + + /** + * Request for a valid external IPv4 address but coming in the wrong port. + */ @Test public void testReplyExternalPortBadRequest() { replay(hostService); // no further host service expectations @@ -442,6 +636,38 @@ public class ProxyArpManagerTest { assertEquals(0, packetService.packets.size()); } + /** + * Request for a valid external IPv6 address but coming in the wrong port. + */ + @Test + public void testReplyExternalPortBadRequestIpv6() { + replay(hostService); // no further host service expectations + replay(interfaceService); + + Ip6Address theirIp = Ip6Address.valueOf("1000::ffff"); + + Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC1, + MacAddress.valueOf("33:33:ff:00:00:01"), + theirIp, + Ip6Address.valueOf("3000::1")); + proxyArp.reply(ndpRequest, LOC1); + assertEquals(0, packetService.packets.size()); + + // Request for a valid internal IP address but coming in an external port + packetService.packets.clear(); + ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC1, + MacAddress.valueOf("33:33:ff:00:00:01"), + theirIp, + IP3); + proxyArp.reply(ndpRequest, LOC1); + assertEquals(0, packetService.packets.size()); + } + + /** + * Test ARP request from internal network to an external host. + */ @Test public void testReplyToRequestFromUs() { Ip4Address ourIp = Ip4Address.valueOf("10.0.1.1"); @@ -474,6 +700,48 @@ public class ProxyArpManagerTest { } /** + * Test NDP request from internal network to an external host. + */ + @Test + public void testReplyToRequestFromUsIpv6() { + Ip6Address ourIp = Ip6Address.valueOf("1000::1"); + MacAddress ourMac = MacAddress.valueOf(1L); + Ip6Address theirIp = Ip6Address.valueOf("1000::100"); + + expect(hostService.getHostsByIp(theirIp)).andReturn(Collections.emptySet()); + expect(interfaceService.getInterfacesByIp(ourIp)) + .andReturn(Collections.singleton(new Interface(getLocation(1), + Collections.singleton(new InterfaceIpAddress( + ourIp, + IpPrefix.valueOf("1000::1/64"))), + ourMac, + VLAN1))); + expect(hostService.getHost(HostId.hostId(ourMac, VLAN1))).andReturn(null); + replay(hostService); + replay(interfaceService); + + // This is a request from something inside our network (like a BGP + // daemon) to an external host. + Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + ourMac, + MacAddress.valueOf("33:33:ff:00:00:01"), + ourIp, + theirIp); + + //Ensure the packet is allowed through (it is not to an internal port) + isEdgePointReturn = true; + + proxyArp.reply(ndpRequest, getLocation(5)); + assertEquals(1, packetService.packets.size()); + verifyPacketOut(ndpRequest, getLocation(1), packetService.packets.get(0)); + + // The same request from a random external port should fail + packetService.packets.clear(); + proxyArp.reply(ndpRequest, getLocation(2)); + assertEquals(0, packetService.packets.size()); + } + + /** * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the * destination host is known. * Verifies the correct ARP request is sent out the correct port. @@ -502,6 +770,35 @@ public class ProxyArpManagerTest { /** * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the + * destination host is known. + * Verifies the correct ARP request is sent out the correct port. + */ + @Test + public void testForwardToHostIpv6() { + Host host1 = new DefaultHost(PID, HID3, MAC3, VLAN1, LOC1, + Collections.singleton(IP3)); + Host host2 = new DefaultHost(PID, HID4, MAC4, VLAN1, LOC2, + Collections.singleton(IP4)); + + expect(hostService.getHost(SOLICITED_HID3)).andReturn(host1); + expect(hostService.getHost(HID4)).andReturn(host2); + replay(hostService); + replay(interfaceService); + + Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC4, SOLICITED_MAC3, + IP4, IP3); + + proxyArp.forward(ndpRequest, LOC2); + + assertEquals(1, packetService.packets.size()); + OutboundPacket packet = packetService.packets.get(0); + + verifyPacketOut(ndpRequest, LOC1, packet); + } + + /** + * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the * destination host is not known. * Verifies the correct ARP request is flooded out the correct edge ports. */ @@ -526,6 +823,33 @@ public class ProxyArpManagerTest { } /** + * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the + * destination host is not known. + * Verifies the correct NDP request is flooded out the correct edge ports. + */ + @Test + public void testForwardFloodIpv6() { + expect(hostService.getHost(SOLICITED_HID3)).andReturn(null); + replay(hostService); + replay(interfaceService); + + Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC4, SOLICITED_MAC3, + IP4, IP3); + + //populate the list of edges when so that when forward hits flood in the manager it contains the values + //that should continue on + getEdgePointsNoArg = Lists.newLinkedList(); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("3"), PortNumber.portNumber(1))); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("5"), PortNumber.portNumber(1))); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("4"), PortNumber.portNumber(1))); + + proxyArp.forward(ndpRequest, getLocation(6)); + + verifyFlood(ndpRequest); + } + + /** * Verifies that the given packet was flooded out all available edge ports, * except for the input port. * @@ -626,6 +950,61 @@ public class ProxyArpManagerTest { } /** + * Builds an NDP packet with the given parameters. + * + * @param type NeighborSolicitation or NeighborAdvertisement + * @param srcMac source MAC address + * @param dstMac destination MAC address, or null if this is a request + * @param srcIp source IP address + * @param dstIp destination IP address + * @return the NDP packet + */ + private Ethernet buildNDP(byte type, MacAddress srcMac, MacAddress dstMac, + Ip6Address srcIp, Ip6Address dstIp) { + assertThat(type, anyOf( + is(ICMP6.NEIGHBOR_SOLICITATION), + is(ICMP6.NEIGHBOR_ADVERTISEMENT) + )); + assertNotNull(srcMac); + assertNotNull(dstMac); + assertNotNull(srcIp); + assertNotNull(dstIp); + + IPacket ndp; + if (type == ICMP6.NEIGHBOR_SOLICITATION) { + ndp = new NeighborSolicitation().setTargetAddress(dstIp.toOctets()); + } else { + ndp = new NeighborAdvertisement() + .setSolicitedFlag((byte) 1) + .setOverrideFlag((byte) 1) + .setTargetAddress(srcIp.toOctets()) + .addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS, + srcMac.toBytes()); + } + + ICMP6 icmp6 = new ICMP6(); + icmp6.setIcmpType(type); + icmp6.setIcmpCode((byte) 0); + icmp6.setPayload(ndp); + + IPv6 ipv6 = new IPv6(); + ipv6.setDestinationAddress(dstIp.toOctets()); + ipv6.setSourceAddress(srcIp.toOctets()); + ipv6.setNextHeader(IPv6.PROTOCOL_ICMP6); + ipv6.setHopLimit((byte) 255); + ipv6.setPayload(icmp6); + + Ethernet eth = new Ethernet(); + eth.setDestinationMACAddress(dstMac); + eth.setSourceMACAddress(srcMac); + eth.setEtherType(Ethernet.TYPE_IPV6); + eth.setVlanID(VLAN1.toShort()); + eth.setPayload(ipv6); + + return eth; + } + + /** * Test PacketService implementation that simply stores OutboundPackets * passed to {@link #emit(OutboundPacket)} for later verification. */ diff --git a/framework/src/onos/core/store/dist/pom.xml b/framework/src/onos/core/store/dist/pom.xml index cc293da4..f2ec2a71 100644 --- a/framework/src/onos/core/store/dist/pom.xml +++ b/framework/src/onos/core/store/dist/pom.xml @@ -71,7 +71,7 @@ <dependency> <groupId>org.mapdb</groupId> <artifactId>mapdb</artifactId> - <version>1.0.7</version> + <version>1.0.8</version> </dependency> <dependency> diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/ClusterDefinition.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/ClusterDefinition.java deleted file mode 100644 index 75f05a31..00000000 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/ClusterDefinition.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2015 Open Networking Laboratory - * - * 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.store.cluster.impl; - -import java.util.Set; - -import com.google.common.collect.ImmutableSet; - -/** - * Cluster definition. - */ -public class ClusterDefinition { - - private Set<NodeInfo> nodes; - private String ipPrefix; - - /** - * Creates a new cluster definition. - * @param nodes cluster nodes information - * @param ipPrefix ip prefix common to all cluster nodes - * @return cluster definition - */ - public static ClusterDefinition from(Set<NodeInfo> nodes, String ipPrefix) { - ClusterDefinition definition = new ClusterDefinition(); - definition.ipPrefix = ipPrefix; - definition.nodes = ImmutableSet.copyOf(nodes); - return definition; - } - - /** - * Returns set of cluster nodes info. - * @return cluster nodes info - */ - public Set<NodeInfo> getNodes() { - return ImmutableSet.copyOf(nodes); - } - - /** - * Returns ipPrefix in dotted decimal notion. - * @return ip prefix - */ - public String getIpPrefix() { - return ipPrefix; - } -}
\ No newline at end of file diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/ClusterDefinitionManager.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/ClusterDefinitionManager.java deleted file mode 100644 index 8b0001d8..00000000 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/ClusterDefinitionManager.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2015 Open Networking Laboratory - * - * 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.store.cluster.impl; - -import com.google.common.collect.ImmutableSet; -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.Service; -import org.onlab.packet.IpAddress; -import org.onosproject.cluster.ClusterDefinitionService; -import org.onosproject.cluster.ControllerNode; -import org.onosproject.cluster.DefaultControllerNode; -import org.onosproject.cluster.NodeId; -import org.onosproject.store.consistent.impl.DatabaseDefinition; -import org.onosproject.store.consistent.impl.DatabaseDefinitionStore; -import org.slf4j.Logger; - -import java.io.File; -import java.io.IOException; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.util.Enumeration; -import java.util.Set; -import java.util.stream.Collectors; - -import static java.net.NetworkInterface.getNetworkInterfaces; -import static java.util.Collections.list; -import static org.onosproject.cluster.DefaultControllerNode.DEFAULT_PORT; -import static org.onosproject.store.consistent.impl.DatabaseManager.PARTITION_DEFINITION_FILE; -import static org.slf4j.LoggerFactory.getLogger; - -/** - * Implementation of ClusterDefinitionService. - */ -@Component(immediate = true) -@Service -public class ClusterDefinitionManager implements ClusterDefinitionService { - - public static final String CLUSTER_DEFINITION_FILE = "../config/cluster.json"; - private static final String ONOS_NIC = "ONOS_NIC"; - private static final Logger log = getLogger(ClusterDefinitionManager.class); - private ControllerNode localNode; - private Set<ControllerNode> seedNodes; - - @Activate - public void activate() { - File clusterDefinitionFile = new File(CLUSTER_DEFINITION_FILE); - ClusterDefinitionStore clusterDefinitionStore = - new ClusterDefinitionStore(clusterDefinitionFile.getPath()); - - if (!clusterDefinitionFile.exists()) { - createDefaultClusterDefinition(clusterDefinitionStore); - } - - try { - ClusterDefinition clusterDefinition = clusterDefinitionStore.read(); - establishSelfIdentity(clusterDefinition); - seedNodes = ImmutableSet - .copyOf(clusterDefinition.getNodes()) - .stream() - .filter(n -> !localNode.id().equals(new NodeId(n.getId()))) - .map(n -> new DefaultControllerNode(new NodeId(n.getId()), - IpAddress.valueOf(n.getIp()), - n.getTcpPort())) - .collect(Collectors.toSet()); - } catch (IOException e) { - throw new IllegalStateException("Failed to read cluster definition.", e); - } - - log.info("Started"); - } - - @Deactivate - public void deactivate() { - log.info("Stopped"); - } - - @Override - public ControllerNode localNode() { - return localNode; - } - - @Override - public Set<ControllerNode> seedNodes() { - return seedNodes; - } - - @Override - public void formCluster(Set<ControllerNode> nodes, String ipPrefix) { - try { - Set<NodeInfo> infos = Sets.newHashSet(); - nodes.forEach(n -> infos.add(NodeInfo.from(n.id().toString(), - n.ip().toString(), - n.tcpPort()))); - - ClusterDefinition cdef = ClusterDefinition.from(infos, ipPrefix); - new ClusterDefinitionStore(CLUSTER_DEFINITION_FILE).write(cdef); - - DatabaseDefinition ddef = DatabaseDefinition.from(infos); - new DatabaseDefinitionStore(PARTITION_DEFINITION_FILE).write(ddef); - } catch (IOException e) { - log.error("Unable to form cluster", e); - } - } - - private IpAddress findLocalIp(ClusterDefinition clusterDefinition) throws SocketException { - Enumeration<NetworkInterface> interfaces = - NetworkInterface.getNetworkInterfaces(); - while (interfaces.hasMoreElements()) { - NetworkInterface iface = interfaces.nextElement(); - Enumeration<InetAddress> inetAddresses = iface.getInetAddresses(); - while (inetAddresses.hasMoreElements()) { - IpAddress ip = IpAddress.valueOf(inetAddresses.nextElement()); - if (clusterDefinition.getNodes().stream() - .map(NodeInfo::getIp) - .map(IpAddress::valueOf) - .anyMatch(nodeIp -> ip.equals(nodeIp))) { - return ip; - } - } - } - throw new IllegalStateException("Unable to determine local ip"); - } - - private void establishSelfIdentity(ClusterDefinition clusterDefinition) { - try { - IpAddress ip = findLocalIp(clusterDefinition); - localNode = new DefaultControllerNode(new NodeId(ip.toString()), ip); - } catch (SocketException e) { - throw new IllegalStateException("Cannot determine local IP", e); - } - } - - private void createDefaultClusterDefinition(ClusterDefinitionStore store) { - // Assumes IPv4 is returned. - String ip = getSiteLocalAddress(); - String ipPrefix = ip.replaceFirst("\\.[0-9]*$", ".*"); - NodeInfo node = NodeInfo.from(ip, ip, DEFAULT_PORT); - try { - store.write(ClusterDefinition.from(ImmutableSet.of(node), ipPrefix)); - } catch (IOException e) { - log.warn("Unable to write default cluster definition", e); - } - } - - /** - * Returns the address that matches the IP prefix given in ONOS_NIC - * environment variable if one was specified, or the first site local - * address if one can be found or the loopback address otherwise. - * - * @return site-local address in string form - */ - public static String getSiteLocalAddress() { - try { - String ipPrefix = System.getenv(ONOS_NIC); - for (NetworkInterface nif : list(getNetworkInterfaces())) { - for (InetAddress address : list(nif.getInetAddresses())) { - IpAddress ip = IpAddress.valueOf(address); - if (ipPrefix == null && address.isSiteLocalAddress() || - ipPrefix != null && matchInterface(ip.toString(), ipPrefix)) { - return ip.toString(); - } - } - } - } catch (SocketException e) { - log.error("Unable to get network interfaces", e); - } - - return IpAddress.valueOf(InetAddress.getLoopbackAddress()).toString(); - } - - // Indicates whether the specified interface address matches the given prefix. - // FIXME: Add a facility to IpPrefix to make this more robust - private static boolean matchInterface(String ip, String ipPrefix) { - String s = ipPrefix.replaceAll("\\.\\*", ""); - return ip.startsWith(s); - } -} diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/ClusterDefinitionStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/ClusterDefinitionStore.java deleted file mode 100644 index 2a2f4dc4..00000000 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/ClusterDefinitionStore.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2014-2015 Open Networking Laboratory - * - * 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.store.cluster.impl; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.io.Files; - -import java.io.File; -import java.io.IOException; - -/** - * Allows for reading and writing cluster definition as a JSON file. - */ -public class ClusterDefinitionStore { - - private final File file; - - /** - * Creates a reader/writer of the cluster definition file. - * @param filePath location of the definition file - */ - public ClusterDefinitionStore(String filePath) { - file = new File(filePath); - } - - /** - * Returns the cluster definition. - * @return cluster definition - * @throws IOException when I/O exception of some sort has occurred - */ - public ClusterDefinition read() throws IOException { - ObjectMapper mapper = new ObjectMapper(); - return mapper.readValue(file, ClusterDefinition.class); - } - - /** - * Writes the specified cluster definition to file. - * @param definition cluster definition - * @throws IOException when I/O exception of some sort has occurred - */ - public void write(ClusterDefinition definition) throws IOException { - checkNotNull(definition); - // write back to file - Files.createParentDirs(file); - ObjectMapper mapper = new ObjectMapper(); - mapper.writeValue(file, definition); - } -} diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/DistributedClusterStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/DistributedClusterStore.java index 859efebf..3bb6a708 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/DistributedClusterStore.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/DistributedClusterStore.java @@ -27,8 +27,8 @@ import org.apache.felix.scr.annotations.Service; import org.joda.time.DateTime; import org.onlab.packet.IpAddress; import org.onlab.util.KryoNamespace; -import org.onosproject.cluster.ClusterDefinitionService; import org.onosproject.cluster.ClusterEvent; +import org.onosproject.cluster.ClusterMetadataService; import org.onosproject.cluster.ClusterStore; import org.onosproject.cluster.ClusterStoreDelegate; import org.onosproject.cluster.ControllerNode; @@ -99,14 +99,14 @@ public class DistributedClusterStore private ControllerNode localNode; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected ClusterDefinitionService clusterDefinitionService; + protected ClusterMetadataService clusterMetadataService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected MessagingService messagingService; @Activate public void activate() { - localNode = clusterDefinitionService.localNode(); + localNode = clusterMetadataService.getLocalNode(); messagingService.registerHandler(HEARTBEAT_MESSAGE, new HeartbeatMessageHandler(), heartBeatMessageHandler); @@ -116,9 +116,6 @@ public class DistributedClusterStore heartBeatSender.scheduleWithFixedDelay(this::heartbeat, 0, HEARTBEAT_INTERVAL_MS, TimeUnit.MILLISECONDS); - addNode(localNode); - updateState(localNode.id(), State.ACTIVE); - log.info("Started"); } @@ -188,7 +185,7 @@ public class DistributedClusterStore private void addNode(ControllerNode node) { allNodes.put(node.id(), node); - updateState(node.id(), State.INACTIVE); + updateState(node.id(), node.equals(localNode) ? State.ACTIVE : State.INACTIVE); notifyDelegate(new ClusterEvent(ClusterEvent.Type.INSTANCE_ADDED, node)); } diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/StaticClusterMetadataStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/StaticClusterMetadataStore.java new file mode 100644 index 00000000..9f6c4130 --- /dev/null +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/impl/StaticClusterMetadataStore.java @@ -0,0 +1,221 @@ +package org.onosproject.store.cluster.impl; + +import static com.google.common.base.Preconditions.checkNotNull; +import static java.net.NetworkInterface.getNetworkInterfaces; +import static org.slf4j.LoggerFactory.getLogger; + +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; + +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.Service; +import org.onlab.packet.IpAddress; +import org.onosproject.cluster.ClusterMetadata; +import org.onosproject.cluster.ClusterMetadataEvent; +import org.onosproject.cluster.ClusterMetadataStore; +import org.onosproject.cluster.ClusterMetadataStoreDelegate; +import org.onosproject.cluster.ControllerNode; +import org.onosproject.cluster.DefaultControllerNode; +import org.onosproject.cluster.NodeId; +import org.onosproject.cluster.Partition; +import org.onosproject.store.AbstractStore; +import org.onosproject.store.service.Versioned; +import org.slf4j.Logger; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.google.common.base.Throwables; +import com.google.common.collect.Lists; +import com.google.common.io.Files; + +/** + * ClusterMetadataStore backed by a local file. + */ +@Component(immediate = true, enabled = true) +@Service +public class StaticClusterMetadataStore + extends AbstractStore<ClusterMetadataEvent, ClusterMetadataStoreDelegate> + implements ClusterMetadataStore { + + private final Logger log = getLogger(getClass()); + private static final String CLUSTER_METADATA_FILE = "../config/cluster.json"; + private static final int DEFAULT_ONOS_PORT = 9876; + private final File metadataFile = new File(CLUSTER_METADATA_FILE); + private AtomicReference<ClusterMetadata> metadata = new AtomicReference<>(); + private ObjectMapper mapper; + private long version; + + @Activate + public void activate() { + mapper = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addSerializer(NodeId.class, new NodeIdSerializer()); + module.addDeserializer(NodeId.class, new NodeIdDeserializer()); + module.addSerializer(ControllerNode.class, new ControllerNodeSerializer()); + module.addDeserializer(ControllerNode.class, new ControllerNodeDeserializer()); + mapper.registerModule(module); + File metadataFile = new File(CLUSTER_METADATA_FILE); + if (metadataFile.exists()) { + try { + metadata.set(mapper.readValue(metadataFile, ClusterMetadata.class)); + version = metadataFile.lastModified(); + } catch (IOException e) { + Throwables.propagate(e); + } + } else { + String localIp = getSiteLocalAddress(); + ControllerNode localNode = + new DefaultControllerNode(new NodeId(localIp), IpAddress.valueOf(localIp), DEFAULT_ONOS_PORT); + metadata.set(ClusterMetadata.builder() + .withName("default") + .withControllerNodes(Arrays.asList(localNode)) + .withPartitions(Lists.newArrayList(new Partition("p1", Lists.newArrayList(localNode.id())))) + .build()); + version = System.currentTimeMillis(); + } + log.info("Started"); + } + + @Deactivate + public void deactivate() { + log.info("Stopped"); + } + + @Override + public void setDelegate(ClusterMetadataStoreDelegate delegate) { + checkNotNull(delegate, "Delegate cannot be null"); + this.delegate = delegate; + } + + @Override + public void unsetDelegate(ClusterMetadataStoreDelegate delegate) { + this.delegate = null; + } + + @Override + public boolean hasDelegate() { + return this.delegate != null; + } + + @Override + public Versioned<ClusterMetadata> getClusterMetadata() { + return new Versioned<>(metadata.get(), version); + } + + @Override + public void setClusterMetadata(ClusterMetadata metadata) { + checkNotNull(metadata); + try { + Files.createParentDirs(metadataFile); + mapper.writeValue(metadataFile, metadata); + this.metadata.set(metadata); + } catch (IOException e) { + Throwables.propagate(e); + } + } + + @Override + public void setActiveReplica(String partitionId, NodeId nodeId) { + throw new UnsupportedOperationException(); + } + + @Override + public void unsetActiveReplica(String partitionId, NodeId nodeId) { + throw new UnsupportedOperationException(); + } + + @Override + public Collection<NodeId> getActiveReplicas(String partitionId) { + return metadata.get().getPartitions() + .stream() + .filter(r -> r.getName().equals(partitionId)) + .findFirst() + .map(r -> r.getMembers()) + .orElse(null); + } + + private static class ControllerNodeSerializer extends JsonSerializer<ControllerNode> { + @Override + public void serialize(ControllerNode node, JsonGenerator jgen, SerializerProvider provider) + throws IOException, JsonProcessingException { + jgen.writeStartObject(); + jgen.writeStringField("id", node.id().toString()); + jgen.writeStringField("ip", node.ip().toString()); + jgen.writeNumberField("port", node.tcpPort()); + jgen.writeEndObject(); + } + } + + private static class ControllerNodeDeserializer extends JsonDeserializer<ControllerNode> { + @Override + public ControllerNode deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + JsonNode node = jp.getCodec().readTree(jp); + NodeId nodeId = new NodeId(node.get("id").textValue()); + IpAddress ip = IpAddress.valueOf(node.get("ip").textValue()); + int port = node.get("port").asInt(); + return new DefaultControllerNode(nodeId, ip, port); + } + } + + private static class NodeIdSerializer extends JsonSerializer<NodeId> { + @Override + public void serialize(NodeId nodeId, JsonGenerator jgen, SerializerProvider provider) + throws IOException, JsonProcessingException { + jgen.writeString(nodeId.toString()); + } + } + + private class NodeIdDeserializer extends JsonDeserializer<NodeId> { + @Override + public NodeId deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + JsonNode node = jp.getCodec().readTree(jp); + return new NodeId(node.asText()); + } + } + + + private static String getSiteLocalAddress() { + Function<NetworkInterface, IpAddress> ipLookup = nif -> { + for (InetAddress address : Collections.list(nif.getInetAddresses())) { + if (address.isSiteLocalAddress()) { + return IpAddress.valueOf(address); + } + } + return null; + }; + try { + IpAddress ip = ipLookup.apply(NetworkInterface.getByName("eth0")); + if (ip != null) { + return ip.toString(); + } + for (NetworkInterface nif : Collections.list(getNetworkInterfaces())) { + ip = ipLookup.apply(nif); + if (ip != null) { + return ip.toString(); + } + } + } catch (Exception e) { + throw new IllegalStateException("Unable to get network interfaces", e); + } + return IpAddress.valueOf(InetAddress.getLoopbackAddress()).toString(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/messaging/impl/IOLoopMessagingManager.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/messaging/impl/IOLoopMessagingManager.java index ffdd25f2..ddb45f71 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/messaging/impl/IOLoopMessagingManager.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/messaging/impl/IOLoopMessagingManager.java @@ -22,7 +22,7 @@ import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.Service; import org.onlab.nio.service.IOLoopMessaging; -import org.onosproject.cluster.ClusterDefinitionService; +import org.onosproject.cluster.ClusterMetadataService; import org.onosproject.cluster.ControllerNode; import org.onosproject.store.cluster.messaging.Endpoint; import org.slf4j.Logger; @@ -38,11 +38,11 @@ public class IOLoopMessagingManager extends IOLoopMessaging { private final Logger log = LoggerFactory.getLogger(getClass()); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected ClusterDefinitionService clusterDefinitionService; + protected ClusterMetadataService clusterMetadataService; @Activate public void activate() throws Exception { - ControllerNode localNode = clusterDefinitionService.localNode(); + ControllerNode localNode = clusterMetadataService.getLocalNode(); super.start(new Endpoint(localNode.ip(), localNode.tcpPort())); log.info("Started"); } diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/messaging/impl/NettyMessagingManager.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/messaging/impl/NettyMessagingManager.java index 9328817b..23c81869 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/messaging/impl/NettyMessagingManager.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/cluster/messaging/impl/NettyMessagingManager.java @@ -16,6 +16,7 @@ package org.onosproject.store.cluster.messaging.impl; import com.google.common.base.Strings; + import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; @@ -23,7 +24,7 @@ import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.Service; import org.onlab.netty.NettyMessaging; -import org.onosproject.cluster.ClusterDefinitionService; +import org.onosproject.cluster.ClusterMetadataService; import org.onosproject.cluster.ControllerNode; import org.onosproject.store.cluster.messaging.Endpoint; import org.slf4j.Logger; @@ -41,11 +42,11 @@ public class NettyMessagingManager extends NettyMessaging { private static final short MIN_KS_LENGTH = 6; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected ClusterDefinitionService clusterDefinitionService; + protected ClusterMetadataService clusterMetadataService; @Activate public void activate() throws Exception { - ControllerNode localNode = clusterDefinitionService.localNode(); + ControllerNode localNode = clusterMetadataService.getLocalNode(); getTLSParameters(); super.start(new Endpoint(localNode.ip(), localNode.tcpPort())); log.info("Started"); diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseDefinition.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseDefinition.java deleted file mode 100644 index 11b56c14..00000000 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseDefinition.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2015 Open Networking Laboratory - * - * 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.store.consistent.impl; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Maps; -import org.onosproject.store.cluster.impl.NodeInfo; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static com.google.common.base.Preconditions.checkNotNull; - -/** - * Partitioned database configuration. - */ -public class DatabaseDefinition { - private Map<String, Set<NodeInfo>> partitions; - private Set<NodeInfo> nodes; - - /** - * Creates a new DatabaseDefinition. - * - * @param partitions partition map - * @param nodes set of nodes - * @return database definition - */ - public static DatabaseDefinition from(Map<String, Set<NodeInfo>> partitions, - Set<NodeInfo> nodes) { - checkNotNull(partitions); - checkNotNull(nodes); - DatabaseDefinition definition = new DatabaseDefinition(); - definition.partitions = ImmutableMap.copyOf(partitions); - definition.nodes = ImmutableSet.copyOf(nodes); - return definition; - } - - /** - * Creates a new DatabaseDefinition using default partitions. - * - * @param nodes set of nodes - * @return database definition - */ - public static DatabaseDefinition from(Set<NodeInfo> nodes) { - return from(generateDefaultPartitions(nodes), nodes); - } - - /** - * Returns the map of database partitions. - * - * @return db partition map - */ - public Map<String, Set<NodeInfo>> getPartitions() { - return partitions; - } - - /** - * Returns the set of nodes. - * - * @return nodes - */ - public Set<NodeInfo> getNodes() { - return nodes; - } - - - /** - * Generates set of default partitions using permutations of the nodes. - * - * @param nodes information about cluster nodes - * @return default partition map - */ - private static Map<String, Set<NodeInfo>> generateDefaultPartitions(Set<NodeInfo> nodes) { - List<NodeInfo> sorted = new ArrayList<>(nodes); - Collections.sort(sorted, (o1, o2) -> o1.getId().compareTo(o2.getId())); - Map<String, Set<NodeInfo>> partitions = Maps.newHashMap(); - - int length = nodes.size(); - int count = 3; - for (int i = 0; i < length; i++) { - Set<NodeInfo> set = new HashSet<>(count); - for (int j = 0; j < count; j++) { - set.add(sorted.get((i + j) % length)); - } - partitions.put("p" + (i + 1), set); - } - return partitions; - } - -}
\ No newline at end of file diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseDefinitionStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseDefinitionStore.java deleted file mode 100644 index b77667b2..00000000 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseDefinitionStore.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2015 Open Networking Laboratory - * - * 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.store.consistent.impl; - -import static com.google.common.base.Preconditions.checkNotNull; -import java.io.File; -import java.io.IOException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.io.Files; - -/** - * Allows for reading and writing partitioned database definition as a JSON file. - */ -public class DatabaseDefinitionStore { - - private final File file; - - /** - * Creates a reader/writer of the database definition file. - * - * @param filePath location of the definition file - */ - public DatabaseDefinitionStore(String filePath) { - file = new File(checkNotNull(filePath)); - } - - /** - * Creates a reader/writer of the database definition file. - * - * @param filePath location of the definition file - */ - public DatabaseDefinitionStore(File filePath) { - file = checkNotNull(filePath); - } - - /** - * Returns the database definition. - * - * @return database definition - * @throws IOException when I/O exception of some sort has occurred. - */ - public DatabaseDefinition read() throws IOException { - ObjectMapper mapper = new ObjectMapper(); - return mapper.readValue(file, DatabaseDefinition.class); - } - - /** - * Writes the specified database definition to file. - * - * @param definition database definition - * @throws IOException when I/O exception of some sort has occurred. - */ - public void write(DatabaseDefinition definition) throws IOException { - checkNotNull(definition); - // write back to file - Files.createParentDirs(file); - ObjectMapper mapper = new ObjectMapper(); - mapper.writeValue(file, definition); - } -} diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseManager.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseManager.java index 6ea7c220..3e89635a 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseManager.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseManager.java @@ -18,7 +18,6 @@ package org.onosproject.store.consistent.impl; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; @@ -50,12 +49,12 @@ import org.apache.felix.scr.annotations.Service; import org.onosproject.app.ApplicationEvent; import org.onosproject.app.ApplicationListener; import org.onosproject.app.ApplicationService; +import org.onosproject.cluster.ClusterMetadataService; import org.onosproject.cluster.ClusterService; +import org.onosproject.cluster.ControllerNode; import org.onosproject.cluster.NodeId; import org.onosproject.core.ApplicationId; import org.onosproject.core.IdGenerator; -import org.onosproject.store.cluster.impl.ClusterDefinitionManager; -import org.onosproject.store.cluster.impl.NodeInfo; import org.onosproject.store.cluster.messaging.ClusterCommunicationService; import org.onosproject.store.ecmap.EventuallyConsistentMapBuilderImpl; import org.onosproject.store.service.AtomicCounterBuilder; @@ -73,8 +72,6 @@ import org.onosproject.store.service.Transaction; import org.onosproject.store.service.TransactionContextBuilder; import org.slf4j.Logger; -import java.io.File; -import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.Map; @@ -99,8 +96,6 @@ public class DatabaseManager implements StorageService, StorageAdminService { private final Logger log = getLogger(getClass()); - public static final int COPYCAT_TCP_PORT = 9876; - public static final String PARTITION_DEFINITION_FILE = "../config/tablets.json"; public static final String BASE_PARTITION_NAME = "p0"; private static final int RAFT_ELECTION_TIMEOUT_MILLIS = 3000; @@ -122,6 +117,9 @@ public class DatabaseManager implements StorageService, StorageAdminService { Multimaps.synchronizedMultimap(ArrayListMultimap.create()); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ClusterMetadataService clusterMetadataService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected ClusterService clusterService; @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY, policy = ReferencePolicy.DYNAMIC) @@ -130,8 +128,9 @@ public class DatabaseManager implements StorageService, StorageAdminService { @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected ClusterCommunicationService clusterCommunicator; - protected String nodeToUri(NodeInfo node) { - return String.format("onos://%s:%d", node.getIp(), node.getTcpPort()); + protected String nodeIdToUri(NodeId nodeId) { + ControllerNode node = clusterService.getNode(nodeId); + return String.format("onos://%s:%d", node.ip(), node.tcpPort()); } protected void bindApplicationService(ApplicationService service) { @@ -147,30 +146,22 @@ public class DatabaseManager implements StorageService, StorageAdminService { @Activate public void activate() { localNodeId = clusterService.getLocalNode().id(); - // load database configuration - File databaseDefFile = new File(PARTITION_DEFINITION_FILE); - log.info("Loading database definition: {}", databaseDefFile.getAbsolutePath()); - Map<String, Set<NodeInfo>> partitionMap; - try { - DatabaseDefinitionStore databaseDefStore = new DatabaseDefinitionStore(databaseDefFile); - if (!databaseDefFile.exists()) { - createDefaultDatabaseDefinition(databaseDefStore); - } - partitionMap = databaseDefStore.read().getPartitions(); - } catch (IOException e) { - throw new IllegalStateException("Failed to load database config", e); - } + Map<String, Set<NodeId>> partitionMap = Maps.newHashMap(); + clusterMetadataService.getClusterMetadata().getPartitions().forEach(p -> { + partitionMap.put(p.getName(), Sets.newHashSet(p.getMembers())); + }); + String[] activeNodeUris = partitionMap.values() .stream() .reduce((s1, s2) -> Sets.union(s1, s2)) .get() .stream() - .map(this::nodeToUri) + .map(this::nodeIdToUri) .toArray(String[]::new); - String localNodeUri = nodeToUri(NodeInfo.of(clusterService.getLocalNode())); + String localNodeUri = nodeIdToUri(clusterMetadataService.getLocalNode().id()); Protocol protocol = new CopycatCommunicationProtocol(clusterService, clusterCommunicator); ClusterConfig clusterConfig = new ClusterConfig() @@ -198,7 +189,7 @@ public class DatabaseManager implements StorageService, StorageAdminService { List<Database> partitions = partitionMap.entrySet() .stream() .map(entry -> { - String[] replicas = entry.getValue().stream().map(this::nodeToUri).toArray(String[]::new); + String[] replicas = entry.getValue().stream().map(this::nodeIdToUri).toArray(String[]::new); return newDatabaseConfig(entry.getKey(), newPersistentLog(), replicas); }) .map(config -> { @@ -229,17 +220,6 @@ public class DatabaseManager implements StorageService, StorageAdminService { log.info("Started"); } - private void createDefaultDatabaseDefinition(DatabaseDefinitionStore store) { - // Assumes IPv4 is returned. - String ip = ClusterDefinitionManager.getSiteLocalAddress(); - NodeInfo node = NodeInfo.from(ip, ip, COPYCAT_TCP_PORT); - try { - store.write(DatabaseDefinition.from(ImmutableSet.of(node))); - } catch (IOException e) { - log.warn("Unable to write default cluster definition", e); - } - } - @Deactivate public void deactivate() { CompletableFuture.allOf(inMemoryDatabase.close(), partitionedDatabase.close()) diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultTransactionContext.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultTransactionContext.java index b66f424b..73888221 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultTransactionContext.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultTransactionContext.java @@ -86,7 +86,7 @@ public class DefaultTransactionContext implements TransactionContext { @SuppressWarnings("unchecked") @Override - public void commit() { + public boolean commit() { // TODO: rework commit implementation to be more intuitive checkState(isOpen, TX_NOT_OPEN_ERROR); CommitResponse response = null; @@ -95,10 +95,11 @@ public class DefaultTransactionContext implements TransactionContext { txMaps.values().forEach(m -> updates.addAll(m.prepareDatabaseUpdates())); Transaction transaction = new DefaultTransaction(transactionId, updates); response = Futures.getUnchecked(database.prepareAndCommit(transaction)); + return response.success(); + } catch (Exception e) { + abort(); + return false; } finally { - if (response != null && !response.success()) { - abort(); - } isOpen = false; } } diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java index 973db494..687762e0 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/device/impl/GossipDeviceStore.java @@ -427,6 +427,7 @@ public class GossipDeviceStore // Primary providers can respond to all changes, but ancillary ones // should respond only to annotation changes. + DeviceEvent event = null; if ((providerId.isAncillary() && annotationsChanged) || (!providerId.isAncillary() && (propertiesChanged || annotationsChanged))) { boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice); @@ -436,17 +437,18 @@ public class GossipDeviceStore providerId, oldDevice, devices.get(newDevice.id()) , newDevice); } - if (!providerId.isAncillary()) { - boolean wasOnline = availableDevices.contains(newDevice.id()); - markOnline(newDevice.id(), newTimestamp); - if (!wasOnline) { - notifyDelegateIfNotNull(new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null)); - } - } - return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null); + event = new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null); } - return null; + + if (!providerId.isAncillary()) { + boolean wasOnline = availableDevices.contains(newDevice.id()); + markOnline(newDevice.id(), newTimestamp); + if (!wasOnline) { + notifyDelegateIfNotNull(new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null)); + } + } + return event; } @Override diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/ecmap/MapDbPersistentStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/ecmap/MapDbPersistentStore.java index e62a2d5c..f5ce47fc 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/ecmap/MapDbPersistentStore.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/ecmap/MapDbPersistentStore.java @@ -100,4 +100,4 @@ class MapDbPersistentStore<K, V> implements PersistentStore<K, V> { items.remove(keyBytes); database.commit(); } -} +}
\ No newline at end of file diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/newresource/impl/ConsistentResourceStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/newresource/impl/ConsistentResourceStore.java index 10f79eb0..687576c3 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/newresource/impl/ConsistentResourceStore.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/newresource/impl/ConsistentResourceStore.java @@ -52,7 +52,7 @@ import static com.google.common.base.Preconditions.checkNotNull; /** * Implementation of ResourceStore using TransactionalMap. */ -@Component(immediate = true, enabled = false) +@Component(immediate = true) @Service @Beta public class ConsistentResourceStore implements ResourceStore { @@ -239,6 +239,18 @@ public class ConsistentResourceStore implements ResourceStore { } @Override + public Collection<ResourcePath> getChildResources(ResourcePath parent) { + checkNotNull(parent); + + Versioned<List<ResourcePath>> children = childMap.get(parent); + if (children == null) { + return Collections.emptyList(); + } + + return children.value(); + } + + @Override public <T> Collection<ResourcePath> getAllocatedResources(ResourcePath parent, Class<T> cls) { checkNotNull(parent); checkNotNull(cls); diff --git a/framework/src/onos/core/store/dist/src/test/java/org/onosproject/store/intent/impl/PartitionManagerTest.java b/framework/src/onos/core/store/dist/src/test/java/org/onosproject/store/intent/impl/PartitionManagerTest.java index 25e23d3a..61d1937e 100644 --- a/framework/src/onos/core/store/dist/src/test/java/org/onosproject/store/intent/impl/PartitionManagerTest.java +++ b/framework/src/onos/core/store/dist/src/test/java/org/onosproject/store/intent/impl/PartitionManagerTest.java @@ -325,5 +325,11 @@ public class PartitionManagerTest { return Objects.equals(this.hash(), that.hash()); } + + @Override + public int compareTo(Key o) { + Long thisHash = hash(); + return thisHash.compareTo(o.hash()); + } } } diff --git a/framework/src/onos/core/store/persistence/pom.xml b/framework/src/onos/core/store/persistence/pom.xml new file mode 100644 index 00000000..555de11a --- /dev/null +++ b/framework/src/onos/core/store/persistence/pom.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ 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. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <repositories> + <repository> + <id>repository.springsource.com.release</id> + <name>SpringSource OBR - Release</name> + <url>http://repository.springsource.com/maven/bundles/release</url> + </repository> + <repository> + <id>repository.springsource.com.external</id> + <name>SpringSource OBR - External</name> + <url>http://repository.springsource.com/maven/bundles/external</url> + </repository> + </repositories> + + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-core-store</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>onos-core-persistence</artifactId> + <packaging>bundle</packaging> + + <description>ONOS Core persistent local store subsystem</description> + + + + <dependencies> + <!--<dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + </dependency>--> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mapdb</groupId> + <artifactId>mapdb</artifactId> + <version>1.0.8</version> + </dependency> + </dependencies> + +</project> diff --git a/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/DefaultPersistentMapBuilder.java b/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/DefaultPersistentMapBuilder.java new file mode 100644 index 00000000..88c7d148 --- /dev/null +++ b/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/DefaultPersistentMapBuilder.java @@ -0,0 +1,63 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.persistence.impl; + +import org.mapdb.DB; +import org.onosproject.persistence.PersistentMapBuilder; +import org.onosproject.store.service.Serializer; + +import java.util.Map; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default builder for persistent maps stored in the mapDB local database via the persistence service. + */ +public class DefaultPersistentMapBuilder<K, V> implements PersistentMapBuilder<K, V> { + + private final DB localDB; + + private String name = null; + + private Serializer serializer = null; + + + public DefaultPersistentMapBuilder(DB localDB) { + checkNotNull(localDB, "The local database cannot be null."); + this.localDB = localDB; + } + + public PersistentMapBuilder<K, V> withName(String name) { + this.name = PersistenceManager.MAP_PREFIX + checkNotNull(name); + return this; + } + + public PersistentMapBuilder<K, V> withSerializer(Serializer serializer) { + checkArgument(this.serializer == null); + checkNotNull(serializer); + this.serializer = serializer; + return this; + } + + public Map<K, V> build() { + checkNotNull(name, "The name must be assigned."); + checkNotNull(serializer, "The key serializer must be assigned."); + + return new PersistentMap<K, V>(serializer, localDB, name); + } +}
\ No newline at end of file diff --git a/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/DefaultPersistentSetBuilder.java b/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/DefaultPersistentSetBuilder.java new file mode 100644 index 00000000..e1544fb4 --- /dev/null +++ b/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/DefaultPersistentSetBuilder.java @@ -0,0 +1,59 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.persistence.impl; + +import org.mapdb.DB; +import org.onosproject.persistence.PersistentSetBuilder; +import org.onosproject.store.service.Serializer; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default builder for persistent sets stored in the mapDB local database via the persistence service.. + */ +public class DefaultPersistentSetBuilder<E> implements PersistentSetBuilder<E> { + + private final DB localDB; + + private String name = null; + + private Serializer serializer = null; + + public DefaultPersistentSetBuilder(DB localDB) { + this.localDB = checkNotNull(localDB, "The local database cannot be null."); + } + + public PersistentSetBuilder<E> withName(String name) { + this.name = PersistenceManager.SET_PREFIX + checkNotNull(name); + return this; + } + + public PersistentSetBuilder<E> withSerializer(Serializer serializer) { + checkArgument(this.serializer == null); + checkNotNull(serializer); + this.serializer = serializer; + return this; + } + + public PersistentSet<E> build() { + checkNotNull(name, "The name must be assigned."); + checkNotNull(serializer, "The serializer must be assigned."); + + return new PersistentSet<E>(serializer, localDB, name); + } +}
\ No newline at end of file diff --git a/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/PersistenceException.java b/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/PersistenceException.java new file mode 100644 index 00000000..f2ba20c2 --- /dev/null +++ b/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/PersistenceException.java @@ -0,0 +1,30 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.persistence.impl; + +/** + * An exception defined for failures of the local persistent store system. + */ + +/** + * Throws an exception with the specified message. + */ +public class PersistenceException extends RuntimeException { + public PersistenceException(String s) { + super(s); + } +} diff --git a/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/PersistenceManager.java b/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/PersistenceManager.java new file mode 100644 index 00000000..64a8683a --- /dev/null +++ b/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/PersistenceManager.java @@ -0,0 +1,138 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.persistence.impl; + +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.Service; +import org.mapdb.DB; +import org.mapdb.DBMaker; +import org.onosproject.persistence.PersistenceService; +import org.onosproject.persistence.PersistentMapBuilder; +import org.onosproject.persistence.PersistentSetBuilder; +import org.slf4j.Logger; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Service that maintains local disk backed maps and sets. This implementation automatically deletes empty structures + * on shutdown. + */ +@Component(immediate = true) +@Service +public class PersistenceManager implements PersistenceService { + + private static final String DATABASE_PATH = "../data/localDB"; + + private static final String ENCLOSING_FOLDER = "../data"; + + static final String MAP_PREFIX = "map:"; + + static final String SET_PREFIX = "set:"; + + private final Logger log = getLogger(getClass()); + + private DB localDB = null; + + private static final int FLUSH_FREQUENCY_MILLIS = 3000; + + private final Timer timer = new Timer(); + + private final CommitTask commitTask = new CommitTask(); + + @Activate + public void activate() { + Path dbPath = Paths.get(DATABASE_PATH); + Path dbFolderPath = Paths.get(ENCLOSING_FOLDER); + //Make sure the directory exists, if it does not, make it. + if (!dbFolderPath.toFile().isDirectory()) { + log.info("The specified folder location for the database did not exist and will be created."); + try { + Files.createDirectories(dbFolderPath); + } catch (IOException e) { + log.error("Could not create the required folder for the database."); + throw new PersistenceException("Database folder could not be created."); + } + } + //Notify if the database file does not exist. + boolean dbFound = Files.exists(dbPath); + if (!dbFound) { + log.info("The database file could not be located, a new database will be constructed."); + + } else { + log.info("A previous database file has been found."); + } + localDB = DBMaker.newFileDB(dbPath.toFile()) + .asyncWriteEnable() + .closeOnJvmShutdown() + .make(); + timer.schedule(commitTask, FLUSH_FREQUENCY_MILLIS, FLUSH_FREQUENCY_MILLIS); + log.info("Started"); + } + + @Deactivate + public void deactivate() { + for (Map.Entry<String, Object> entry : localDB.getAll().entrySet()) { + String key = entry.getKey(); + Object value = entry.getValue(); + //This is a map implementation to be handled as such + if (value instanceof Map) { + Map asMap = (Map) value; + if (asMap.isEmpty()) { + //the map is empty and may be deleted + localDB.delete(key); + } + //This is a set implementation and can be handled as such + } else if (value instanceof Set) { + Set asSet = (Set) value; + if (asSet.isEmpty()) { + //the set is empty and may be deleted + localDB.delete(key); + } + } + } + localDB.commit(); + localDB.close(); + log.info("Stopped"); + } + + public <K, V> PersistentMapBuilder<K, V> persistentMapBuilder() { + return new DefaultPersistentMapBuilder<>(localDB); + } + + public <E> PersistentSetBuilder<E> persistentSetBuilder() { + return new DefaultPersistentSetBuilder<>(localDB); + } + + private class CommitTask extends TimerTask { + + @Override + public void run() { + localDB.commit(); + } + } +}
\ No newline at end of file diff --git a/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/PersistentMap.java b/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/PersistentMap.java new file mode 100644 index 00000000..4506bbda --- /dev/null +++ b/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/PersistentMap.java @@ -0,0 +1,192 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.persistence.impl; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.mapdb.DB; +import org.mapdb.Hasher; +import org.onosproject.store.service.Serializer; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkNotNull; + + +/** + * A map implementation that stores and receives all data from a serialized internal map. + */ +public class PersistentMap<K, V> implements Map<K, V> { + + private final Serializer serializer; + + private final org.mapdb.DB database; + + private final Map<byte[], byte[]> items; + + private final String name; + + public PersistentMap(Serializer serializer, DB database, String name) { + this.serializer = checkNotNull(serializer); + this.database = checkNotNull(database); + this.name = checkNotNull(name); + + items = database + .createHashMap(name) + .keySerializer(org.mapdb.Serializer.BYTE_ARRAY) + .valueSerializer(org.mapdb.Serializer.BYTE_ARRAY) + .hasher(Hasher.BYTE_ARRAY) + .makeOrGet(); + } + + /** + * Reads this set in deserialized form into the provided map. + * + * @param items the map to be populated + */ + public void readInto(Map<K, V> items) { + this.items.forEach((keyBytes, valueBytes) -> + items.put(serializer.decode(keyBytes), + serializer.decode(valueBytes))); + } + + @Override + public V remove(Object key) { + checkNotNull(key, "Key can not be null."); + V removed = get(key); + items.remove(serializer.encode(key)); + return removed; + } + + @Override + public int size() { + return items.size(); + } + + @Override + public boolean isEmpty() { + return items.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + checkNotNull(key, "Key cannot be null."); + return items.containsKey(serializer.encode(key)); + } + + @Override + public boolean containsValue(Object value) { + checkNotNull(value, "Value cannot be null."); + byte[] serialized = serializer.encode(value); + for (byte[] compareValue : items.values()) { + boolean same = true; + if (compareValue == null) { + same = false; + } else if (compareValue.length != serialized.length) { + same = false; + } else { + for (int i = 0; i < serialized.length; i++) { + if (serialized[i] != compareValue[i]) { + same = false; + break; + } + } + } + if (same) { + return true; + } + } + return false; + } + + @Override + public V get(Object key) { + checkNotNull(key, "Key cannot be null."); + return serializer.decode(items.get(serializer.encode(key))); + } + + @Override + public V put(K key, V value) { + checkNotNull(key, "Key cannot be null."); + checkNotNull(value, "Value cannot be null."); + byte[] prevVal = items.put(serializer.encode(key), serializer.encode(value)); + if (prevVal == null) { + return null; + } + return serializer.decode(prevVal); + } + + @Override + public void putAll(Map<? extends K, ? extends V> m) { + checkNotNull(m, "The passed in map cannot be null."); + m.forEach((k, v) -> items.put(serializer.encode(k), serializer.encode(v))); + } + + @Override + public void clear() { + items.clear(); + } + + @Override + public Set<K> keySet() { + Set<K> keys = Sets.newHashSet(); + items.keySet().forEach(k -> keys.add(serializer.decode(k))); + return keys; + } + + @Override + public Collection<V> values() { + Collection<V> values = Sets.newHashSet(); + items.values().forEach(v -> values.add(serializer.decode(v))); + return values; + } + + @Override + public Set<Entry<K, V>> entrySet() { + Set<Entry<K, V>> entries = Sets.newHashSet(); + items.entrySet(). + forEach(e -> entries.add(Maps.immutableEntry(serializer.decode(e.getKey()), + serializer.decode(e.getValue())))); + return entries; + } + + @Override + public boolean equals(Object map) { + //This is not threadsafe and on larger maps incurs a significant processing cost + if (!(map instanceof Map)) { + return false; + } + Map asMap = (Map) map; + if (this.size() != asMap.size()) { + return false; + } + for (Entry entry : this.entrySet()) { + Object key = entry.getKey(); + if (!asMap.containsKey(key) || !asMap.get(key).equals(entry.getValue())) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + return super.hashCode(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/PersistentSet.java b/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/PersistentSet.java new file mode 100644 index 00000000..26118cf6 --- /dev/null +++ b/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/PersistentSet.java @@ -0,0 +1,194 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.persistence.impl; + +import com.google.common.collect.Iterators; +import org.mapdb.DB; +import org.mapdb.Hasher; +import org.mapdb.Serializer; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * A set implementation that gets and receives all data from a serialized internal set. + */ +//TODO add locking for reads and writes +public class PersistentSet<E> implements Set<E> { + + private final org.onosproject.store.service.Serializer serializer; + + private final org.mapdb.DB database; + + private final Set<byte[]> items; + + private final String name; + + public PersistentSet(org.onosproject.store.service.Serializer serializer, DB database, String name) { + this.serializer = checkNotNull(serializer); + this.database = checkNotNull(database); + this.name = checkNotNull(name); + + items = database + .createHashSet(name) + .serializer(Serializer.BYTE_ARRAY) + .hasher(Hasher.BYTE_ARRAY) + .makeOrGet(); + } + + public void readInto(Set<E> items) { + this.items.forEach(item -> items.add(serializer.decode(item))); + } + + @Override + public int size() { + return items.size(); + } + + @Override + public boolean isEmpty() { + return items.isEmpty(); + } + + @Override + public boolean contains(Object o) { + checkNotNull(o, "The argument cannot be null"); + return items.contains(serializer.encode(o)); + } + + @Override + public Iterator<E> iterator() { + return Iterators.transform(items.iterator(), serializer::decode); + } + + @Override + public Object[] toArray() { + Object[] retArray = new Object[items.size()]; + int index = 0; + Iterator<byte[]> iterator = items.iterator(); + while (iterator.hasNext()) { + retArray[index] = serializer.decode(iterator.next()); + index++; + } + return retArray; + } + + @Override + public <T> T[] toArray(T[] a) { + checkNotNull(a, "The passed in array cannot be null."); + int index = 0; + Iterator<byte[]> iterator = items.iterator(); + T[] retArray; + if (a.length >= items.size()) { + retArray = a; + } else { + retArray = (T[]) new Object[items.size()]; + } + while (iterator.hasNext()) { + retArray[index++] = serializer.decode(iterator.next()); + } + if (retArray.length > items.size()) { + retArray[index] = null; + } + return retArray; + } + + @Override + public boolean add(E item) { + checkNotNull("Item to be added cannot be null."); + return items.add(serializer.encode(item)); + } + + @Override + public boolean remove(Object o) { + checkNotNull(o, "Item to be removed cannot be null."); + return items.remove(serializer.encode(o)); + } + + @Override + public boolean containsAll(Collection<?> c) { + checkNotNull(c, "Collection cannot be internal."); + for (Object item : c) { + if (!items.contains(serializer.encode(item))) { + return false; + } + } + return true; + } + + @Override + public boolean addAll(Collection<? extends E> c) { + checkNotNull(c, "The collection to be added cannot be null."); + boolean changed = false; + for (Object item : c) { + changed = items.add(serializer.encode(item)) || changed; + } + return changed; + } + + @Override + public boolean retainAll(Collection<?> c) { + boolean changed = false; + for (byte[] item : items) { + E deserialized = serializer.decode(item); + if (!c.contains(deserialized)) { + changed = items.remove(item) || changed; + } + } + return changed; + } + + @Override + public boolean removeAll(Collection<?> c) { + boolean changed = false; + for (Object item : c) { + changed = items.remove(serializer.encode(item)) || changed; + } + return changed; + } + + @Override + public void clear() { + items.clear(); + } + + @Override + public boolean equals(Object set) { + //This is not threadsafe and on larger sets incurs a significant processing cost + if (!(set instanceof Set)) { + return false; + } + Set asSet = (Set) set; + if (asSet.size() != this.size()) { + return false; + } + for (Object item : this) { + if (!asSet.contains(item)) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + return super.hashCode(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/package-info.java b/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/package-info.java new file mode 100644 index 00000000..968a5046 --- /dev/null +++ b/framework/src/onos/core/store/persistence/src/main/java/org/onosproject/persistence/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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. + */ + +/** + * Implementations of core persistence classes. + */ +package org.onosproject.persistence.impl; diff --git a/framework/src/onos/core/store/persistence/src/main/test/test/PersistentMapTest.java b/framework/src/onos/core/store/persistence/src/main/test/test/PersistentMapTest.java new file mode 100644 index 00000000..b059f18e --- /dev/null +++ b/framework/src/onos/core/store/persistence/src/main/test/test/PersistentMapTest.java @@ -0,0 +1,245 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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 test; + +import com.google.common.collect.Maps; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mapdb.DB; +import org.mapdb.DBMaker; +import org.onosproject.persistence.impl.PersistentMap; +import org.onosproject.store.service.Serializer; + +import java.nio.file.Paths; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * Test suite for Persistent Map. + */ +public class PersistentMapTest { + + private Map<Integer, Integer> map = null; + private DB fakeDB = null; + + + /** + * Set up the database, create a map and a direct executor to handle it. + * + * @throws Exception if instantiation fails + */ + @Before + public void setUp() throws Exception { + //Creates a db, a map within it and a basic integer serializer (async writing is off) + fakeDB = DBMaker + .newFileDB(Paths.get("../testDb").toFile()) + .asyncWriteEnable() + .closeOnJvmShutdown() + .make(); + map = new PersistentMap<Integer, Integer>(new Serializer() { + @Override + public <T> byte[] encode(T object) { + if (object == null) { + return null; + } + int num = (Integer) object; + byte[] result = new byte[4]; + + result[0] = (byte) (num >> 24); + result[1] = (byte) (num >> 16); + result[2] = (byte) (num >> 8); + result[3] = (byte) num; + return result; + } + + @Override + public <T> T decode(byte[] bytes) { + if (bytes == null) { + return null; + } + int num = 0x00000000; + + num = num | bytes[0] << 24; + num = num | bytes[1] << 16; + num = num | bytes[2] << 8; + num = num | bytes[3]; + + return (T) new java.lang.Integer(num); + } + }, fakeDB, "map"); + } + + /** + * Clears and deletes the map, closes the datbase and deletes the file. + * + * @throws Exception if shutdown fails + */ + @After + public void tearDown() throws Exception { + map.clear(); + fakeDB.delete("map:map"); + fakeDB.commit(); + fakeDB.close(); + //This is key to prevent artifacts persisting between tests. + Paths.get("../testDB").toFile().delete(); + + + } + + @Test + public void testRemove() throws Exception { + //Checks removal and return values + fillMap(10); + assertEquals(10, map.size()); + for (int i = 0; i < 10; i++) { + assertEquals("The previous value was wrong.", new Integer(i), map.remove(i)); + assertNull("The previous value was wrong.", map.remove(i)); + //(i+1) compensates for base zero. + assertEquals("The size was wrong.", 10 - (i + 1), map.size()); + } + } + + @Test + public void testSize() throws Exception { + //Checks size values throughout addition and removal + for (int i = 0; i < 10; i++) { + map.put(i, i); + assertEquals("The map size is wrong.", i + 1, map.size()); + } + for (int i = 0; i < 10; i++) { + map.remove(i); + assertEquals("The map size is wrong.", 9 - i, map.size()); + } + } + + @Test + public void testIsEmpty() throws Exception { + //Checks empty condition + //asserts that the map starts out empty + assertTrue("Map should be empty", map.isEmpty()); + map.put(1, 1); + assertFalse("Map shouldn't be empty.", map.isEmpty()); + map.remove(1); + assertTrue("Map should be empty", map.isEmpty()); + } + + @Test + public void testContains() throws Exception { + //Checks both containsKey and containsValue be aware the implementations vary widely (value does not use mapDB + //due to object '=='being an insufficient check) + for (int i = 0; i < 10; i++) { + assertFalse("Map should not contain the key", map.containsKey(i)); + assertFalse("Map should not contain the value", map.containsValue(i)); + map.put(i, i); + assertTrue("Map should contain the key", map.containsKey(i)); + assertTrue("Map should contain the value", map.containsValue(i)); + } + } + + @Test + public void testGet() throws Exception { + //Tests value retrieval and nonexistent key return values + for (int i = 0; i < 10; i++) { + map.put(i, i); + for (int j = 0; j <= i; j++) { + assertEquals("The value was wrong.", new Integer(j), map.get(j)); + } + } + assertNull("Null return value for nonexistent keys.", map.get(10)); + } + + @Test + public void testPutAll() throws Exception { + //Tests adding of an outside map + Map<Integer, Integer> testMap = Maps.newHashMap(); + fillMap(10); + map.putAll(testMap); + for (int i = 0; i < 10; i++) { + assertTrue("The map should contain the current 'i' value.", map.containsKey(i)); + assertTrue("The map should contain the current 'i' value.", map.containsValue(i)); + } + } + + @Test + public void testClear() throws Exception { + //Tests clearing the map + assertTrue("Map was initialized incorrectly, should be empty.", map.isEmpty()); + fillMap(10); + assertFalse("Map should contain entries now.", map.isEmpty()); + map.clear(); + assertTrue("Map should have been cleared of entries.", map.isEmpty()); + + } + + @Test + public void testKeySet() throws Exception { + //Tests key set generation + fillMap(10); + Set<Integer> keys = map.keySet(); + for (int i = 0; i < 10; i++) { + assertTrue("The key set doesn't contain all keys 0-9", keys.contains(i)); + } + assertEquals("The key set has an incorrect number of entries", 10, keys.size()); + } + + @Test + public void testValues() throws Exception { + //Tests value set generation + fillMap(10); + Set<Integer> values = (Set<Integer>) map.values(); + for (int i = 0; i < 10; i++) { + assertTrue("The key set doesn't contain all keys 0-9", values.contains(i)); + } + assertEquals("The key set has an incorrect number of entries", 10, values.size()); + } + + @Test + public void testEntrySet() throws Exception { + //Test entry set generation (violates abstraction by knowing the type of the returned entries) + fillMap(10); + Set<Map.Entry<Integer, Integer>> entries = map.entrySet(); + for (int i = 0; i < 10; i++) { + assertTrue("The key set doesn't contain all keys 0-9", entries.contains(Maps.immutableEntry(i, i))); + } + assertEquals("The key set has an incorrect number of entries", 10, entries.size()); + } + + @Test public void testPut() throws Exception { + //Tests insertion behavior (particularly the returning of previous value) + fillMap(10); + for (int i = 0; i < 10; i++) { + assertEquals("Put should return the previous value", new Integer(i), map.put(i, i + 1)); + } + assertNull(map.put(11, 11)); + } + + /** + * Populated the map with pairs of integers from (0, 0) up to (numEntries - 1, numEntries -1). + * @param numEntries number of entries to add + */ + private void fillMap(int numEntries) { + for (int i = 0; i < numEntries; i++) { + map.put(i, i); + } + } +} diff --git a/framework/src/onos/core/store/persistence/src/main/test/test/PersistentSetTest.java b/framework/src/onos/core/store/persistence/src/main/test/test/PersistentSetTest.java new file mode 100644 index 00000000..3107ab30 --- /dev/null +++ b/framework/src/onos/core/store/persistence/src/main/test/test/PersistentSetTest.java @@ -0,0 +1,274 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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 test; + +import com.google.common.collect.Sets; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mapdb.DB; +import org.mapdb.DBMaker; +import org.onosproject.persistence.impl.PersistentSet; +import org.onosproject.store.service.Serializer; + +import java.nio.file.Paths; +import java.util.HashSet; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * Test suite for Persistent Set. + */ +public class PersistentSetTest { + + private Set<Integer> set = null; + private DB fakeDB = null; + + @Before + public void setUp() throws Exception { + //Creates a db, a set within it and a basic integer serializer (async writing is off) + fakeDB = DBMaker + .newFileDB(Paths.get("../testDb").toFile()) + .asyncWriteEnable() + .closeOnJvmShutdown() + .make(); + set = new PersistentSet<Integer>(new Serializer() { + @Override + public <T> byte[] encode(T object) { + if (object == null) { + return null; + } + int num = (Integer) object; + byte[] result = new byte[4]; + + result[0] = (byte) (num >> 24); + result[1] = (byte) (num >> 16); + result[2] = (byte) (num >> 8); + result[3] = (byte) num; + return result; + } + + @Override + public <T> T decode(byte[] bytes) { + if (bytes == null) { + return null; + } + int num = 0x00000000; + + num = num | bytes[0] << 24; + num = num | bytes[1] << 16; + num = num | bytes[2] << 8; + num = num | bytes[3]; + + return (T) new java.lang.Integer(num); + } + }, fakeDB, "set"); + + } + + @After + public void tearDown() throws Exception { + set.clear(); + fakeDB.delete("map:map"); + fakeDB.commit(); + fakeDB.close(); + //This is key to prevent artifacts persisting between tests. + Paths.get("../testDB").toFile().delete(); + } + + @Test + public void testSize() throws Exception { + //Check correct sizing throughout population + for (int i = 0; i < 10; i++) { + set.add(i); + assertEquals("The set should have i + 1 entries.", i + 1, set.size()); + } + } + + @Test + public void testIsEmpty() throws Exception { + //test empty condition + assertTrue("The set should be initialized empty.", set.isEmpty()); + fillSet(5, this.set); + assertFalse("The set should no longer be empty.", set.isEmpty()); + set.clear(); + assertTrue("The set should have been cleared.", set.isEmpty()); + } + + @Test + public void testContains() throws Exception { + //Test contains + assertFalse("The set should not contain anything", set.contains(1)); + fillSet(10, this.set); + for (int i = 0; i < 10; i++) { + assertTrue("The set should contain all values 0-9.", set.contains(i)); + } + } + + @Test + public void testIterator() throws Exception { + //Test iterator behavior (no order guarantees are made) + Set<Integer> validationSet = Sets.newHashSet(); + fillSet(10, this.set); + fillSet(10, validationSet); + set.iterator().forEachRemaining(item -> assertTrue("Items were mismatched.", validationSet.remove(item))); + //All values should have been seen and removed + assertTrue("All entries in the validation set should have been removed.", validationSet.isEmpty()); + } + + @Test + public void testToArray() throws Exception { + //Test creation of a new array of the values + fillSet(10, set); + Object[] arr = set.toArray(); + assertEquals("The array should be of length 10.", 10, arr.length); + for (int i = 0; i < 10; i++) { + assertTrue("All elements of the array should be in the set.", set.contains((Integer) arr[i])); + } + } + + @Test + public void testToArray1() throws Exception { + //Test creation of a new array with the possibility of populating passed array if space allows + Integer[] originalArray = new Integer[9]; + fillSet(9, set); + //Test the case where the array and set match in size + Object[] retArray = set.toArray(originalArray); + assertEquals("If the set can fit the array should be the one passed in.", originalArray, retArray); + //Test the case where the passe array is too small to fit the set + set.add(9); + assertNotEquals("A new set should be generated if the contents will not fit in the passed set", + set.toArray(originalArray), originalArray); + //Now test the case where there should be a null terminator + set.clear(); + fillSet(5, set); + assertNull("The character one after last should be null if the array is larger than the set.", + set.toArray(originalArray)[5]); + } + + @Test + public void testAdd() throws Exception { + //Test of add + for (int i = 0; i < 10; i++) { + assertEquals("The size of the set is wrong.", i, set.size()); + assertTrue("The first add of an element should be true.", set.add(i)); + assertFalse("The second add of an element should be false.", set.add(i)); + } + } + + @Test + public void testRemove() throws Exception { + //Test removal + fillSet(10, set); + for (int i = 0; i < 10; i++) { + assertEquals("The size of the set is wrong.", 10 - i, set.size()); + assertTrue("The first removal should be true.", set.remove(i)); + assertFalse("The second removal should be false (item no longer contained).", set.remove(i)); + } + assertTrue("All elements should have been removed.", set.isEmpty()); + } + + @Test + public void testContainsAll() throws Exception { + //Test contains with short circuiting + Set<Integer> integersToCheck = Sets.newHashSet(); + fillSet(10, integersToCheck); + fillSet(10, set); + assertTrue("The sets should be identical so mutual subsets.", set.containsAll(integersToCheck)); + set.remove(9); + assertFalse("The set should contain one fewer value.", set.containsAll(integersToCheck)); + } + + @Test + public void testAddAll() throws Exception { + //Test multi-adds with change checking + Set<Integer> integersToCheck = Sets.newHashSet(); + fillSet(10, integersToCheck); + assertFalse("Set should be empty and so integers to check should not be a subset.", + set.containsAll(integersToCheck)); + assertTrue("The set should have changed as a result of add all.", set.addAll(integersToCheck)); + assertFalse("The set should not have changed as a result of add all a second time.", + set.addAll(integersToCheck)); + assertTrue("The sets should now be equivalent.", set.containsAll(integersToCheck)); + assertTrue("The sets should now be equivalent.", integersToCheck.containsAll(set)); + } + + @Test + public void testRetainAll() throws Exception { + //Test ability to generate the intersection set + Set<Integer> retainSet = Sets.newHashSet(); + fillSet(10, set); + assertTrue("The set should have changed.", set.retainAll(retainSet)); + assertTrue("The set should have been emptied.", set.isEmpty()); + fillSet(10, set); + fillSet(10, retainSet); + Set<Integer> duplicateSet = new HashSet<>(set); + assertFalse("The set should not have changed.", set.retainAll(retainSet)); + assertEquals("The set should be the same as the duplicate.", duplicateSet, set); + retainSet.remove(9); + assertTrue("The set should have changed.", set.retainAll(retainSet)); + duplicateSet.remove(9); + assertEquals("The set should have had the nine element removed.", duplicateSet, set); + } + + @Test + public void testRemoveAll() throws Exception { + //Test for mass removal and change checking + Set<Integer> removeSet = Sets.newHashSet(); + fillSet(10, set); + Set<Integer> duplicateSet = Sets.newHashSet(set); + assertFalse("No elements should change.", set.removeAll(removeSet)); + assertEquals("Set should not have diverged from the duplicate.", duplicateSet, set); + fillSet(5, removeSet); + assertTrue("Elements should have been removed.", set.removeAll(removeSet)); + assertNotEquals("Duplicate set should no longer be equivalent.", duplicateSet, set); + assertEquals("Five elements should have been removed from set.", 5, set.size()); + for (Integer item : removeSet) { + assertFalse("No element of remove set should remain.", set.contains(item)); + } + } + + @Test + public void testClear() throws Exception { + //Test set emptying + assertTrue("The set should be initialized empty.", set.isEmpty()); + set.clear(); + assertTrue("Clear should have no effect on an empty set.", set.isEmpty()); + fillSet(10, set); + assertFalse("The set should no longer be empty.", set.isEmpty()); + set.clear(); + assertTrue("The set should be empty after clear.", set.isEmpty()); + } + + /** + * Populated the map with integers from (0) up to (numEntries - 1). + * + * @param numEntries number of entries to add + */ + private void fillSet(int numEntries, Set<Integer> set) { + checkNotNull(set); + for (int i = 0; i < numEntries; i++) { + set.add(i); + } + } +} diff --git a/framework/src/onos/core/store/pom.xml b/framework/src/onos/core/store/pom.xml index 2b246b83..219ae5d0 100644 --- a/framework/src/onos/core/store/pom.xml +++ b/framework/src/onos/core/store/pom.xml @@ -33,7 +33,8 @@ <modules> <module>dist</module> - <module>serializers</module> + <module>persistence</module> + <module>serializers</module> </modules> <dependencies> diff --git a/framework/src/onos/core/store/serializers/src/main/java/org/onosproject/store/serializers/ExtensionInstructionSerializer.java b/framework/src/onos/core/store/serializers/src/main/java/org/onosproject/store/serializers/ExtensionInstructionSerializer.java new file mode 100644 index 00000000..6b12df96 --- /dev/null +++ b/framework/src/onos/core/store/serializers/src/main/java/org/onosproject/store/serializers/ExtensionInstructionSerializer.java @@ -0,0 +1,73 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * 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.store.serializers; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.Serializer; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; +import org.onlab.osgi.DefaultServiceDirectory; +import org.onosproject.net.DeviceId; +import org.onosproject.net.behaviour.ExtensionResolver; +import org.onosproject.net.driver.DefaultDriverData; +import org.onosproject.net.driver.DefaultDriverHandler; +import org.onosproject.net.driver.DriverHandler; +import org.onosproject.net.driver.DriverService; +import org.onosproject.net.flow.instructions.ExtensionInstruction; +import org.onosproject.net.flow.instructions.ExtensionType; +import org.onosproject.net.flow.instructions.Instructions; + +/** + * Created by jono on 10/29/15. + */ +public class ExtensionInstructionSerializer extends + Serializer<Instructions.ExtensionInstructionWrapper> { + + public ExtensionInstructionSerializer() { + super(false, true); + } + + @Override + public void write(Kryo kryo, Output output, Instructions.ExtensionInstructionWrapper object) { + kryo.writeClassAndObject(output, object.extensionInstruction().type()); + kryo.writeClassAndObject(output, object.deviceId()); + + kryo.writeClassAndObject(output, object.extensionInstruction().serialize()); + + } + + @Override + public Instructions.ExtensionInstructionWrapper read(Kryo kryo, Input input, + Class<Instructions.ExtensionInstructionWrapper> type) { + ExtensionType exType = (ExtensionType) kryo.readClassAndObject(input); + DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input); + + DriverService driverService = DefaultServiceDirectory.getService(DriverService.class); + DriverHandler handler = new DefaultDriverHandler( + new DefaultDriverData(driverService.getDriver(deviceId), deviceId)); + + ExtensionResolver resolver = handler.behaviour(ExtensionResolver.class); + + ExtensionInstruction instruction = resolver.getExtensionInstruction(exType); + + byte[] bytes = (byte[]) kryo.readClassAndObject(input); + + instruction.deserialize(bytes); + + return Instructions.extension(instruction, deviceId); + } +} diff --git a/framework/src/onos/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java b/framework/src/onos/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java index 5b5056cb..0312bafd 100644 --- a/framework/src/onos/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java +++ b/framework/src/onos/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java @@ -70,6 +70,7 @@ import org.onosproject.net.OchPort; import org.onosproject.net.OchSignal; import org.onosproject.net.OchSignalType; import org.onosproject.net.OduCltPort; +import org.onosproject.net.OduSignalId; import org.onosproject.net.OduSignalType; import org.onosproject.net.OmsPort; import org.onosproject.net.Port; @@ -118,6 +119,8 @@ import org.onosproject.net.flow.criteria.MetadataCriterion; import org.onosproject.net.flow.criteria.MplsCriterion; import org.onosproject.net.flow.criteria.OchSignalCriterion; import org.onosproject.net.flow.criteria.OchSignalTypeCriterion; +import org.onosproject.net.flow.criteria.OduSignalIdCriterion; +import org.onosproject.net.flow.criteria.OduSignalTypeCriterion; import org.onosproject.net.flow.criteria.PortCriterion; import org.onosproject.net.flow.criteria.SctpPortCriterion; import org.onosproject.net.flow.criteria.TcpPortCriterion; @@ -125,8 +128,10 @@ import org.onosproject.net.flow.criteria.TunnelIdCriterion; import org.onosproject.net.flow.criteria.UdpPortCriterion; import org.onosproject.net.flow.criteria.VlanIdCriterion; import org.onosproject.net.flow.criteria.VlanPcpCriterion; +import org.onosproject.net.flow.instructions.ExtensionType; import org.onosproject.net.flow.instructions.Instructions; import org.onosproject.net.flow.instructions.L0ModificationInstruction; +import org.onosproject.net.flow.instructions.L1ModificationInstruction; import org.onosproject.net.flow.instructions.L2ModificationInstruction; import org.onosproject.net.flow.instructions.L3ModificationInstruction; import org.onosproject.net.flow.instructions.L4ModificationInstruction; @@ -337,6 +342,8 @@ public final class KryoNamespaces { IndexedLambdaCriterion.class, OchSignalCriterion.class, OchSignalTypeCriterion.class, + OduSignalIdCriterion.class, + OduSignalTypeCriterion.class, Criterion.class, Criterion.Type.class, DefaultTrafficTreatment.class, @@ -349,6 +356,9 @@ public final class KryoNamespaces { L0ModificationInstruction.L0SubType.class, L0ModificationInstruction.ModLambdaInstruction.class, L0ModificationInstruction.ModOchSignalInstruction.class, + L1ModificationInstruction.class, + L1ModificationInstruction.L1SubType.class, + L1ModificationInstruction.ModOduSignalIdInstruction.class, L2ModificationInstruction.class, L2ModificationInstruction.L2SubType.class, L2ModificationInstruction.ModEtherInstruction.class, @@ -441,6 +451,8 @@ public final class KryoNamespaces { .register(new HostLocationSerializer(), HostLocation.class) .register(new DefaultOutboundPacketSerializer(), DefaultOutboundPacket.class) .register(new AnnotationsSerializer(), DefaultAnnotations.class) + .register(new ExtensionInstructionSerializer(), Instructions.ExtensionInstructionWrapper.class) + .register(ExtensionType.class) .register(Versioned.class) .register(MapEvent.class) .register(MapEvent.Type.class) @@ -458,6 +470,7 @@ public final class KryoNamespaces { .register(OduCltPort.SignalType.class) .register(IndexedLambda.class) .register(OchSignal.class) + .register(OduSignalId.class) .register(OduCltPortDescription.class) .register(OchPortDescription.class) .register(OmsPortDescription.class) |