diff --git a/apps/openstackvtap/BUCK b/apps/openstackvtap/BUCK index e8ee83b876..e1242598a1 100644 --- a/apps/openstackvtap/BUCK +++ b/apps/openstackvtap/BUCK @@ -4,12 +4,12 @@ BUNDLES = [ ] onos_app ( - title = 'OpenStack vTap Application', + title = 'Openstack Vtap Application', category = 'Integration', url = 'https://wiki.onosproject.org/display/ONOS/SONA%3A+DC+Network+Virtualization', included_bundles = BUNDLES, - description = 'SONA Openstack vTap Application.', + description = 'SONA Openstack Vtap Application.', required_apps = [ - 'org.onosproject.openstacknetworking' + 'org.onosproject.openstacknetworking', ] ) diff --git a/apps/openstackvtap/BUILD b/apps/openstackvtap/BUILD index fad480dafe..35db3b9e55 100644 --- a/apps/openstackvtap/BUILD +++ b/apps/openstackvtap/BUILD @@ -4,12 +4,13 @@ BUNDLES = [ ] onos_app( + app_name = "org.onosproject.openstackvtap", category = "Integration", - description = "SONA Openstack vTap Application.", + description = "Openstack Vtap Application.", included_bundles = BUNDLES, required_apps = [ "org.onosproject.openstacknetworking", ], - title = "OpenStack vTap Application", + title = "Openstack Vtap Application", url = "https://wiki.onosproject.org/display/ONOS/SONA%3A+DC+Network+Virtualization", ) diff --git a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtap.java b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtap.java index 6f74e81edd..ad189e2f58 100644 --- a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtap.java +++ b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtap.java @@ -22,15 +22,14 @@ import org.onosproject.net.SparseAnnotations; import java.util.Set; /** - * Abstraction of an OpenstackVtap. + * Abstraction of an openstack vtap. */ public interface OpenstackVtap extends Annotated { /** - * Openstack vTap type. + * List of openstack vtap types. */ enum Type { - /** * Indicates mirroring both TX and RX traffic. */ @@ -47,9 +46,9 @@ public interface OpenstackVtap extends Annotated { VTAP_TX(0x1), /** - * Indicates do not mirroring any traffic. + * Indicates selection of mirroring any traffic. */ - VTAP_NONE(0); + VTAP_ANY(0x0); private final int type; @@ -63,97 +62,96 @@ public interface OpenstackVtap extends Annotated { } /** - * OpenstackVtap identifier. + * Returns the openstack vtap identifier. * - * @return OpenstackVtap id + * @return vtap ID */ OpenstackVtapId id(); /** - * Returns OpenstackVtap type. + * Returns the openstack vtap type. * - * @return type + * @return type of vtap */ Type type(); /** - * Returns the vTap criterion. + * Returns the openstack vtap criterion. * - * @return vTap criterion + * @return criterion of vtap */ - OpenstackVtapCriterion vTapCriterion(); + OpenstackVtapCriterion vtapCriterion(); /** - * Returns a collection of TX device identifiers. + * Returns a collection of device identifiers for tx. * - * @return device identifiers + * @return device identifiers for tx */ Set txDeviceIds(); /** - * Returns a collection of RX device identifiers. + * Returns a collection of device identifiers for rx. * - * @return device identifiers + * @return device identifiers for rx */ Set rxDeviceIds(); /** - * Builder of new openstack vTap instance. + * Builder of new openstack vtap instance. */ interface Builder { - /** - * Returns openstack vTap builder with supplied vTap identifier. + * Returns openstack vtap builder with supplied id. * - * @param id vTap identifier - * @return openstack vTap builder + * @param id openstack vtap id + * @return openstack vtap builder */ Builder id(OpenstackVtapId id); /** - * Returns openstack vTap builder with supplied vTap type. + * Returns openstack vtap builder with supplied type. * - * @param type vTap type - * @return openstack vTap builder + * @param type of the vtap + * @return openstack vtap builder */ Builder type(OpenstackVtap.Type type); /** - * Returns openstack vTap builder with supplied vTap criterion. + * Returns openstack vtap builder with supplied criterion. * - * @param vTapCriterion vTap criterion - * @return openstack vTap builder + * @param vtapCriterion for the vtap + * @return openstack vtap builder */ - Builder vTapCriterion(OpenstackVtapCriterion vTapCriterion); + Builder vtapCriterion(OpenstackVtapCriterion vtapCriterion); /** - * Returns openstack vTap builder with supplied TX device identifiers. + * Returns openstack vtap builder with supplied tx deviceId set. * - * @param txDeviceIds TX device identifiers - * @return openstack vTap builder + * @param txDeviceIds deviceId set for tx + * @return openstack vtap builder */ Builder txDeviceIds(Set txDeviceIds); /** - * Returns openstack vTap builder with supplied RX device identifiers. + * Returns openstack vtap builder with supplied rx deviceId set. * - * @param rxDeviceIds RX device identifiers - * @return openstack vTap builder + * @param rxDeviceIds deviceId set for rx + * @return openstack vtap builder */ Builder rxDeviceIds(Set rxDeviceIds); /** - * Returns openstack vTap builder with supplied annotations. + * Returns openstack vtap builder with supplied annotations. * * @param annotations a set of annotations - * @return openstack vTap builder + * @return openstack vtap builder */ - Builder annotations(SparseAnnotations... annotations); + Builder annotations(SparseAnnotations annotations); /** - * Builds an immutable openstack vTap instance. + * Builds an immutable OpenstackVtap instance. * - * @return openstack vTap instance + * @return openstack vtap instance */ OpenstackVtap build(); } diff --git a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapAdminService.java b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapAdminService.java index da9fd2462d..1828beef6b 100644 --- a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapAdminService.java +++ b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapAdminService.java @@ -15,60 +15,24 @@ */ package org.onosproject.openstackvtap.api; -import org.onlab.packet.VlanId; -import org.onosproject.net.DeviceId; -import org.onosproject.net.PortNumber; - /** - * Service for administering the inventory of vTap. + * Service for administering the inventory of openstack vtap. */ public interface OpenstackVtapAdminService extends OpenstackVtapService { /** - * Creates a new vTap based on the specified description. - * - * @param type vTap type - * @param vTapCriterion criterion of a vTap - * @return created vTap object or null if error occurred + * Initializes the flow rules and group tables, tunneling interface for all completed compute nodes. */ - OpenstackVtap createVtap(OpenstackVtap.Type type, OpenstackVtapCriterion vTapCriterion); + void initVtap(); /** - * Updates the existing vTap based on the given vTap instance. - * - * @param vTapId vTap identifier - * @param vTap vTap instance to be modified - * @return updated vTap object or null if error occurred + * Clears the flow rules and group tables, tunneling interfaces for all compute nodes. */ - OpenstackVtap updateVtap(OpenstackVtapId vTapId, OpenstackVtap vTap); + void clearVtap(); /** - * Removes the specified vTap with given vTap identifier. - * - * @param vTapId vTap identifier - * @return removed vTap object or null if error occurred + * Purges all flow rules and group tables, tunneling interface for openstack vtap. */ - OpenstackVtap removeVtap(OpenstackVtapId vTapId); + void purgeVtap(); - /** - * Sets output port and VLAN tag for vTap. - * - * @param deviceId device identifier - * @param type vTap type - * @param portNumber port number - * @param vlanId VLAN tag - */ - void setVtapOutput(DeviceId deviceId, OpenstackVtap.Type type, - PortNumber portNumber, VlanId vlanId); - - /** - * Sets output port and VNI for vTap. - * - * @param deviceId device identifier - * @param type vTap type - * @param portNumber port number - * @param vni virtual network index (VNI) of VxLAN - */ - void setVtapOutput(DeviceId deviceId, OpenstackVtap.Type type, - PortNumber portNumber, int vni); } diff --git a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapCriterion.java b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapCriterion.java index eb21b0c4dc..088f12d4ab 100644 --- a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapCriterion.java +++ b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapCriterion.java @@ -19,35 +19,35 @@ import org.onlab.packet.IpPrefix; import org.onlab.packet.TpPort; /** - * A vTAP criterion used for mirroring traffic. + * A vtap criterion used for mirroring traffic. */ public interface OpenstackVtapCriterion { /** - * Returns IP Prefix of Source VM. + * Returns IP prefix of source. * - * @return source IP prefix + * @return source IP prefix of vtap criterion */ IpPrefix srcIpPrefix(); /** - * Returns IP Prefix of Destination VM. + * Returns IP prefix of destination. * - * @return destination IP prefix + * @return destination IP prefix of vtap criterion */ IpPrefix dstIpPrefix(); /** * Returns IP protocol. * - * @return IP protocol + * @return IP protocol of vtap criterion */ byte ipProtocol(); /** * Returns source transport port. * - * @return source transport port number + * @return source transport port of vtap criterion */ TpPort srcTpPort(); @@ -55,60 +55,59 @@ public interface OpenstackVtapCriterion { /** * Returns destination transport port. * - * @return destination transport port number + * @return destination transport port of vtap criterion */ TpPort dstTpPort(); /** - * Builder of new openstack vTap criteria. + * Builder of new OpenstackVtapCriterion instance. */ interface Builder { - /** - * Builds an immutable openstack vTap criterion instance. + * Returns openstack vtap criterion builder with supplied source IP prefix. * - * @return openstack vTap criterion - */ - OpenstackVtapCriterion build(); - - /** - * Returns openstack vTap criterion builder with supplied srcIpPrefix. - * - * @param srcIpPrefix Source IP address - * @return openstack vTap criterion builder + * @param srcIpPrefix Source IP prefix + * @return openstack vtap criterion builder */ Builder srcIpPrefix(IpPrefix srcIpPrefix); /** - * Returns openstack vTap criterion builder with supplied srcIpPrefix. + * Returns openstack vtap criterion builder with supplied destination IP prefix. * - * @param dstIpPrefix Destination IP Prefix - * @return openstack vTap criterion builder + * @param dstIpPrefix Destination IP prefix + * @return openstack vtap criterion builder */ Builder dstIpPrefix(IpPrefix dstIpPrefix); /** - * Returns openstack vTap criterion builder with supplied ipProtocol. + * Returns openstack vtap criterion builder with supplied ipProtocol. * - * @param ipProtocol IP protocol number - * @return openstack vTap criterion builder + * @param ipProtocol IP protocol + * @return openstack vtap criterion builder */ Builder ipProtocol(byte ipProtocol); /** - * Returns openstack vTap criterion builder with supplied srcTpPort. + * Returns openstack vtap criterion builder with supplied source port. * - * @param srcTpPort Source transport port number - * @return openstack vTap criterion builder + * @param srcTpPort Source transport port + * @return openstack vtap criterion builder */ Builder srcTpPort(TpPort srcTpPort); /** - * Returns openstack vTap criterion builder with supplied dstTpPort. + * Returns openstack vtap criterion builder with supplied destination port. * - * @param dstTpPort Destination transport port number - * @return openstack vTap criterion builder + * @param dstTpPort Destination transport port + * @return openstack vtap criterion builder */ Builder dstTpPort(TpPort dstTpPort); + + /** + * Builds an immutable OpenstackVtapCriterion instance. + * + * @return OpenstackVtapCriterion criterion + */ + OpenstackVtapCriterion build(); } } diff --git a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapEvent.java b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapEvent.java index 70c1e11571..d44c9f99fd 100644 --- a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapEvent.java +++ b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapEvent.java @@ -19,81 +19,166 @@ import org.onlab.util.Tools; import org.onosproject.event.AbstractEvent; import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkState; /** - * Describes vTap event. + * Describes openstack vtap event. */ -public class OpenstackVtapEvent extends AbstractEvent { +public class OpenstackVtapEvent extends AbstractEvent { + + private static final String INVALID_OBJ_TYPE = "Invalid OpenstackVtapEvent object type of %"; /** - * Type of vTap events. + * Type of openstack vtap events. */ public enum Type { /** - * Signifies that a new vTap has been added. + * Signifies that a new openstack vtap network has been added. + */ + VTAP_NETWORK_ADDED, + + /** + * Signifies that a openstack vtap network has been changed. + */ + VTAP_NETWORK_UPDATED, + + /** + * Signifies that a openstack vtap network has been removed. + */ + VTAP_NETWORK_REMOVED, + + /** + * Signifies that a new openstack vtap has been added. */ VTAP_ADDED, /** - * Signifies that a vTap has been removed. - */ - VTAP_REMOVED, - - /** - * Signifies that a vTap data changed. + * Signifies that a openstack vtap has been changed. */ VTAP_UPDATED, + + /** + * Signifies that a openstack vtap has been removed. + */ + VTAP_REMOVED, } - private OpenstackVtap prevSubject; + private final Object prevSubject; /** - * Creates an event of a given type and for the specified vTap and the - * current time. + * Creates an event with previous openstack vtap network subject. * - * @param type vTap event type - * @param vTap event vTap subject + * The openstack vtap network subject is null if the type is removed + * The previous openstack vtap network subject is null if the type is added + * + * @param type openstack vtap event type + * @param vtapNetwork event openstack vtap network subject + * @param prevVtapNetwork previous openstack vtap network subject */ - public OpenstackVtapEvent(Type type, OpenstackVtap vTap) { - super(type, vTap); + public OpenstackVtapEvent(Type type, + OpenstackVtapNetwork vtapNetwork, + OpenstackVtapNetwork prevVtapNetwork) { + super(type, vtapNetwork); + prevSubject = prevVtapNetwork; } /** - * Creates an event of a given type and for the specified vTap and time. + * Creates an event of a given type and for the specified openstack vtap network and time. * - * @param type vTap event type - * @param vTap event vTap subject - * @param time occurrence time + * @param type openstack vtap event type + * @param vtapNetwork event openstack vtap network subject + * @param prevVtapNetwork previous openstack vtap network subject + * @param time occurrence time */ - public OpenstackVtapEvent(Type type, OpenstackVtap vTap, long time) { - super(type, vTap, time); + public OpenstackVtapEvent(Type type, + OpenstackVtapNetwork vtapNetwork, + OpenstackVtapNetwork prevVtapNetwork, + long time) { + super(type, vtapNetwork, time); + prevSubject = prevVtapNetwork; } /** - * Creates an event with previous subject. + * Creates an event with previous openstack vtap subject. * - * The previous subject is ignored if the type is not moved or updated + * The openstack vtap subject is null if the type is removed + * The previous openstack vtap subject is null if the type is added * - * @param type vTap event type - * @param vTap event vTap subject - * @param prevSubject previous vTap subject + * @param type openstack vtap event type + * @param vtap event openstack vtap subject + * @param prevVtap previous openstack vtap subject */ - public OpenstackVtapEvent(Type type, OpenstackVtap vTap, OpenstackVtap prevSubject) { - super(type, vTap); - if (type == Type.VTAP_UPDATED) { - this.prevSubject = prevSubject; - } + public OpenstackVtapEvent(Type type, OpenstackVtap vtap, OpenstackVtap prevVtap) { + super(type, vtap); + prevSubject = prevVtap; } /** - * Gets the previous subject in this vTap event. + * Creates an event of a given type and for the specified openstack vtap and time. + * + * @param type openstack vtap event type + * @param vtap event openstack vtap subject + * @param prevVtap previous openstack vtap subject + * @param time occurrence time + */ + public OpenstackVtapEvent(Type type, OpenstackVtap vtap, OpenstackVtap prevVtap, long time) { + super(type, vtap, time); + prevSubject = prevVtap; + } + + /** + * Gets the previous subject in this openstack vtap event. * * @return the previous subject, or null if previous subject is not * specified. */ - public OpenstackVtap prevSubject() { - return this.prevSubject; + public Object prevSubject() { + return prevSubject; + } + + /** + * Gets the openstack vtap network in this openstack vtap event. + * + * @return the subject, or null if the type is removed + */ + public OpenstackVtapNetwork openstackVtapNetwork() { + Object obj = subject(); + checkState(obj == null || obj instanceof OpenstackVtapNetwork, INVALID_OBJ_TYPE, obj); + return (OpenstackVtapNetwork) obj; + } + + /** + * Gets the previous openstack vtap network in this openstack vtap event. + * + * @return the previous subject, or null if type is added + */ + public OpenstackVtapNetwork prevOpenstackVtapNetwork() { + Object obj = prevSubject; + checkState(obj == null || obj instanceof OpenstackVtapNetwork, INVALID_OBJ_TYPE, obj); + return (OpenstackVtapNetwork) obj; + } + + /** + * Gets the openstack vtap in this openstack vtap event. + * + * @return the subject, or null if the type is removed + */ + public OpenstackVtap openstackVtap() { + Object obj = subject(); + checkState(obj == null || obj instanceof OpenstackVtap, INVALID_OBJ_TYPE, obj); + return (OpenstackVtap) obj; + } + + /** + * Gets the previous openstack vtap in this openstack vtap event. + * + * @return the previous subject, or null if type is added + */ + public OpenstackVtap prevOpenstackVtap() { + Object obj = prevSubject; + checkState(obj == null || obj instanceof OpenstackVtap, INVALID_OBJ_TYPE, obj); + return (OpenstackVtap) obj; } @Override diff --git a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapId.java b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapId.java index ce8945dfb9..c70fb03d8c 100644 --- a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapId.java +++ b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapId.java @@ -19,12 +19,12 @@ import java.util.Objects; import java.util.UUID; /** - * Immutable representation of an openstack vTap identifier. + * Immutable representation of an openstack vtap identifier. */ public final class OpenstackVtapId { /** - * Represents either no vTap, or an unspecified vTap. + * Represents either no vtap, or an unspecified vtap. */ public static final OpenstackVtapId NONE = new OpenstackVtapId(null); @@ -50,35 +50,35 @@ public final class OpenstackVtapId { } /** - * Creates a vTap identifier using the supplied UUID. + * Creates a vtap identifier using the supplied UUID. * - * @param uuid vTap UUID - * @return vTap identifier + * @param uuid vtap UUID + * @return vtap identifier */ - public static OpenstackVtapId vTapId(UUID uuid) { + public static OpenstackVtapId vtapId(UUID uuid) { return new OpenstackVtapId(uuid); } /** - * Creates a vTap identifier using the supplied string format of UUID. + * Creates a vtap identifier using the supplied string format of UUID. * - * @param uuidString vTap UUID - * @return vTap identifier + * @param uuidString vtap UUID + * @return vtap identifier */ - public static OpenstackVtapId vTapId(String uuidString) { + public static OpenstackVtapId vtapId(String uuidString) { try { - return vTapId(UUID.fromString(uuidString)); + return vtapId(UUID.fromString(uuidString)); } catch (Exception e) { throw new IllegalArgumentException("Invalid UUID string: " + uuidString); } } /** - * Creates a OpenstackVtap id using random uuid. + * Creates a openstack vtap id using random uuid. * - * @return OpenstackVtap identifier + * @return openstack vtap identifier */ - public static OpenstackVtapId vTapId() { + public static OpenstackVtapId vtapId() { return new OpenstackVtapId(UUID.randomUUID()); } diff --git a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapListener.java b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapListener.java index ebea5edd27..9df73847ce 100644 --- a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapListener.java +++ b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapListener.java @@ -18,7 +18,7 @@ package org.onosproject.openstackvtap.api; import org.onosproject.event.EventListener; /** - * Entity capable of receiving user related events. + * Entity capable of receiving openstack vtap related events. */ public interface OpenstackVtapListener extends EventListener { } diff --git a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapNetwork.java b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapNetwork.java new file mode 100644 index 0000000000..daa95a851c --- /dev/null +++ b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapNetwork.java @@ -0,0 +1,106 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.openstackvtap.api; + +import org.onlab.packet.IpAddress; +import org.onosproject.net.Annotated; +import org.onosproject.net.SparseAnnotations; + +/** + * Abstraction of an openstack vtap network. + */ +public interface OpenstackVtapNetwork extends Annotated { + + /** + * List of valid openstack vtap tunneling modes. + */ + enum Mode { + /** + * Indicates GRE tunneling. + */ + GRE, + + /** + * Indicates VXLAN tunneling. + */ + VXLAN + } + + /** + * Returns the OpenstackVtapNetwork mode. + * + * @return mode of vtap tunneling + */ + Mode mode(); + + /** + * Returns the network id of the vtap tunneling. + * + * @return networkId (e.g., gre key, vxlan vni) + */ + Integer networkId(); + + /** + * Returns the vtap server IP address used for tunneling. + * + * @return ip address for vtap server + */ + IpAddress serverIp(); + + /** + * Builder of new OpenstackVtapNetwork instance. + */ + interface Builder { + /** + * Returns openstack vtap network builder with supplied OpenstackVtapNetwork mode. + * + * @param mode mode of vtap tunneling + * @return openstack vtap network builder + */ + Builder mode(Mode mode); + + /** + * Returns openstack vtap network builder with supplied networkId for tunneling. + * + * @param networkId (e.g., gre key, vxlan vni) + * @return openstack vtap network builder + */ + Builder networkId(Integer networkId); + + /** + * Returns openstack vtap network builder with supplied server IP address. + * + * @param serverIp ip address for vtap server + * @return openstack vtap network builder + */ + Builder serverIp(IpAddress serverIp); + + /** + * Returns openstack vtap network builder with supplied annotations. + * + * @param annotations a set of annotations + * @return openstack vtap network builder + */ + Builder annotations(SparseAnnotations annotations); + + /** + * Builds an immutable OpenstackVtapNetwork instance. + * + * @return OpenstackVtapNetwork instance + */ + OpenstackVtapNetwork build(); + } +} diff --git a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapService.java b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapService.java index 5041aafa96..bfb63a0671 100644 --- a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapService.java +++ b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapService.java @@ -15,47 +15,111 @@ */ package org.onosproject.openstackvtap.api; +import org.onlab.packet.IpAddress; import org.onosproject.event.ListenerService; import org.onosproject.net.DeviceId; import java.util.Set; /** - * Service for interacting with the inventory of vTap. + * Service for interacting with the inventory of openstack vtap. */ public interface OpenstackVtapService extends ListenerService { /** - * Returns the number of vTaps in the store. + * Creates a new openstack vtap network based on the specified description. * - * @param type vTap type - * @return vTap count + * @param mode mode of vtap network + * @param networkId network id of the vtap tunneling network + * @param serverIp server IP address used for tunneling + * @return created openstack vtap network object or null if error occurred + */ + OpenstackVtapNetwork createVtapNetwork(OpenstackVtapNetwork.Mode mode, Integer networkId, IpAddress serverIp); + + /** + * Updates the openstack vtap network based on the specified description. + * + * @param description description of vtap network + * @return updated openstack vtap network object or null if error occurred + */ + OpenstackVtapNetwork updateVtapNetwork(OpenstackVtapNetwork description); + + /** + * Removes the specified openstack vtap network. + * + * @return removed openstack vtap network object or null if error occurred + */ + OpenstackVtapNetwork removeVtapNetwork(); + + /** + * Returns the openstack vtap network. + * + * @return openstack vtap network or null if not exists + */ + OpenstackVtapNetwork getVtapNetwork(); + + /** + * Returns a set of devices which are associated with the openstack vtap network. + * + * @return set of devices + */ + Set getVtapNetworkDevices(); + + /** + * Creates a new openstack vtap based on the specified type and criterion. + * + * @param type type of vtap (all,rx,tx) + * @param vtapCriterion criterion of vtap + * @return created openstack vtap object or null if error occurred + */ + OpenstackVtap createVtap(OpenstackVtap.Type type, OpenstackVtapCriterion vtapCriterion); + + /** + * Updates the openstack vtap with specified description. + * + * @param description description of vtap + * @return updated openstack vtap object or null if error occurred + */ + OpenstackVtap updateVtap(OpenstackVtap description); + + /** + * Removes the specified openstack vtap with given vtap identifier. + * + * @param vtapId vtap identifier + * @return removed openstack vtap object or null if error occurred + */ + OpenstackVtap removeVtap(OpenstackVtapId vtapId); + + /** + * Returns the number of openstack vtaps for the given type. + * + * @param type type of vtap (any,rx,tx,all) + * @return number of openstack vtaps */ int getVtapCount(OpenstackVtap.Type type); /** - * Returns a collection of selected vTaps in the store. + * Returns a set of openstack vtaps for the given type. * - * @param type vTap type - * @return iterable collection of selected vTaps + * @param type type of vtap (any,rx,tx,all) + * @return set of openstack vtaps */ Set getVtaps(OpenstackVtap.Type type); /** - * Returns the vTap with the specified identifier. + * Returns the openstack vtap with the specified identifier. * - * @param vTapId vTap identifier - * @return vTap or null if not exist + * @param vtapId vtap identifier + * @return openstack vtap or null if one with the given identifier is not known */ - OpenstackVtap getVtap(OpenstackVtapId vTapId); + OpenstackVtap getVtap(OpenstackVtapId vtapId); /** - * Returns a collection of vTaps which are associated with the given device. + * Returns a set of openstack vtaps which are associated with the given device. * - * @param type vTap type - * @param deviceId device identifier - * @return a set of vTaps + * @param deviceId device identifier + * @return set of openstack vtaps */ - Set getVtapsByDeviceId(OpenstackVtap.Type type, DeviceId deviceId); + Set getVtapsByDeviceId(DeviceId deviceId); } diff --git a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapStore.java b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapStore.java index 15c945e2b7..da9364d2bb 100644 --- a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapStore.java +++ b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/OpenstackVtapStore.java @@ -21,92 +21,162 @@ import org.onosproject.store.Store; import java.util.Set; /** - * Manages inventory of OpenstackVtap states; not intended for direct use. + * Manages inventory of openstack vtap datum; not intended for direct use. */ public interface OpenstackVtapStore extends Store { /** - * Creates a new vTap or updates the existing one based on the specified - * description. + * Creates a new openstack vtap network based on the specified description. * - * @param vTapId vTap identifier - * @param description vTap description data - * @param replaceFlag replace device set if true, merge device set otherwise - * @return created or updated vTap object or null if error occurred + * @param key vtap network identifier + * @param description description of vtap network + * @return created openstack vtap network object or null if error occurred */ - OpenstackVtap createOrUpdateVtap(OpenstackVtapId vTapId, OpenstackVtap description, boolean replaceFlag); + OpenstackVtapNetwork createVtapNetwork(Integer key, OpenstackVtapNetwork description); /** - * Removes the specified vTap from the inventory by the given vTap identifier. + * Updates the openstack vtap network based on the specified description. * - * @param vTapId vTap identifier - * @return removed vTap object or null if error occurred + * @param key vtap network identifier + * @param description description of vtap network + * @return updated openstack vtap network object or null if error occurred */ - OpenstackVtap removeVtapById(OpenstackVtapId vTapId); + OpenstackVtapNetwork updateVtapNetwork(Integer key, OpenstackVtapNetwork description); /** - * Adds the specified device id from the vTap entry. + * Removes the specified openstack vtap network with the specified identifier. * - * @param vTapId vTap identification - * @param type vTap type - * @param deviceId device identifier to be added + * @param key vtap network identifier + * @return removed openstack vtap network object or null if error occurred + */ + OpenstackVtapNetwork removeVtapNetwork(Integer key); + + /** + * Removes all openstack vtap networks. + */ + void clearVtapNetworks(); + + /** + * Returns the number of openstack vtap networks. + * + * @return number of openstack vtap networks + */ + int getVtapNetworkCount(); + + /** + * Returns the openstack vtap network with the specified identifier. + * + * @param key vtap network identifier + * @return openstack vtap or null if one with the given identifier is not known + */ + OpenstackVtapNetwork getVtapNetwork(Integer key); + + /** + * Adds the specified device id to the specified vtap network entry. + * + * @param key vtap network identifier + * @param deviceId device identifier to be added * @return true on success, false otherwise */ - boolean addDeviceToVtap(OpenstackVtapId vTapId, OpenstackVtap.Type type, DeviceId deviceId); + boolean addDeviceToVtapNetwork(Integer key, DeviceId deviceId); /** - * Removes the specified device id from the vTap entry. + * Removes the specified device id from the specified vtap network entry. * - * @param vTapId vTap identification - * @param type vTap type - * @param deviceId device identifier to be removed + * @param key vtap network identifier + * @param deviceId device identifier to be removed * @return true on success, false otherwise */ - boolean removeDeviceFromVtap(OpenstackVtapId vTapId, OpenstackVtap.Type type, DeviceId deviceId); + boolean removeDeviceFromVtapNetwork(Integer key, DeviceId deviceId); /** - * Adds the specified device id from the vTap entry. + * Returns a set of device identifiers from the specified vtap network entry. * - * @param vTapId vTap identification - * @param txDeviceIds TX device identifiers to be updated - * @param rxDeviceIds RX device identifiers to be updated - * @param replaceFlag replace device set if true, merge device set otherwise - * @return true on success, false otherwise + * @param key vtap network identifier + * @return set of device identifier */ - boolean updateDeviceForVtap(OpenstackVtapId vTapId, Set txDeviceIds, - Set rxDeviceIds, boolean replaceFlag); + Set getVtapNetworkDevices(Integer key); /** - * Returns the number of vTaps in the store. + * Creates a new openstack vtap based on the specified description. * - * @param type vTap type - * @return vTap count + * @param description description of vtap + * @return created openstack vtap object or null if error occurred + */ + OpenstackVtap createVtap(OpenstackVtap description); + + /** + * Updates the openstack vtap with specified description. + * + * @param description description of vtap + * @param replaceDevices replace device set if true, merge device set otherwise + * @return updated openstack vtap object or null if error occurred + */ + OpenstackVtap updateVtap(OpenstackVtap description, boolean replaceDevices); + + /** + * Removes the specified openstack vtap with given vtap identifier. + * + * @param vtapId vtap identifier + * @return removed openstack vtap object or null if error occurred + */ + OpenstackVtap removeVtap(OpenstackVtapId vtapId); + + /** + * Removes all openstack vtaps. + */ + void clearVtaps(); + + /** + * Returns the number of openstack vtaps for the given type. + * + * @param type type of vtap (any,rx,tx,all) + * @return number of openstack vtaps */ int getVtapCount(OpenstackVtap.Type type); /** - * Returns a collection of selected vTaps in the store. + * Returns a set of openstack vtaps for the given type. * - * @param type vTap type - * @return iterable collection of selected vTaps + * @param type type of vtap (any,rx,tx,all) + * @return set of openstack vtaps */ Set getVtaps(OpenstackVtap.Type type); /** - * Returns the vTap with the specified identifier. + * Returns the openstack vtap with the specified identifier. * - * @param vTapId vTap identifier - * @return vtap or null if not found + * @param vtapId vtap identifier + * @return openstack vtap or null if one with the given identifier is not known */ - OpenstackVtap getVtap(OpenstackVtapId vTapId); + OpenstackVtap getVtap(OpenstackVtapId vtapId); /** - * Returns the set of vTaps whose included on device. + * Adds the specified device id to the specified vtap entry. * - * @param type vTap type - * @param deviceId device identifier - * @return set of vTaps + * @param vtapId vtap identifier + * @param type type of vtap (any,rx,tx,all) + * @param deviceId device identifier to be added + * @return true on success, false otherwise */ - Set getVtapsByDeviceId(OpenstackVtap.Type type, DeviceId deviceId); + boolean addDeviceToVtap(OpenstackVtapId vtapId, OpenstackVtap.Type type, DeviceId deviceId); + + /** + * Removes the specified device id from the vtap entry. + * + * @param vtapId vtap identifier + * @param type type of vtap (any,rx,tx,all) + * @param deviceId device identifier to be removed + * @return true on success, false otherwise + */ + boolean removeDeviceFromVtap(OpenstackVtapId vtapId, OpenstackVtap.Type type, DeviceId deviceId); + + /** + * Returns a set of openstack vtaps which are associated with the given device. + * + * @param deviceId device identifier + * @return set of openstack vtaps + */ + Set getVtapsByDeviceId(DeviceId deviceId); } diff --git a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/package-info.java b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/package-info.java index 77ad58c612..fe00c7cade 100644 --- a/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/package-info.java +++ b/apps/openstackvtap/api/src/main/java/org/onosproject/openstackvtap/api/package-info.java @@ -15,6 +15,6 @@ */ /** - * Application API for Openstack vTap. + * Application API for openstack vtap. */ package org.onosproject.openstackvtap.api; \ No newline at end of file diff --git a/apps/openstackvtap/app/BUCK b/apps/openstackvtap/app/BUCK index dc3f55c7ed..3656f6c396 100644 --- a/apps/openstackvtap/app/BUCK +++ b/apps/openstackvtap/app/BUCK @@ -25,8 +25,8 @@ osgi_jar_with_tests ( deps = COMPILE_DEPS, test_deps = TEST_DEPS, web_context = '/onos/openstackvtap', - api_title = 'OpenStack Network vTap REST API', + api_title = 'OpenStack Vtap REST API', api_version = '1.0', - api_description = 'OpenStack Network vTap REST API', + api_description = 'OpenStack Vtap REST API', api_package = 'org.onosproject.openstackvtap.web', ) diff --git a/apps/openstackvtap/app/BUILD b/apps/openstackvtap/app/BUILD index 078ed2b9b9..7d2cebaf0a 100644 --- a/apps/openstackvtap/app/BUILD +++ b/apps/openstackvtap/app/BUILD @@ -12,9 +12,9 @@ TEST_DEPS = TEST_ADAPTERS + TEST_REST + [ ] osgi_jar_with_tests( - api_description = "OpenStack Network vTap REST API", + api_description = "Openstack Vtap REST API", api_package = "org.onosproject.openstackvtap.web", - api_title = "OpenStack Network vTap REST API", + api_title = "Openstack Vtap REST API", api_version = "1.0", test_deps = TEST_DEPS, web_context = "/onos/openstackvtap", diff --git a/apps/openstackvtap/app/app.xml b/apps/openstackvtap/app/app.xml index 45024d987c..b077858397 100644 --- a/apps/openstackvtap/app/app.xml +++ b/apps/openstackvtap/app/app.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> ${project.description} diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapAddCommand.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapAddCommand.java index 8d9955fd37..5272083869 100644 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapAddCommand.java +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapAddCommand.java @@ -28,13 +28,13 @@ import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getProtocolTy import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getVtapTypeFromString; /** - * Command line interface for adding openstack vTap rule. + * Adds a openstack vtap rule. */ @Command(scope = "onos", name = "openstack-vtap-add", description = "OpenstackVtap activate") public class OpenstackVtapAddCommand extends AbstractShellCommand { - private final OpenstackVtapAdminService vTapService = + private final OpenstackVtapAdminService vtapService = get(OpenstackVtapAdminService.class); @Argument(index = 0, name = "srcIp", @@ -48,9 +48,9 @@ public class OpenstackVtapAddCommand extends AbstractShellCommand { String dstIp = ""; @Argument(index = 2, name = "ipProto", - description = "IP protocol [tcp|udp|icmp|none]", + description = "IP protocol [any|tcp|udp|icmp]", required = false, multiValued = false) - String ipProto = ""; + String ipProto = "any"; @Argument(index = 3, name = "srcTpPort", description = "source transport layer port (0 is skip)", @@ -63,43 +63,42 @@ public class OpenstackVtapAddCommand extends AbstractShellCommand { int dstTpPort = 0; @Argument(index = 5, name = "type", - description = "vTap type [all|tx|rx]", + description = "vtap type [all|rx|tx]", required = false, multiValued = false) - String vTapTypeStr = "all"; + String vtapTypeStr = "all"; @Override protected void execute() { - DefaultOpenstackVtapCriterion.Builder - defaultVtapCriterionBuilder = DefaultOpenstackVtapCriterion.builder(); - if (makeCriterion(defaultVtapCriterionBuilder)) { - OpenstackVtap.Type type = getVtapTypeFromString(vTapTypeStr); + DefaultOpenstackVtapCriterion.Builder vtapCriterionBuilder = DefaultOpenstackVtapCriterion.builder(); + if (makeCriterion(vtapCriterionBuilder)) { + OpenstackVtap.Type type = getVtapTypeFromString(vtapTypeStr); if (type == null) { - print("Invalid vTap type"); + print("Invalid vtap type"); return; } - OpenstackVtap vTap = vTapService.createVtap(type, defaultVtapCriterionBuilder.build()); - if (vTap != null) { - print("Created OpenstackVtap with id { %s }", vTap.id().toString()); + OpenstackVtap vtap = vtapService.createVtap(type, vtapCriterionBuilder.build()); + if (vtap != null) { + print("Created OpenstackVtap with id { %s }", vtap.id().toString()); } else { print("Failed to create OpenstackVtap"); } } } - private boolean makeCriterion(DefaultOpenstackVtapCriterion.Builder vTapCriterionBuilder) { + private boolean makeCriterion(DefaultOpenstackVtapCriterion.Builder vtapCriterionBuilder) { try { - vTapCriterionBuilder.srcIpPrefix(IpPrefix.valueOf(srcIp)); - vTapCriterionBuilder.dstIpPrefix(IpPrefix.valueOf(dstIp)); + vtapCriterionBuilder.srcIpPrefix(IpPrefix.valueOf(srcIp)); + vtapCriterionBuilder.dstIpPrefix(IpPrefix.valueOf(dstIp)); } catch (Exception e) { print("Inputted valid source IP & destination IP in CIDR (e.g., \"10.1.0.4/32\")"); return false; } - vTapCriterionBuilder.ipProtocol(getProtocolTypeFromString(ipProto.toLowerCase())); + vtapCriterionBuilder.ipProtocol(getProtocolTypeFromString(ipProto.toLowerCase())); - vTapCriterionBuilder.srcTpPort(TpPort.tpPort(srcTpPort)); - vTapCriterionBuilder.dstTpPort(TpPort.tpPort(dstTpPort)); + vtapCriterionBuilder.srcTpPort(TpPort.tpPort(srcTpPort)); + vtapCriterionBuilder.dstTpPort(TpPort.tpPort(dstTpPort)); return true; } diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapDeleteCommand.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapDeleteCommand.java index e4a167b919..e54f6ac48c 100644 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapDeleteCommand.java +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapDeleteCommand.java @@ -22,25 +22,25 @@ import org.onosproject.openstackvtap.api.OpenstackVtapAdminService; import org.onosproject.openstackvtap.api.OpenstackVtapId; /** - * Command line interface for removing openstack vTap rule. + * Delete a openstack vtap rule from the existing vtaps. */ @Command(scope = "onos", name = "openstack-vtap-del", description = "OpenstackVtap deactivate") public class OpenstackVtapDeleteCommand extends AbstractShellCommand { - private final OpenstackVtapAdminService vTapService = get(OpenstackVtapAdminService.class); + private final OpenstackVtapAdminService vtapService = get(OpenstackVtapAdminService.class); - @Argument(index = 0, name = "id", description = "vTap ID", + @Argument(index = 0, name = "id", description = "vtap ID", required = true, multiValued = false) - String vTapId = ""; + String vtapId = ""; @Override protected void execute() { - OpenstackVtap vTap = vTapService.removeVtap(OpenstackVtapId.vTapId(vTapId)); - if (vTap != null) { - print("Removed OpenstackVtap with id { %s }", vTap.id().toString()); + OpenstackVtap vtap = vtapService.removeVtap(OpenstackVtapId.vtapId(vtapId)); + if (vtap != null) { + print("Removed OpenstackVtap with id { %s }", vtap.id().toString()); } else { - print("Failed to remove OpenstackVtap with id { %s }", vTapId); + print("Failed to remove OpenstackVtap with id { %s }", vtapId); } } } diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapListCommand.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapListCommand.java index afb9bbf955..a0989b33d9 100644 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapListCommand.java +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapListCommand.java @@ -15,46 +15,66 @@ */ package org.onosproject.openstackvtap.cli; +import com.google.common.collect.ImmutableSet; import org.apache.karaf.shell.commands.Argument; import org.apache.karaf.shell.commands.Command; import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.net.DeviceId; +import org.onosproject.openstacknode.api.OpenstackNode; +import org.onosproject.openstacknode.api.OpenstackNodeService; import org.onosproject.openstackvtap.api.OpenstackVtap; import org.onosproject.openstackvtap.api.OpenstackVtapService; +import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getVtapTypeFromString; /** - * Command line interface for listing openstack vTap rules. + * Lists openstack vtap rules. */ @Command(scope = "onos", name = "openstack-vtap-list", description = "OpenstackVtap list") public class OpenstackVtapListCommand extends AbstractShellCommand { - private final OpenstackVtapService vTapService = get(OpenstackVtapService.class); + private final OpenstackVtapService vtapService = get(OpenstackVtapService.class); + private final OpenstackNodeService osNodeService = get(OpenstackNodeService.class); @Argument(index = 0, name = "type", - description = "vTap type [all|tx|rx]", + description = "vtap type [any|all|rx|tx]", required = false, multiValued = false) - String vTapType = "none"; + String vtapType = "any"; private static final String FORMAT = "ID { %s }: type [%s], srcIP [%s], dstIP [%s]"; - private static final String FORMAT_TX_DEVICES = " tx devices: %s"; - private static final String FORMAT_RX_DEVICES = " rx devices: %s"; + private static final String FORMAT_TX_NODES = " tx openstack nodes: %s"; + private static final String FORMAT_RX_NODES = " rx openstack nodes: %s"; @Override protected void execute() { - OpenstackVtap.Type type = getVtapTypeFromString(vTapType); - Set openstackVtaps = vTapService.getVtaps(type); - for (OpenstackVtap vTap : openstackVtaps) { + OpenstackVtap.Type type = getVtapTypeFromString(vtapType); + Set openstackVtaps = vtapService.getVtaps(type); + for (OpenstackVtap vtap : openstackVtaps) { print(FORMAT, - vTap.id().toString(), - vTap.type().toString(), - vTap.vTapCriterion().srcIpPrefix().toString(), - vTap.vTapCriterion().dstIpPrefix().toString()); - print(FORMAT_TX_DEVICES, vTap.txDeviceIds()); - print(FORMAT_RX_DEVICES, vTap.rxDeviceIds()); + vtap.id().toString(), + vtap.type().toString(), + vtap.vtapCriterion().srcIpPrefix().toString(), + vtap.vtapCriterion().dstIpPrefix().toString()); + print(FORMAT_TX_NODES, osNodeNames(vtap.txDeviceIds())); + print(FORMAT_RX_NODES, osNodeNames(vtap.rxDeviceIds())); } } + + private Set osNodeNames(Set deviceIds) { + if (deviceIds == null) { + return ImmutableSet.of(); + } else { + return deviceIds.parallelStream() + .map(osNodeService::node) + .filter(Objects::nonNull) + .map(OpenstackNode::hostname) + .collect(Collectors.toSet()); + } + } + } diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapNetworkListCommand.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapNetworkListCommand.java new file mode 100644 index 0000000000..68a94f834d --- /dev/null +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapNetworkListCommand.java @@ -0,0 +1,68 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.openstackvtap.cli; + +import com.google.common.collect.ImmutableSet; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.net.DeviceId; +import org.onosproject.openstacknode.api.OpenstackNode; +import org.onosproject.openstacknode.api.OpenstackNodeService; +import org.onosproject.openstackvtap.api.OpenstackVtapAdminService; +import org.onosproject.openstackvtap.api.OpenstackVtapNetwork; + +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Lists openstack vtap networks. + */ +@Command(scope = "onos", name = "openstack-vtap-network-list", + description = "OpenstackVtap network list") +public class OpenstackVtapNetworkListCommand extends AbstractShellCommand { + + private final OpenstackVtapAdminService osVtapAdminService = get(OpenstackVtapAdminService.class); + private final OpenstackNodeService osNodeService = get(OpenstackNodeService.class); + + private static final String FORMAT = "mode [%s], networkId [%d], serverIp [%s]"; + private static final String FORMAT_NODES = " openstack nodes: %s"; + + @Override + protected void execute() { + OpenstackVtapNetwork vtapNetwork = osVtapAdminService.getVtapNetwork(); + if (vtapNetwork != null) { + print(FORMAT, + vtapNetwork.mode().toString(), + vtapNetwork.networkId() != null ? vtapNetwork.networkId() : "N/A", + vtapNetwork.serverIp().toString()); + print(FORMAT_NODES, osNodeNames(osVtapAdminService.getVtapNetworkDevices())); + } + } + + private Set osNodeNames(Set deviceIds) { + if (deviceIds == null) { + return ImmutableSet.of(); + } else { + return deviceIds.parallelStream() + .map(osNodeService::node) + .filter(Objects::nonNull) + .map(OpenstackNode::hostname) + .collect(Collectors.toSet()); + } + } + +} diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapOutputCommand.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapOutputCommand.java deleted file mode 100644 index 257cb9a679..0000000000 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapOutputCommand.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2018-present Open Networking Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.openstackvtap.cli; - -import org.apache.karaf.shell.commands.Argument; -import org.apache.karaf.shell.commands.Command; -import org.onlab.packet.VlanId; -import org.onosproject.cli.AbstractShellCommand; -import org.onosproject.net.Device; -import org.onosproject.net.DeviceId; -import org.onosproject.net.PortNumber; -import org.onosproject.net.device.DeviceService; -import org.onosproject.openstackvtap.api.OpenstackVtap; -import org.onosproject.openstackvtap.api.OpenstackVtapAdminService; - -import static org.onlab.packet.VlanId.UNTAGGED; -import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getVtapTypeFromString; - -/** - * Command line interface for set openstack vTap output. - */ -@Command(scope = "onos", name = "openstack-vtap-output", - description = "OpenstackVtap output setup") -public class OpenstackVtapOutputCommand extends AbstractShellCommand { - - private final DeviceService deviceService = get(DeviceService.class); - private final OpenstackVtapAdminService vTapAdminService = - get(OpenstackVtapAdminService.class); - - @Argument(index = 0, name = "deviceId", description = "device id", - required = true, multiValued = false) - String id = ""; - - @Argument(index = 1, name = "port", description = "output port number", - required = true, multiValued = false) - int port = 0; - - @Argument(index = 2, name = "vlan", description = "vlan id", - required = false, multiValued = false) - int vlan = UNTAGGED; - - @Argument(index = 3, name = "type", description = "vTap type [all|tx|rx]", - required = false, multiValued = false) - String vTapTypeStr = "all"; - - @Override - protected void execute() { - try { - Device device = deviceService.getDevice(DeviceId.deviceId(id)); - if (device != null) { - OpenstackVtap.Type type = getVtapTypeFromString(vTapTypeStr); - - vTapAdminService.setVtapOutput(device.id(), type, - PortNumber.portNumber(port), VlanId.vlanId((short) vlan)); - print("Set OpenstackVtap output deviceId { %s }, port=%s, vlan=%s", - device.id().toString(), - PortNumber.portNumber(port).toString(), - VlanId.vlanId((short) vlan).toString()); - } else { - print("Invalid device id"); - } - } catch (Exception e) { - print("Invalid parameter: %s", e.toString()); - } - } -} diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/ProtocolTypeCompleter.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/ProtocolTypeCompleter.java index 3d0f747ff2..3bfcf2bb96 100644 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/ProtocolTypeCompleter.java +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/ProtocolTypeCompleter.java @@ -28,10 +28,10 @@ public class ProtocolTypeCompleter extends AbstractChoicesCompleter { @Override protected List choices() { List strings = Lists.newArrayList(); + strings.add("any"); strings.add("tcp"); strings.add("udp"); strings.add("icmp"); - strings.add("none"); return strings; } } diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapIdCompleter.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapIdCompleter.java index c53098ddee..dad6e8e686 100644 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapIdCompleter.java +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapIdCompleter.java @@ -27,11 +27,11 @@ import java.util.SortedSet; import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getVtapTypeFromString; /** - * vTap ID completer. + * Vtap ID completer. */ public class VtapIdCompleter implements Completer { - private static final String VTAP_TYPE = "none"; + private static final String VTAP_TYPE = "any"; @Override public int complete(String buffer, int cursor, List candidates) { diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapTypeCompleter.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapTypeCompleter.java index 2784833383..c77c913f3f 100644 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapTypeCompleter.java +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapTypeCompleter.java @@ -21,16 +21,17 @@ import org.onosproject.cli.AbstractChoicesCompleter; import java.util.List; /** - * vTap type completer. + * Vtap type completer. */ public class VtapTypeCompleter extends AbstractChoicesCompleter { @Override protected List choices() { List strings = Lists.newArrayList(); - strings.add("tx"); - strings.add("rx"); strings.add("all"); + strings.add("rx"); + strings.add("tx"); + strings.add("any"); return strings; } } diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/package-info.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/package-info.java index 772bbc8856..3b3dec16ac 100644 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/package-info.java +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/package-info.java @@ -15,6 +15,6 @@ */ /** - * Console commands for OpenStack vtap. + * Console commands for openstack vtap. */ package org.onosproject.openstackvtap.cli; \ No newline at end of file diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/codec/OpenstackVtapNetworkCodec.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/codec/OpenstackVtapNetworkCodec.java new file mode 100644 index 0000000000..6dd6b5c04f --- /dev/null +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/codec/OpenstackVtapNetworkCodec.java @@ -0,0 +1,73 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.openstackvtap.codec; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.onlab.packet.IpAddress; +import org.onosproject.codec.CodecContext; +import org.onosproject.codec.JsonCodec; +import org.onosproject.openstackvtap.api.OpenstackVtapNetwork; +import org.onosproject.openstackvtap.impl.DefaultOpenstackVtapNetwork; +import org.slf4j.Logger; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Openstack vtap network codec used for serializing and de-serializing JSON string. + */ +public final class OpenstackVtapNetworkCodec extends JsonCodec { + + private final Logger log = getLogger(getClass()); + + private static final String MODE = "mode"; + private static final String NETWORK_ID = "networkId"; + private static final String SERVER_IP = "serverIp"; + private static final String NODES = "nodes"; + + private static final String JSON_NULL_MESSAGE = "% cannot be null"; + private static final String JSON_MISSING_MESSAGE = "% is required"; + private static final String JSON_TYPE_MESSAGE = "% is not json object type"; + + @Override + public ObjectNode encode(OpenstackVtapNetwork network, CodecContext context) { + checkNotNull(network, JSON_NULL_MESSAGE, "OpenstackVtapNetwork object"); + + ObjectNode result = context.mapper().createObjectNode() + .put(MODE, network.mode().toString()) + .put(NETWORK_ID, network.networkId()) + .put(SERVER_IP, network.serverIp().toString()); + + return result; + } + + @Override + public OpenstackVtapNetwork decode(ObjectNode json, CodecContext context) { + checkNotNull(json, JSON_NULL_MESSAGE, "OpenstackVtapNetwork json"); + checkState(json.isObject(), JSON_TYPE_MESSAGE, "OpenstackVtapNetwork json"); + + DefaultOpenstackVtapNetwork.Builder builder = DefaultOpenstackVtapNetwork.builder() + .mode(OpenstackVtapNetwork.Mode.valueOf(checkNotNull(json.get(MODE).asText(null), + JSON_MISSING_MESSAGE, MODE))) + .networkId(json.get(NETWORK_ID).asInt(0)) + .serverIp(IpAddress.valueOf(checkNotNull(json.get(SERVER_IP).asText(null), + JSON_MISSING_MESSAGE, SERVER_IP))); + + log.debug("OpenstackVtapNetwork is {}", builder.build().toString()); + return builder.build(); + } +} diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/codec/package-info.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/codec/package-info.java new file mode 100644 index 0000000000..f76f7a98d8 --- /dev/null +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/codec/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Implementations of the codec broker and openstack vtap entity JSON codecs. + */ +package org.onosproject.openstackvtap.codec; \ No newline at end of file diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapUI.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapUI.java new file mode 100644 index 0000000000..c68c3385bf --- /dev/null +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapUI.java @@ -0,0 +1,85 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.openstackvtap.gui; + +import com.google.common.collect.ImmutableList; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.onosproject.ui.UiExtensionService; +import org.onosproject.ui.UiMessageHandlerFactory; +import org.onosproject.ui.UiTopoOverlayFactory; +import org.onosproject.ui.UiView; +import org.onosproject.ui.UiViewHidden; +import org.onosproject.ui.UiExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * Mechanism to stream data to the GUI. + */ +@Component(immediate = true, enabled = true) +@Service(value = OpenstackVtapUI.class) +public class OpenstackVtapUI { + private static final String OPENSTACK_VTAP_ID = "openstackvtap"; + private static final String RESOURCE_PATH = "gui"; + private static final ClassLoader CL = OpenstackVtapUI.class.getClassLoader(); + + private final Logger log = LoggerFactory.getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected UiExtensionService uiExtensionService; + + // Factory for UI message handlers + private final UiMessageHandlerFactory messageHandlerFactory = + () -> ImmutableList.of(new OpenstackVtapViewMessageHandler()); + + // List of application views + private final List views = ImmutableList.of( + new UiViewHidden(OPENSTACK_VTAP_ID) + ); + + // Factory for UI topology overlays + private final UiTopoOverlayFactory topoOverlayFactory = + () -> ImmutableList.of( + new OpenstackVtapUiTopovOverlay() + ); + + // Application UI extension + private final UiExtension uiExtension = + new UiExtension.Builder(CL, views) + .messageHandlerFactory(messageHandlerFactory) + .resourcePath(RESOURCE_PATH) + .topoOverlayFactory(topoOverlayFactory) + .build(); + + @Activate + protected void activate() { + uiExtensionService.register(uiExtension); + log.info("Started"); + } + + @Deactivate + protected void deactivate() { + uiExtensionService.unregister(uiExtension); + log.info("Stopped"); + } +} diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapUiTopovOverlay.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapUiTopovOverlay.java new file mode 100644 index 0000000000..287aa998a2 --- /dev/null +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapUiTopovOverlay.java @@ -0,0 +1,40 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.openstackvtap.gui; + +import org.onosproject.ui.UiTopoOverlay; + +public class OpenstackVtapUiTopovOverlay extends UiTopoOverlay { + private static final String OVERLAY_ID = "vtap-overlay"; + + public OpenstackVtapUiTopovOverlay() { + super(OVERLAY_ID); + } + + @Override + public void activate() { + super.activate(); + log.debug("Openstack VtapOverlay Activated"); + } + + @Override + public void deactivate() { + super.deactivate(); + log.debug("Openstack VtapOverlay Deactivated"); + } + +} + diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapViewMessageHandler.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapViewMessageHandler.java new file mode 100644 index 0000000000..f95bbc4f1b --- /dev/null +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapViewMessageHandler.java @@ -0,0 +1,230 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.openstackvtap.gui; + +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.ImmutableSet; +import org.onlab.osgi.ServiceDirectory; +import org.onlab.packet.IpAddress; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.ui.RequestHandler; +import org.onosproject.ui.UiConnection; +import org.onosproject.ui.UiMessageHandler; +import org.onosproject.net.host.HostService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.Collection; +import java.util.Set; +import java.util.Iterator; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.TpPort; +import org.onosproject.openstackvtap.api.OpenstackVtap; +import org.onosproject.openstackvtap.api.OpenstackVtapAdminService; +import org.onosproject.openstackvtap.impl.DefaultOpenstackVtapCriterion; + +import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getProtocolTypeFromString; +import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getVtapTypeFromString; + + +import static org.onosproject.net.HostId.hostId; + +/** + * Message handler for Openstack Vtap related messages. + */ +public class OpenstackVtapViewMessageHandler extends UiMessageHandler { + private final Logger log = LoggerFactory.getLogger(getClass()); + + private static final String OSV_IS_ACTIVATED_REQ = "openstackVtapIsActivatedRequest"; + private static final String OSV_IS_ACTIVATED_RESP = "openstackVtapIsActivatedResponse"; + private static final String OSV_CREATE_REQ = "openstackVtapCreateRequest"; + private static final String OSV_CREATE_RESP = "openstackVtapCreateResponse"; + + private static final String SOURCE = "src"; + private static final String DESTINATION = "dst"; + private static final String SOURCE_IP = "srcIp"; + private static final String DESTINATION_IP = "dstIp"; + private static final String SOURCE_TRANSPORT_PORT = "srcPort"; + private static final String DESTINATION_TRANSPORT_PORT = "dstPort"; + private static final String SOURCE_HOST_NAME = "srcName"; + private static final String DESTINATION_HOST_NAME = "dstName"; + private static final String IP_PROTOCOL = "ipProto"; + private static final String VTAP_TYPE = "vtapType"; + private static final String IP_PROTOCOL_LIST = "ipProtoList"; + private static final String VTAP_TYPE_LIST = "vtapTypeList"; + + private static final String RESULT = "result"; + private static final String VALUE = "value"; + private static final String SUCCESS = "Success"; + private static final String FAILED = "Failed"; + private static final String INVALID_VTAP_TYPE = "Invalid vtap type"; + private static final String FAILED_TO_CREATE_VTAP = "Failed to create OpenstackVtap"; + private static final String WRONG_IP_ADDRESS = + "Inputted valid source & destination IP in CIDR (e.g., \"10.1.0.4/32\")"; + private static final String INVALID_TRANSPORT_PORT = + "Invalid source & destination transport port has been entered"; + + private static final String[] IP_PROTOCOL_ARRAY = {"Any", "TCP", "UDP", "ICMP"}; + private static final String[] VTAP_TYPE_ARRAY = {"All", "RX", "TX"}; + + private HostService hostService; + private OpenstackVtapAdminService vtapService; + + @Override + public void init(UiConnection connection, ServiceDirectory directory) { + super.init(connection, directory); + + hostService = directory.get(HostService.class); + vtapService = directory.get(OpenstackVtapAdminService.class); + } + + @Override + protected Collection createRequestHandlers() { + return ImmutableSet.of( + new VtapIsActivatedRequestHandler(), + new VtapCreateRequestHandler() + ); + + } + + private final class VtapIsActivatedRequestHandler extends RequestHandler { + + private VtapIsActivatedRequestHandler() { + super(OSV_IS_ACTIVATED_REQ); + } + + @Override + public void process(ObjectNode payload) { + String srcId = string(payload, SOURCE, null); + String dstId = string(payload, DESTINATION, null); + + if (srcId != null && dstId != null) { + HostId sHostId = hostId(srcId); + HostId dHostId = hostId(dstId); + + Host sHost = hostService.getHost(sHostId); + Host dHost = hostService.getHost(dHostId); + + if (sHost != null && dHost != null) { + ArrayNode ipProtos = arrayNode(); + ArrayNode types = arrayNode(); + String sHostName = ipForHost(sHost); + String dHostName = ipForHost(dHost); + + for (String proto : IP_PROTOCOL_ARRAY) { + ipProtos.add(proto); + } + + for (String type : VTAP_TYPE_ARRAY) { + types.add(type); + } + + payload.put(SOURCE_HOST_NAME, sHostName); + payload.put(DESTINATION_HOST_NAME, dHostName); + payload.put(IP_PROTOCOL_LIST, ipProtos); + payload.put(VTAP_TYPE_LIST, types); + + sendMessage(OSV_IS_ACTIVATED_RESP, payload); + } + } + } + + // Returns the first of the given host's set of IP addresses as a string. + private String ipForHost(Host host) { + Set ipAddresses = host.ipAddresses(); + Iterator it = ipAddresses.iterator(); + return it.hasNext() ? it.next().toString() + "/32" : "unknown"; + } + } + + private final class VtapCreateRequestHandler extends RequestHandler { + private String srcIp; + private String dstIp; + private String ipProto; + private String srcTpPort; + private String dstTpPort; + private String vtapTypeStr; + private ObjectNode result = objectNode(); + + private VtapCreateRequestHandler() { + super(OSV_CREATE_REQ); + } + + @Override + public void process(ObjectNode payload) { + srcIp = string(payload, SOURCE_IP, null); + dstIp = string(payload, DESTINATION_IP, null); + ipProto = string(payload, IP_PROTOCOL, null); + srcTpPort = string(payload, SOURCE_TRANSPORT_PORT, null); + dstTpPort = string(payload, DESTINATION_TRANSPORT_PORT, null); + vtapTypeStr = string(payload, VTAP_TYPE, null); + log.trace("VtapCreateRequestHandler payload srcIp:{}, dstIp:{}, ipPro:{}, " + + "srcTpPort:{}, dstTpPort:{}, vtapType:{}", srcIp, dstIp, ipProto, + srcTpPort, dstTpPort, vtapTypeStr); + + DefaultOpenstackVtapCriterion.Builder vtapCriterionBuilder = DefaultOpenstackVtapCriterion.builder(); + if (makeCriterion(vtapCriterionBuilder)) { + OpenstackVtap.Type type = getVtapTypeFromString(vtapTypeStr.toLowerCase()); + if (type == null) { + log.warn(INVALID_VTAP_TYPE); + result.put(RESULT, FAILED); + result.put(VALUE, INVALID_VTAP_TYPE); + sendMessage(OSV_CREATE_RESP, result); + return; + } + + OpenstackVtap vtap = vtapService.createVtap(type, vtapCriterionBuilder.build()); + if (vtap != null) { + log.info("Created OpenstackVtap with id {}", vtap.id().toString()); + result.put(RESULT, SUCCESS); + result.put(VALUE, "vtap id: " + vtap.id().toString()); + } else { + log.warn(FAILED_TO_CREATE_VTAP); + result.put(RESULT, FAILED); + result.put(VALUE, FAILED_TO_CREATE_VTAP); + } + } + sendMessage(OSV_CREATE_RESP, result); + } + + private boolean makeCriterion(DefaultOpenstackVtapCriterion.Builder vtapCriterionBuilder) { + try { + vtapCriterionBuilder.srcIpPrefix(IpPrefix.valueOf(srcIp)); + vtapCriterionBuilder.dstIpPrefix(IpPrefix.valueOf(dstIp)); + } catch (Exception e) { + log.warn(WRONG_IP_ADDRESS); + result.put(RESULT, FAILED); + result.put(VALUE, WRONG_IP_ADDRESS); + return false; + } + + vtapCriterionBuilder.ipProtocol(getProtocolTypeFromString(ipProto.toLowerCase())); + + try { + vtapCriterionBuilder.srcTpPort(TpPort.tpPort(Integer.valueOf(srcTpPort))); + vtapCriterionBuilder.dstTpPort(TpPort.tpPort(Integer.valueOf(dstTpPort))); + } catch (Exception e) { + log.warn(INVALID_TRANSPORT_PORT); + result.put(RESULT, FAILED); + result.put(VALUE, INVALID_TRANSPORT_PORT); + return false; + } + + return true; + } + } +} diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/package-info.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/package-info.java new file mode 100644 index 0000000000..7521561030 --- /dev/null +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Web GUI for the control plane manager. + */ +package org.onosproject.openstackvtap.gui; diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtap.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtap.java index d5c842f2bb..cc258c041e 100644 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtap.java +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtap.java @@ -15,9 +15,7 @@ */ package org.onosproject.openstackvtap.impl; -import com.google.common.base.Objects; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; import org.onosproject.net.AbstractDescription; import org.onosproject.net.DefaultAnnotations; import org.onosproject.net.DeviceId; @@ -26,33 +24,47 @@ import org.onosproject.openstackvtap.api.OpenstackVtap; import org.onosproject.openstackvtap.api.OpenstackVtapCriterion; import org.onosproject.openstackvtap.api.OpenstackVtapId; +import java.util.Objects; import java.util.Set; import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; /** - * Default implementation of an immutable openstack vTap. + * Default implementation of an immutable OpenstackVtap. */ public final class DefaultOpenstackVtap extends AbstractDescription implements OpenstackVtap { private final OpenstackVtapId id; private final Type type; - private final OpenstackVtapCriterion vTapCriterion; + private final OpenstackVtapCriterion vtapCriterion; private final Set txDeviceIds; private final Set rxDeviceIds; - // private constructor not intended to use from external - private DefaultOpenstackVtap(OpenstackVtapId id, Type type, - OpenstackVtapCriterion vTapCriterion, - Set txDeviceIds, Set rxDeviceIds, + /** + * Creates an DefaultOpenstackVtap using the supplied information. + * + * @param id vtap identifier + * @param type type of vtap (all,rx,tx) + * @param vtapCriterion criterion of vtap + * @param txDeviceIds device identifiers applied by vtap tx + * @param rxDeviceIds device identifiers applied by vtap rx + * @param annotations optional key/value annotations + */ + private DefaultOpenstackVtap(OpenstackVtapId id, + Type type, + OpenstackVtapCriterion vtapCriterion, + Set txDeviceIds, + Set rxDeviceIds, SparseAnnotations... annotations) { super(annotations); - this.id = id; - this.type = type; - this.vTapCriterion = vTapCriterion; - this.txDeviceIds = txDeviceIds; - this.rxDeviceIds = rxDeviceIds; + this.id = checkNotNull(id); + this.type = checkNotNull(type); + this.vtapCriterion = checkNotNull(vtapCriterion); + this.txDeviceIds = Objects.nonNull(txDeviceIds) ? + ImmutableSet.copyOf(txDeviceIds) : ImmutableSet.of(); + this.rxDeviceIds = Objects.nonNull(rxDeviceIds) ? + ImmutableSet.copyOf(rxDeviceIds) : ImmutableSet.of(); } @Override @@ -66,129 +78,184 @@ public final class DefaultOpenstackVtap extends AbstractDescription implements O } @Override - public OpenstackVtapCriterion vTapCriterion() { - return vTapCriterion; + public OpenstackVtapCriterion vtapCriterion() { + return vtapCriterion; } @Override public Set txDeviceIds() { - return ImmutableSet.copyOf(txDeviceIds); + return txDeviceIds; } @Override public Set rxDeviceIds() { - return ImmutableSet.copyOf(rxDeviceIds); + return rxDeviceIds; + } + + @Override + public int hashCode() { + return Objects.hash(id, type, vtapCriterion, txDeviceIds, rxDeviceIds); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DefaultOpenstackVtap) { + final DefaultOpenstackVtap other = (DefaultOpenstackVtap) obj; + return Objects.equals(this.id, other.id) && + Objects.equals(this.type, other.type) && + Objects.equals(this.vtapCriterion, other.vtapCriterion) && + Objects.equals(this.txDeviceIds(), other.txDeviceIds()) && + Objects.equals(this.rxDeviceIds(), other.rxDeviceIds()) && + Objects.equals(this.annotations(), other.annotations()); + } + return false; } @Override public String toString() { return toStringHelper(this) - .add("id", id) - .add("type", type) - .add("vTapCriterion", vTapCriterion) - .add("txDeviceIds", txDeviceIds) - .add("rxDeviceIds", rxDeviceIds) + .add("id", id()) + .add("type", type()) + .add("vtapCriterion", vtapCriterion()) + .add("txDeviceIds", txDeviceIds()) + .add("rxDeviceIds", rxDeviceIds()) + .add("annotations", annotations()) .toString(); } - @Override - public int hashCode() { - return Objects.hashCode(id, type, vTapCriterion, txDeviceIds, rxDeviceIds); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - DefaultOpenstackVtap that = (DefaultOpenstackVtap) o; - return Objects.equal(this.id, that.id) - && Objects.equal(this.type, that.type) - && Objects.equal(this.vTapCriterion, that.vTapCriterion) - && Objects.equal(this.txDeviceIds, that.txDeviceIds) - && Objects.equal(this.rxDeviceIds, that.rxDeviceIds); - } - /** - * Creates a new default openstack vTap builder. + * Creates OpenstackVtap builder with default parameters. * - * @return default openstack vTap builder + * @return builder */ public static Builder builder() { return new Builder(); } + /** + * Creates OpenstackVtap builder inheriting with default parameters, + * from specified OpenstackVtap. + * + * @param vtap to inherit default from + * @return builder + */ + public static Builder builder(OpenstackVtap vtap) { + return new Builder(vtap); + } + /** * Builder for DefaultOpenstackVtap object. */ public static class Builder implements OpenstackVtap.Builder { - private static final SparseAnnotations EMPTY = DefaultAnnotations.builder().build(); - private OpenstackVtapId id; - private Type type; - private OpenstackVtapCriterion vTapCriterion; + private Type type = Type.VTAP_ALL; + private OpenstackVtapCriterion vtapCriterion; private Set txDeviceIds; private Set rxDeviceIds; - private SparseAnnotations annotations = EMPTY; + private SparseAnnotations annotations = DefaultAnnotations.EMPTY; - // private constructor not intended to use from external + // Private constructor not intended to use from external Builder() { } + Builder(OpenstackVtap description) { + this.id = description.id(); + this.type = description.type(); + this.vtapCriterion = description.vtapCriterion(); + this.type = description.type(); + this.txDeviceIds = description.txDeviceIds(); + this.rxDeviceIds = description.rxDeviceIds(); + this.annotations = (SparseAnnotations) description.annotations(); + } + + /** + * Sets mandatory field id. + * + * @param id to set + * @return self + */ @Override public Builder id(OpenstackVtapId id) { this.id = id; return this; } + /** + * Sets mandatory field type. + * + * @param type of the vtap + * @return self + */ @Override public Builder type(Type type) { this.type = type; return this; } + /** + * Sets mandatory field criterion. + * + * @param vtapCriterion for the vtap + * @return self + */ @Override - public Builder vTapCriterion(OpenstackVtapCriterion vTapCriterion) { - this.vTapCriterion = vTapCriterion; + public Builder vtapCriterion(OpenstackVtapCriterion vtapCriterion) { + this.vtapCriterion = vtapCriterion; return this; } + /** + * Sets a tx deviceId set. + * + * @param txDeviceIds deviceId set for tx + * @return builder + */ @Override public Builder txDeviceIds(Set txDeviceIds) { - if (txDeviceIds != null) { - this.txDeviceIds = ImmutableSet.copyOf(txDeviceIds); - } else { - this.txDeviceIds = Sets.newHashSet(); - } + this.txDeviceIds = txDeviceIds; return this; } + /** + * Sets a rx deviceId set. + * + * @param rxDeviceIds deviceId set for rx + * @return builder + */ @Override public Builder rxDeviceIds(Set rxDeviceIds) { - if (rxDeviceIds != null) { - this.rxDeviceIds = ImmutableSet.copyOf(rxDeviceIds); - } else { - this.rxDeviceIds = Sets.newHashSet(); - } + this.rxDeviceIds = rxDeviceIds; return this; } + /** + * Sets annotations. + * + * @param annotations of the vtap + * @return self + */ @Override - public Builder annotations(SparseAnnotations... annotations) { - checkArgument(annotations.length <= 1, - "Only one set of annotations is expected"); - this.annotations = annotations.length == 1 ? annotations[0] : EMPTY; + public Builder annotations(SparseAnnotations annotations) { + this.annotations = annotations; return this; } + /** + * Builds a DefaultOpenstackVtap instance. + * + * @return DefaultOpenstackVtap + */ @Override public DefaultOpenstackVtap build() { - return new DefaultOpenstackVtap(id, type, vTapCriterion, - txDeviceIds, rxDeviceIds, annotations); + return new DefaultOpenstackVtap(checkNotNull(id), + checkNotNull(type), + checkNotNull(vtapCriterion), + txDeviceIds, + rxDeviceIds, + checkNotNull(annotations)); } } diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapCriterion.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapCriterion.java index d53bf251ee..287f7635e7 100644 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapCriterion.java +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapCriterion.java @@ -24,16 +24,15 @@ import org.onosproject.openstackvtap.api.OpenstackVtapCriterion; import static com.google.common.base.Preconditions.checkArgument; /** - * Default implementation of an immutable openstack vTap criterion. + * Default implementation of an immutable openstack vtap criterion. */ public final class DefaultOpenstackVtapCriterion implements OpenstackVtapCriterion { + private final IpPrefix srcIpPrefix; private final IpPrefix dstIpPrefix; - private final byte ipProtocol; + private final byte ipProtocol; private final TpPort srcTpPort; - private final TpPort dstTpPort; - - private static final String NOT_NULL_MSG = "Element % cannot be null"; + private final TpPort dstTpPort; // private constructor not intended to use from external private DefaultOpenstackVtapCriterion(IpPrefix srcIpPrefix, @@ -107,18 +106,20 @@ public final class DefaultOpenstackVtapCriterion implements OpenstackVtapCriteri } /** - * Creates a new default openstack vTap criterion builder. + * Creates a new default openstack vtap criterion builder. * - * @return default openstack vTap criterion builder + * @return default openstack vtap criterion builder */ public static Builder builder() { return new Builder(); } /** - * A builder class for openstack vTap criterion builder. + * A builder class for openstack vtap criterion builder. */ public static final class Builder implements OpenstackVtapCriterion.Builder { + private static final String NOT_NULL_MSG = "OpenstackVtapCriterion % cannot be null"; + private IpPrefix srcIpPrefix; private IpPrefix dstIpPrefix; private byte ipProtocol; @@ -168,4 +169,5 @@ public final class DefaultOpenstackVtapCriterion implements OpenstackVtapCriteri return this; } } + } diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapNetwork.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapNetwork.java new file mode 100644 index 0000000000..af086a250a --- /dev/null +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapNetwork.java @@ -0,0 +1,203 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.openstackvtap.impl; + +import org.onlab.packet.IpAddress; +import org.onosproject.net.AbstractDescription; +import org.onosproject.net.DefaultAnnotations; +import org.onosproject.net.SparseAnnotations; +import org.onosproject.openstackvtap.api.OpenstackVtapNetwork; + +import java.util.Objects; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default implementation of an immutable OpenstackVtapNetwork. + */ +public class DefaultOpenstackVtapNetwork extends AbstractDescription implements OpenstackVtapNetwork { + + private final Mode mode; + private final Integer networkId; + private final IpAddress serverIp; + + /** + * Creates an DefaultOpenstackVtapNetwork using the supplied information. + * + * @param mode mode of vtap network + * @param networkId network id of the vtap tunneling network + * @param serverIp server IP address used for tunneling + * @param annotations optional key/value annotations + */ + protected DefaultOpenstackVtapNetwork(Mode mode, + Integer networkId, + IpAddress serverIp, + SparseAnnotations... annotations) { + super(annotations); + this.mode = checkNotNull(mode); + this.networkId = networkId; + this.serverIp = checkNotNull(serverIp); + } + + @Override + public Mode mode() { + return mode; + } + + @Override + public Integer networkId() { + return networkId; + } + + @Override + public IpAddress serverIp() { + return serverIp; + } + + @Override + public int hashCode() { + return Objects.hash(mode, networkId, serverIp); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DefaultOpenstackVtapNetwork) { + final DefaultOpenstackVtapNetwork other = (DefaultOpenstackVtapNetwork) obj; + return Objects.equals(this.mode, other.mode) && + Objects.equals(this.networkId, other.networkId) && + Objects.equals(this.serverIp, other.serverIp) && + Objects.equals(this.annotations(), other.annotations()); + } + return false; + } + + @Override + public String toString() { + return toStringHelper(this) + .add("mode", mode()) + .add("networkId", networkId()) + .add("serverIp", serverIp()) + .add("annotations", annotations()) + .toString(); + } + + /** + * Creates OpenstackVtapNetwork builder with default parameters. + * + * @return builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Creates OpenstackVtapNetwork builder inheriting with default parameters, + * from specified OpenstackVtapNetwork. + * + * @param vtapNetwork to inherit default from + * @return builder + */ + public static Builder builder(OpenstackVtapNetwork vtapNetwork) { + return new Builder(vtapNetwork); + } + + /** + * Builder for DefaultOpenstackVtapNetwork object. + */ + public static class Builder implements OpenstackVtapNetwork.Builder { + private Mode mode; + private Integer networkId; + private IpAddress serverIp; + private SparseAnnotations annotations = DefaultAnnotations.EMPTY; + + // private constructor not intended to use from external + private Builder() { + } + + Builder(OpenstackVtapNetwork description) { + this.mode = description.mode(); + this.networkId = description.networkId(); + this.serverIp = description.serverIp(); + this.annotations = (SparseAnnotations) description.annotations(); + } + + /** + * Sets mandatory field mode. + * + * @param mode of vtap network + * @return self + */ + @Override + public Builder mode(Mode mode) { + this.mode = mode; + return this; + } + + /** + * Sets mandatory field networkId. + * + * @param networkId of the vtap tunneling network + * @return self + */ + @Override + public Builder networkId(Integer networkId) { + this.networkId = networkId; + return this; + } + + /** + * Sets mandatory field serverIp. + * + * @param serverIp address used for tunneling + * @return self + */ + @Override + public Builder serverIp(IpAddress serverIp) { + this.serverIp = serverIp; + return this; + } + + /** + * Sets annotations. + * + * @param annotations of the vtap network + * @return self + */ + @Override + public Builder annotations(SparseAnnotations annotations) { + this.annotations = annotations; + return this; + } + + /** + * Builds a DefaultOpenstackVtapNetwork instance. + * + * @return DefaultOpenstackVtapNetwork + */ + @Override + public DefaultOpenstackVtapNetwork build() { + return new DefaultOpenstackVtapNetwork(checkNotNull(mode), + networkId, + checkNotNull(serverIp), + checkNotNull(annotations)); + } + } + +} diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DistributedOpenstackVtapStore.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DistributedOpenstackVtapStore.java index 141af0bc94..9f2ee12f04 100644 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DistributedOpenstackVtapStore.java +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DistributedOpenstackVtapStore.java @@ -29,9 +29,10 @@ import org.onosproject.net.DefaultAnnotations; import org.onosproject.net.DeviceId; import org.onosproject.net.SparseAnnotations; import org.onosproject.openstackvtap.api.OpenstackVtap; -import org.onosproject.openstackvtap.api.OpenstackVtapCriterion; +import org.onosproject.openstackvtap.api.OpenstackVtap.Type; import org.onosproject.openstackvtap.api.OpenstackVtapEvent; import org.onosproject.openstackvtap.api.OpenstackVtapId; +import org.onosproject.openstackvtap.api.OpenstackVtapNetwork; import org.onosproject.openstackvtap.api.OpenstackVtapStore; import org.onosproject.openstackvtap.api.OpenstackVtapStoreDelegate; import org.onosproject.store.AbstractStore; @@ -42,6 +43,7 @@ import org.onosproject.store.service.MapEvent; import org.onosproject.store.service.MapEventListener; import org.onosproject.store.service.Serializer; import org.onosproject.store.service.StorageService; +import org.onosproject.store.service.Versioned; import org.slf4j.Logger; import java.util.Comparator; @@ -53,465 +55,570 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.function.Consumer; import java.util.stream.Collectors; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; import static org.onlab.util.Tools.groupedThreads; import static org.onosproject.net.DefaultAnnotations.merge; +import static org.onosproject.store.service.Versioned.valueOrNull; import static org.slf4j.LoggerFactory.getLogger; /** - * Manages the inventory of users using a {@code ConsistentMap}. + * Manages the inventory of openstack vtap and openstack vtap network using a {@code ConsistentMap}. */ @Component(immediate = true) @Service public class DistributedOpenstackVtapStore extends AbstractStore implements OpenstackVtapStore { + private final Logger log = getLogger(getClass()); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected StorageService storageService; - private ConsistentMap vTapConsistentMap; - private MapEventListener - vTapListener = new VtapEventListener(); - private Map vTapMap; + private ConsistentMap vtapConsistentMap; + private MapEventListener vtapListener = + new VtapEventListener(); + private Map vtapMap; + + private ConsistentMap vtapNetworkConsistentMap; + private MapEventListener vtapNetworkListener = + new VtapNetworkEventListener(); + private Map vtapNetworkMap; + + private ConsistentMap> vtapNetworkDevicesConsistentMap; private static final Serializer SERIALIZER = Serializer .using(new KryoNamespace.Builder().register(KryoNamespaces.API) .register(OpenstackVtapId.class) .register(UUID.class) .register(DefaultOpenstackVtap.class) - .register(OpenstackVtap.Type.class) + .register(Type.class) .register(DefaultOpenstackVtapCriterion.class) + .register(DefaultOpenstackVtapNetwork.class) + .register(OpenstackVtapNetwork.Mode.class) .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID) .build()); private Map> - vTapIdsByTxDeviceId = Maps.newConcurrentMap(); + vtapIdsByTxDeviceId = Maps.newConcurrentMap(); private Map> - vTapIdsByRxDeviceId = Maps.newConcurrentMap(); + vtapIdsByRxDeviceId = Maps.newConcurrentMap(); private ScheduledExecutorService eventExecutor; + private Consumer vtapStatusListener; - private Consumer vTapStatusListener; - - public static final String INVALID_DESCRIPTION = "Invalid create/update parameter"; + private static final String ERR_NOT_FOUND = "ID {} does not exist"; + private static final String ERR_DUPLICATE = "ID {} already exists"; @Activate public void activate() { eventExecutor = newSingleThreadScheduledExecutor( groupedThreads(this.getClass().getSimpleName(), "event-handler", log)); - vTapConsistentMap = storageService. + // vtap network data + vtapNetworkConsistentMap = storageService. consistentMapBuilder() - .withName("vTapMap") + .withName("vtapNetworkMap") + .withSerializer(SERIALIZER) + .build(); + vtapNetworkMap = vtapNetworkConsistentMap.asJavaMap(); + vtapNetworkConsistentMap.addListener(vtapNetworkListener); + + // vtap network devices data + vtapNetworkDevicesConsistentMap = storageService.> + consistentMapBuilder() + .withName("vtapNetworkDevicesMap") .withSerializer(SERIALIZER) .build(); - vTapMap = vTapConsistentMap.asJavaMap(); - vTapConsistentMap.addListener(vTapListener); + // vtap data + vtapConsistentMap = storageService. + consistentMapBuilder() + .withName("vtapMap") + .withSerializer(SERIALIZER) + .build(); + vtapMap = vtapConsistentMap.asJavaMap(); + vtapConsistentMap.addListener(vtapListener); - vTapStatusListener = status -> { + // initialize vtap data + vtapStatusListener = status -> { if (status == Status.ACTIVE) { eventExecutor.execute(this::loadVtapIds); } }; - vTapConsistentMap.addStatusChangeListener(vTapStatusListener); + vtapConsistentMap.addStatusChangeListener(vtapStatusListener); - log.info("Started {} - {}", this.getClass().getSimpleName()); + log.info("Started"); } @Deactivate public void deactivate() { - vTapConsistentMap.removeStatusChangeListener(vTapStatusListener); - vTapConsistentMap.removeListener(vTapListener); + vtapConsistentMap.removeStatusChangeListener(vtapStatusListener); + vtapConsistentMap.removeListener(vtapListener); + vtapNetworkConsistentMap.removeListener(vtapNetworkListener); + eventExecutor.shutdown(); - log.info("Stopped {} - {}", this.getClass().getSimpleName()); + log.info("Stopped"); } - @Override - public OpenstackVtap createOrUpdateVtap(OpenstackVtapId vTapId, - OpenstackVtap description, - boolean replaceFlag) { - - return vTapMap.compute(vTapId, (id, existing) -> { - if (existing == null && - (description.type() == null || - description.vTapCriterion() == null || - description.txDeviceIds() == null || - description.rxDeviceIds() == null)) { - checkState(false, INVALID_DESCRIPTION); - return null; - } - - if (shouldUpdate(existing, description, replaceFlag)) { - // Replace items - OpenstackVtap.Type type = - (description.type() == null ? existing.type() : description.type()); - OpenstackVtapCriterion vTapCriterion = - (description.vTapCriterion() == null ? - existing.vTapCriterion() : description.vTapCriterion()); - - // Replace or add devices - Set txDeviceIds; - if (description.txDeviceIds() == null) { - txDeviceIds = existing.txDeviceIds(); - } else { - if (existing == null || replaceFlag) { - txDeviceIds = ImmutableSet.copyOf(description.txDeviceIds()); - } else { - txDeviceIds = Sets.newHashSet(existing.txDeviceIds()); - txDeviceIds.addAll(description.txDeviceIds()); - } - } - - Set rxDeviceIds; - if (description.rxDeviceIds() == null) { - rxDeviceIds = existing.rxDeviceIds(); - } else { - if (existing == null || replaceFlag) { - rxDeviceIds = ImmutableSet.copyOf(description.rxDeviceIds()); - } else { - rxDeviceIds = Sets.newHashSet(existing.rxDeviceIds()); - rxDeviceIds.addAll(description.rxDeviceIds()); - } - } - - // Replace or add annotations - SparseAnnotations annotations; - if (existing != null) { - annotations = merge((DefaultAnnotations) existing.annotations(), - (SparseAnnotations) description.annotations()); - } else { - annotations = (SparseAnnotations) description.annotations(); - } - - // Make new changed vTap and return - return DefaultOpenstackVtap.builder() - .id(vTapId) - .type(type) - .vTapCriterion(vTapCriterion) - .txDeviceIds(txDeviceIds) - .rxDeviceIds(rxDeviceIds) - .annotations(annotations) - .build(); - } - return existing; - }); - } - - @Override - public OpenstackVtap removeVtapById(OpenstackVtapId vTapId) { - return vTapMap.remove(vTapId); - } - - @Override - public boolean addDeviceToVtap(OpenstackVtapId vTapId, - OpenstackVtap.Type type, - DeviceId deviceId) { - checkNotNull(vTapId); - checkNotNull(deviceId); - - OpenstackVtap vTap = vTapMap.compute(vTapId, (id, existing) -> { - if (existing == null) { - return null; - } - if (!existing.type().isValid(type)) { - log.error("Not valid OpenstackVtap type {} for requested type {}", - existing.type(), type); - return existing; - } - - Set txDeviceIds = null; - if (type.isValid(OpenstackVtap.Type.VTAP_TX) && - !existing.txDeviceIds().contains(deviceId)) { - txDeviceIds = Sets.newHashSet(existing.txDeviceIds()); - txDeviceIds.add(deviceId); - } - - Set rxDeviceIds = null; - if (type.isValid(OpenstackVtap.Type.VTAP_RX) && - !existing.rxDeviceIds().contains(deviceId)) { - rxDeviceIds = Sets.newHashSet(existing.rxDeviceIds()); - rxDeviceIds.add(deviceId); - } - - if (txDeviceIds != null || rxDeviceIds != null) { - //updateVTapIdFromDeviceId(existing.id(), deviceId); // execute from event listener - - return DefaultOpenstackVtap.builder() - .id(vTapId) - .type(existing.type()) - .vTapCriterion(existing.vTapCriterion()) - .txDeviceIds(txDeviceIds != null ? txDeviceIds : existing.txDeviceIds()) - .rxDeviceIds(rxDeviceIds != null ? rxDeviceIds : existing.rxDeviceIds()) - .annotations(existing.annotations()) - .build(); - } - return existing; - }); - return (vTap != null); - } - - @Override - public boolean removeDeviceFromVtap(OpenstackVtapId vTapId, - OpenstackVtap.Type type, - DeviceId deviceId) { - checkNotNull(vTapId); - checkNotNull(deviceId); - - OpenstackVtap vTap = vTapMap.compute(vTapId, (id, existing) -> { - if (existing == null) { - return null; - } - if (!existing.type().isValid(type)) { - log.error("Not valid OpenstackVtap type {} for requested type {}", - existing.type(), type); - return existing; - } - - Set txDeviceIds = null; - if (type.isValid(OpenstackVtap.Type.VTAP_TX) && - existing.txDeviceIds().contains(deviceId)) { - txDeviceIds = Sets.newHashSet(existing.txDeviceIds()); - txDeviceIds.remove(deviceId); - } - - Set rxDeviceIds = null; - if (type.isValid(OpenstackVtap.Type.VTAP_RX) && - existing.rxDeviceIds().contains(deviceId)) { - rxDeviceIds = Sets.newHashSet(existing.rxDeviceIds()); - rxDeviceIds.remove(deviceId); - } - - if (txDeviceIds != null || rxDeviceIds != null) { - //removeVTapIdFromDeviceId(existing.id(), deviceId); // execute from event listener - - return DefaultOpenstackVtap.builder() - .id(vTapId) - .type(existing.type()) - .vTapCriterion(existing.vTapCriterion()) - .txDeviceIds(txDeviceIds != null ? txDeviceIds : existing.txDeviceIds()) - .rxDeviceIds(rxDeviceIds != null ? rxDeviceIds : existing.rxDeviceIds()) - .annotations(existing.annotations()) - .build(); - } - return existing; - }); - return (vTap != null); - } - - @Override - public boolean updateDeviceForVtap(OpenstackVtapId vTapId, - Set txDeviceIds, Set rxDeviceIds, - boolean replaceDevices) { - checkNotNull(vTapId); - checkNotNull(txDeviceIds); - checkNotNull(rxDeviceIds); - - OpenstackVtap vTap = vTapMap.compute(vTapId, (id, existing) -> { - if (existing == null) { - return null; - } - - // Replace or add devices - Set txDS = null; - if (replaceDevices) { - if (!existing.txDeviceIds().equals(txDeviceIds)) { - txDS = ImmutableSet.copyOf(txDeviceIds); - } - } else { - if (!existing.txDeviceIds().containsAll(txDeviceIds)) { - txDS = Sets.newHashSet(existing.txDeviceIds()); - txDS.addAll(txDeviceIds); - } - } - - Set rxDS = null; - if (replaceDevices) { - if (!existing.rxDeviceIds().equals(rxDeviceIds)) { - rxDS = ImmutableSet.copyOf(rxDeviceIds); - } - } else { - if (!existing.rxDeviceIds().containsAll(rxDeviceIds)) { - rxDS = Sets.newHashSet(existing.rxDeviceIds()); - rxDS.addAll(rxDeviceIds); - } - } - - if (txDS != null || rxDS != null) { - - return DefaultOpenstackVtap.builder() - .id(vTapId) - .type(existing.type()) - .vTapCriterion(existing.vTapCriterion()) - .txDeviceIds(txDS != null ? txDS : existing.txDeviceIds()) - .rxDeviceIds(rxDS != null ? rxDS : existing.rxDeviceIds()) - .annotations(existing.annotations()) - .build(); - } - return existing; - }); - return (vTap != null); - } - - @Override - public int getVtapCount(OpenstackVtap.Type type) { - return (int) vTapMap.values().parallelStream() - .filter(vTap -> vTap.type().isValid(type)) - .count(); - } - - @Override - public Set getVtaps(OpenstackVtap.Type type) { - return ImmutableSet.copyOf( - vTapMap.values().parallelStream() - .filter(vTap -> vTap.type().isValid(type)) - .collect(Collectors.toSet())); - } - - @Override - public OpenstackVtap getVtap(OpenstackVtapId vTapId) { - return vTapMap.get(vTapId); - } - - @Override - public Set getVtapsByDeviceId(OpenstackVtap.Type type, - DeviceId deviceId) { - Set vtapIds = Sets.newHashSet(); - if (type.isValid(OpenstackVtap.Type.VTAP_TX)) { - if (vTapIdsByTxDeviceId.get(deviceId) != null) { - vtapIds.addAll(vTapIdsByTxDeviceId.get(deviceId)); - } - } - if (type.isValid(OpenstackVtap.Type.VTAP_RX)) { - if (vTapIdsByRxDeviceId.get(deviceId) != null) { - vtapIds.addAll(vTapIdsByRxDeviceId.get(deviceId)); - } - } - - return ImmutableSet.copyOf( - vtapIds.parallelStream() - .map(vTapId -> vTapMap.get(vTapId)) - .filter(Objects::nonNull) - .collect(Collectors.toSet())); - } - - private void loadVtapIds() { - vTapIdsByTxDeviceId.clear(); - vTapIdsByRxDeviceId.clear(); - vTapMap.values().forEach(vTap -> refreshDeviceIdsByVtap(null, vTap)); - } - - private boolean shouldUpdate(DefaultOpenstackVtap existing, - OpenstackVtap description, - boolean replaceDevices) { + private boolean shouldUpdateVtapNetwork(DefaultOpenstackVtapNetwork existing, + OpenstackVtapNetwork description) { if (existing == null) { return true; } - if ((description.type() != null && !description.type().equals(existing.type())) - || (description.vTapCriterion() != null && - !description.vTapCriterion().equals(existing.vTapCriterion()))) { + if (!Objects.equals(existing.mode(), description.mode()) || + !Objects.equals(existing.networkId(), description.networkId()) || + !Objects.equals(existing.serverIp(), description.serverIp())) { return true; } - if (description.txDeviceIds() != null) { - if (replaceDevices) { - if (!existing.txDeviceIds().equals(description.txDeviceIds())) { - return true; - } - } else { - if (!existing.txDeviceIds().containsAll(description.txDeviceIds())) { - return true; - } - } - } - - if (description.rxDeviceIds() != null) { - if (replaceDevices) { - if (!existing.rxDeviceIds().equals(description.rxDeviceIds())) { - return true; - } - } else { - if (!existing.rxDeviceIds().containsAll(description.rxDeviceIds())) { - return true; - } - } - } - - // check to see if any of the annotations provided by vTap - // differ from those in the existing vTap + // check to see if any of the annotations provided by description + // differ from those in the existing vtap network return description.annotations().keys().stream() .anyMatch(k -> !Objects.equals(description.annotations().value(k), existing.annotations().value(k))); } + private OpenstackVtapNetwork createOrUpdateVtapNetwork(boolean update, + Integer key, + OpenstackVtapNetwork description) { + DefaultOpenstackVtapNetwork result = + vtapNetworkMap.compute(key, (id, existing) -> { + // Check create or update validity + if (update && existing == null) { + return null; + } else if (!update && existing != null) { + return existing; + } + + if (shouldUpdateVtapNetwork(existing, description)) { + // Replace or add annotations + final SparseAnnotations annotations; + if (existing != null) { + annotations = merge((DefaultAnnotations) existing.annotations(), + (SparseAnnotations) description.annotations()); + } else { + annotations = (SparseAnnotations) description.annotations(); + } + + return DefaultOpenstackVtapNetwork.builder(description) + .annotations(annotations) + .build(); + } + return existing; + }); + return result; + } + + @Override + public OpenstackVtapNetwork createVtapNetwork(Integer key, OpenstackVtapNetwork description) { + if (getVtapNetwork(key) == null) { + OpenstackVtapNetwork vtapNetwork = createOrUpdateVtapNetwork(false, key, description); + if (Objects.equals(vtapNetwork, description)) { + return vtapNetwork; + } + } + log.error(ERR_DUPLICATE, key); + return null; + } + + @Override + public OpenstackVtapNetwork updateVtapNetwork(Integer key, OpenstackVtapNetwork description) { + OpenstackVtapNetwork vtapNetwork = createOrUpdateVtapNetwork(true, key, description); + if (vtapNetwork == null) { + log.error(ERR_NOT_FOUND, key); + } + return vtapNetwork; + } + + @Override + public OpenstackVtapNetwork removeVtapNetwork(Integer key) { + return vtapNetworkMap.remove(key); + } + + @Override + public void clearVtapNetworks() { + vtapNetworkMap.clear(); + } + + @Override + public int getVtapNetworkCount() { + return vtapNetworkMap.size(); + } + + @Override + public OpenstackVtapNetwork getVtapNetwork(Integer key) { + return vtapNetworkMap.get(key); + } + + @Override + public boolean addDeviceToVtapNetwork(Integer key, DeviceId deviceId) { + Versioned> result = + vtapNetworkDevicesConsistentMap.compute(key, (id, existing) -> { + // Add deviceId to deviceIds + if (existing == null) { + return Sets.newHashSet(deviceId); + } else if (!existing.contains(deviceId)) { + Set deviceIds = Sets.newHashSet(existing); + deviceIds.add(deviceId); + return deviceIds; + } else { + return existing; + } + }); + return Objects.nonNull(valueOrNull(result)); + } + + @Override + public boolean removeDeviceFromVtapNetwork(Integer key, DeviceId deviceId) { + Versioned> result = + vtapNetworkDevicesConsistentMap.compute(key, (id, existing) -> { + // Remove deviceId from deviceIds + if (existing != null && existing.contains(deviceId)) { + Set deviceIds = Sets.newHashSet(existing); + deviceIds.remove(deviceId); + return deviceIds; + } else { + return existing; + } + }); + return Objects.nonNull(valueOrNull(result)); + } + + @Override + public Set getVtapNetworkDevices(Integer key) { + return valueOrNull(vtapNetworkDevicesConsistentMap.get(key)); + } + + private boolean shouldUpdateVtap(DefaultOpenstackVtap existing, + OpenstackVtap description, + boolean replaceDevices) { + if (existing == null) { + return true; + } + + if (!Objects.equals(existing.type(), description.type()) || + !Objects.equals(existing.vtapCriterion(), description.vtapCriterion())) { + return true; + } + + if (replaceDevices) { + if (!Objects.equals(description.txDeviceIds(), existing.txDeviceIds()) || + !Objects.equals(description.rxDeviceIds(), existing.rxDeviceIds())) { + return true; + } + } else { + if (!existing.txDeviceIds().containsAll(description.txDeviceIds()) || + !existing.rxDeviceIds().containsAll(description.rxDeviceIds())) { + return true; + } + } + + // check to see if any of the annotations provided by description + // differ from those in the existing vtap + return description.annotations().keys().stream() + .anyMatch(k -> !Objects.equals(description.annotations().value(k), + existing.annotations().value(k))); + } + + private OpenstackVtap createOrUpdateVtap(boolean update, + OpenstackVtap description, + boolean replaceDevices) { + DefaultOpenstackVtap result = + vtapMap.compute(description.id(), (id, existing) -> { + // Check create or update validity + if (update && existing == null) { + return null; + } else if (!update && existing != null) { + return existing; + } + + if (shouldUpdateVtap(existing, description, replaceDevices)) { + // Replace or add devices + final Set txDeviceIds; + if (existing == null || replaceDevices) { + txDeviceIds = description.txDeviceIds(); + } else { + txDeviceIds = Sets.newHashSet(existing.txDeviceIds()); + txDeviceIds.addAll(description.txDeviceIds()); + } + + final Set rxDeviceIds; + if (existing == null || replaceDevices) { + rxDeviceIds = description.rxDeviceIds(); + } else { + rxDeviceIds = Sets.newHashSet(existing.rxDeviceIds()); + rxDeviceIds.addAll(description.rxDeviceIds()); + } + + // Replace or add annotations + final SparseAnnotations annotations; + if (existing != null) { + annotations = merge((DefaultAnnotations) existing.annotations(), + (SparseAnnotations) description.annotations()); + } else { + annotations = (SparseAnnotations) description.annotations(); + } + + return DefaultOpenstackVtap.builder(description) + .txDeviceIds(txDeviceIds) + .rxDeviceIds(rxDeviceIds) + .annotations(annotations) + .build(); + } + return existing; + }); + return result; + } + + @Override + public OpenstackVtap createVtap(OpenstackVtap description) { + if (getVtap(description.id()) == null) { + OpenstackVtap vtap = createOrUpdateVtap(false, description, true); + if (Objects.equals(vtap, description)) { + return vtap; + } + } + log.error(ERR_DUPLICATE, description.id()); + return null; + } + + @Override + public OpenstackVtap updateVtap(OpenstackVtap description, boolean replaceDevices) { + OpenstackVtap vtap = createOrUpdateVtap(true, description, replaceDevices); + if (vtap == null) { + log.error(ERR_NOT_FOUND, description.id()); + } + return vtap; + } + + @Override + public OpenstackVtap removeVtap(OpenstackVtapId vtapId) { + return vtapMap.remove(vtapId); + } + + @Override + public void clearVtaps() { + vtapMap.clear(); + } + + @Override + public int getVtapCount(Type type) { + return (int) vtapMap.values().parallelStream() + .filter(vtap -> vtap.type().isValid(type)) + .count(); + } + + @Override + public Set getVtaps(Type type) { + return ImmutableSet.copyOf( + vtapMap.values().parallelStream() + .filter(vtap -> vtap.type().isValid(type)) + .collect(Collectors.toSet())); + } + + @Override + public OpenstackVtap getVtap(OpenstackVtapId vtapId) { + return vtapMap.get(vtapId); + } + + @Override + public boolean addDeviceToVtap(OpenstackVtapId vtapId, Type type, DeviceId deviceId) { + OpenstackVtap result = + vtapMap.compute(vtapId, (id, existing) -> { + if (existing == null) { + return null; + } + + // Check type validate + if (!existing.type().isValid(type)) { + log.error("Not valid OpenstackVtap type {} for requested type {}", existing.type(), type); + return existing; + } + + // Add deviceId to txDeviceIds + final Set txDeviceIds; + if (existing.type().isValid(Type.VTAP_TX) && + (type.isValid(Type.VTAP_TX) || type == Type.VTAP_ANY) && + !existing.txDeviceIds().contains(deviceId)) { + txDeviceIds = Sets.newHashSet(existing.txDeviceIds()); + txDeviceIds.add(deviceId); + } else { + txDeviceIds = null; + } + + // Add deviceId to rxDeviceIds + final Set rxDeviceIds; + if (existing.type().isValid(Type.VTAP_RX) && + (type.isValid(Type.VTAP_RX) || type == Type.VTAP_ANY) && + !existing.rxDeviceIds().contains(deviceId)) { + rxDeviceIds = Sets.newHashSet(existing.rxDeviceIds()); + rxDeviceIds.add(deviceId); + } else { + rxDeviceIds = null; + } + + if (txDeviceIds != null || rxDeviceIds != null) { + return DefaultOpenstackVtap.builder() + .id(id) + .type(existing.type()) + .vtapCriterion(existing.vtapCriterion()) + .txDeviceIds(txDeviceIds != null ? txDeviceIds : existing.txDeviceIds()) + .rxDeviceIds(rxDeviceIds != null ? rxDeviceIds : existing.rxDeviceIds()) + .annotations(existing.annotations()) + .build(); + } else { + return existing; + } + }); + return Objects.nonNull(result); + } + + @Override + public boolean removeDeviceFromVtap(OpenstackVtapId vtapId, OpenstackVtap.Type type, DeviceId deviceId) { + OpenstackVtap result = + vtapMap.compute(vtapId, (id, existing) -> { + if (existing == null) { + return null; + } + + // Check type validate + if (!existing.type().isValid(type)) { + log.error("Not valid OpenstackVtap type {} for requested type {}", + existing.type(), type); + return existing; + } + + // Remove deviceId from txDeviceIds + final Set txDeviceIds; + if (existing.type().isValid(Type.VTAP_TX) && + (type.isValid(Type.VTAP_TX) || type == Type.VTAP_ANY) && + existing.txDeviceIds().contains(deviceId)) { + txDeviceIds = Sets.newHashSet(existing.txDeviceIds()); + txDeviceIds.remove(deviceId); + } else { + txDeviceIds = null; + } + + // Remove deviceId from rxDeviceIds + final Set rxDeviceIds; + if (existing.type().isValid(Type.VTAP_RX) && + (type.isValid(Type.VTAP_RX) || type == Type.VTAP_ANY) && + existing.rxDeviceIds().contains(deviceId)) { + rxDeviceIds = Sets.newHashSet(existing.rxDeviceIds()); + rxDeviceIds.remove(deviceId); + } else { + rxDeviceIds = null; + } + + if (txDeviceIds != null || rxDeviceIds != null) { + return DefaultOpenstackVtap.builder() + .id(id) + .type(existing.type()) + .vtapCriterion(existing.vtapCriterion()) + .txDeviceIds(txDeviceIds != null ? txDeviceIds : existing.txDeviceIds()) + .rxDeviceIds(rxDeviceIds != null ? rxDeviceIds : existing.rxDeviceIds()) + .annotations(existing.annotations()) + .build(); + } else { + return existing; + } + }); + return Objects.nonNull(result); + } + + @Override + public Set getVtapsByDeviceId(DeviceId deviceId) { + Set vtapIds = Sets.newHashSet(); + Set txIds = vtapIdsByTxDeviceId.get(deviceId); + if (txIds != null) { + vtapIds.addAll(txIds); + } + Set rxIds = vtapIdsByRxDeviceId.get(deviceId); + if (rxIds != null) { + vtapIds.addAll(rxIds); + } + return ImmutableSet.copyOf( + vtapIds.parallelStream() + .map(vtapId -> vtapMap.get(vtapId)) + .filter(Objects::nonNull) + .collect(Collectors.toSet())); + } + private class VtapComparator implements Comparator { @Override public int compare(OpenstackVtap v1, OpenstackVtap v2) { int diff = (v2.type().compareTo(v1.type())); if (diff == 0) { - return (v2.vTapCriterion().ipProtocol() - v1.vTapCriterion().ipProtocol()); + return (v2.vtapCriterion().ipProtocol() - v1.vtapCriterion().ipProtocol()); } return diff; } } - private static Set addVTapIds(OpenstackVtapId vTapId) { + private void loadVtapIds() { + vtapIdsByTxDeviceId.clear(); + vtapIdsByRxDeviceId.clear(); + vtapMap.values().forEach(vtap -> refreshDeviceIdsByVtap(null, vtap)); + } + + private static Set addVTapIds(OpenstackVtapId vtapId) { Set vtapIds = Sets.newConcurrentHashSet(); - vtapIds.add(vTapId); + vtapIds.add(vtapId); return vtapIds; } private static Set updateVTapIds(Set existingVtapIds, - OpenstackVtapId vTapId) { - existingVtapIds.add(vTapId); + OpenstackVtapId vtapId) { + existingVtapIds.add(vtapId); return existingVtapIds; } private static Set removeVTapIds(Set existingVtapIds, - OpenstackVtapId vTapId) { - existingVtapIds.remove(vTapId); + OpenstackVtapId vtapId) { + existingVtapIds.remove(vtapId); if (existingVtapIds.isEmpty()) { return null; } return existingVtapIds; } - private void updateVTapIdFromTxDeviceId(OpenstackVtapId vTapId, DeviceId deviceId) { - vTapIdsByTxDeviceId.compute(deviceId, (k, v) -> v == null ? - addVTapIds(vTapId) : updateVTapIds(v, vTapId)); + private void updateVTapIdFromTxDeviceId(OpenstackVtapId vtapId, DeviceId deviceId) { + vtapIdsByTxDeviceId.compute(deviceId, (k, v) -> v == null ? + addVTapIds(vtapId) : updateVTapIds(v, vtapId)); } - private void removeVTapIdFromTxDeviceId(OpenstackVtapId vTapId, DeviceId deviceId) { - vTapIdsByTxDeviceId.computeIfPresent(deviceId, (k, v) -> removeVTapIds(v, vTapId)); + private void removeVTapIdFromTxDeviceId(OpenstackVtapId vtapId, DeviceId deviceId) { + vtapIdsByTxDeviceId.computeIfPresent(deviceId, (k, v) -> removeVTapIds(v, vtapId)); } - private void updateVTapIdFromRxDeviceId(OpenstackVtapId vTapId, DeviceId deviceId) { - vTapIdsByRxDeviceId.compute(deviceId, (k, v) -> v == null ? - addVTapIds(vTapId) : updateVTapIds(v, vTapId)); + private void updateVTapIdFromRxDeviceId(OpenstackVtapId vtapId, DeviceId deviceId) { + vtapIdsByRxDeviceId.compute(deviceId, (k, v) -> v == null ? + addVTapIds(vtapId) : updateVTapIds(v, vtapId)); } - private void removeVTapIdFromRxDeviceId(OpenstackVtapId vTapId, DeviceId deviceId) { - vTapIdsByRxDeviceId.computeIfPresent(deviceId, (k, v) -> removeVTapIds(v, vTapId)); + private void removeVTapIdFromRxDeviceId(OpenstackVtapId vtapId, DeviceId deviceId) { + vtapIdsByRxDeviceId.computeIfPresent(deviceId, (k, v) -> removeVTapIds(v, vtapId)); } - private void refreshDeviceIdsByVtap(OpenstackVtap oldOpenstackVtap, - OpenstackVtap newOpenstackVtap) { + private void refreshDeviceIdsByVtap(OpenstackVtap newOpenstackVtap, + OpenstackVtap oldOpenstackVtap) { + if (Objects.equals(newOpenstackVtap, oldOpenstackVtap)) { + return; + } + if (oldOpenstackVtap != null) { Set removeDeviceIds; - // Remove TX vTap + // Remove TX vtap removeDeviceIds = (newOpenstackVtap != null) ? Sets.difference(oldOpenstackVtap.txDeviceIds(), newOpenstackVtap.txDeviceIds()) : oldOpenstackVtap.txDeviceIds(); removeDeviceIds.forEach(id -> removeVTapIdFromTxDeviceId(oldOpenstackVtap.id(), id)); - // Remove RX vTap + // Remove RX vtap removeDeviceIds = (newOpenstackVtap != null) ? Sets.difference(oldOpenstackVtap.rxDeviceIds(), newOpenstackVtap.rxDeviceIds()) : oldOpenstackVtap.rxDeviceIds(); @@ -521,13 +628,13 @@ public class DistributedOpenstackVtapStore if (newOpenstackVtap != null) { Set addDeviceIds; - // Add TX vTap + // Add TX vtap addDeviceIds = (oldOpenstackVtap != null) ? Sets.difference(newOpenstackVtap.txDeviceIds(), oldOpenstackVtap.txDeviceIds()) : newOpenstackVtap.txDeviceIds(); addDeviceIds.forEach(id -> updateVTapIdFromTxDeviceId(newOpenstackVtap.id(), id)); - // Add RX vTap + // Add RX vtap addDeviceIds = (oldOpenstackVtap != null) ? Sets.difference(newOpenstackVtap.rxDeviceIds(), oldOpenstackVtap.rxDeviceIds()) : newOpenstackVtap.rxDeviceIds(); @@ -544,26 +651,24 @@ public class DistributedOpenstackVtapStore DefaultOpenstackVtap oldValue = event.oldValue() != null ? event.oldValue().value() : null; - log.debug("VtapEventListener {} -> {}, {}", event.type(), oldValue, newValue); + log.trace("VtapEventListener {}: {} -> {}", event.type(), oldValue, newValue); switch (event.type()) { case INSERT: - refreshDeviceIdsByVtap(oldValue, newValue); + refreshDeviceIdsByVtap(newValue, oldValue); notifyDelegate(new OpenstackVtapEvent( - OpenstackVtapEvent.Type.VTAP_ADDED, newValue)); + OpenstackVtapEvent.Type.VTAP_ADDED, newValue, null)); break; case UPDATE: - if (!Objects.equals(newValue, oldValue)) { - refreshDeviceIdsByVtap(oldValue, newValue); - notifyDelegate(new OpenstackVtapEvent( - OpenstackVtapEvent.Type.VTAP_UPDATED, newValue, oldValue)); - } + refreshDeviceIdsByVtap(newValue, oldValue); + notifyDelegate(new OpenstackVtapEvent( + OpenstackVtapEvent.Type.VTAP_UPDATED, newValue, oldValue)); break; case REMOVE: - refreshDeviceIdsByVtap(oldValue, newValue); + refreshDeviceIdsByVtap(newValue, oldValue); notifyDelegate(new OpenstackVtapEvent( - OpenstackVtapEvent.Type.VTAP_REMOVED, oldValue)); + OpenstackVtapEvent.Type.VTAP_REMOVED, null, oldValue)); break; default: @@ -571,4 +676,37 @@ public class DistributedOpenstackVtapStore } } } + + private class VtapNetworkEventListener + implements MapEventListener { + @Override + public void event(MapEvent event) { + DefaultOpenstackVtapNetwork newValue = + event.newValue() != null ? event.newValue().value() : null; + DefaultOpenstackVtapNetwork oldValue = + event.oldValue() != null ? event.oldValue().value() : null; + + log.trace("VtapNetworkEventListener {}: {} -> {}", event.type(), oldValue, newValue); + switch (event.type()) { + case INSERT: + notifyDelegate(new OpenstackVtapEvent( + OpenstackVtapEvent.Type.VTAP_NETWORK_ADDED, newValue, null)); + break; + + case UPDATE: + notifyDelegate(new OpenstackVtapEvent( + OpenstackVtapEvent.Type.VTAP_NETWORK_UPDATED, newValue, oldValue)); + break; + + case REMOVE: + notifyDelegate(new OpenstackVtapEvent( + OpenstackVtapEvent.Type.VTAP_NETWORK_REMOVED, null, oldValue)); + break; + + default: + log.warn("Unknown map event type: {}", event.type()); + } + } + } + } diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/OpenstackVtapManager.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/OpenstackVtapManager.java index 02a6579c3b..6370de6acc 100644 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/OpenstackVtapManager.java +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/OpenstackVtapManager.java @@ -22,12 +22,15 @@ 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.Modified; +import org.apache.felix.scr.annotations.Property; 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.onlab.packet.IpPrefix; -import org.onlab.packet.VlanId; +import org.onlab.util.Tools; +import org.onosproject.cfg.ComponentConfigService; import org.onosproject.cluster.ClusterService; import org.onosproject.cluster.LeadershipService; import org.onosproject.cluster.NodeId; @@ -35,21 +38,21 @@ import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; import org.onosproject.core.GroupId; import org.onosproject.event.AbstractListenerManager; -import org.onosproject.mastership.MastershipService; import org.onosproject.net.Device; import org.onosproject.net.DeviceId; import org.onosproject.net.Host; import org.onosproject.net.HostLocation; +import org.onosproject.net.Port; import org.onosproject.net.PortNumber; +import org.onosproject.net.behaviour.DefaultTunnelDescription; import org.onosproject.net.behaviour.ExtensionTreatmentResolver; +import org.onosproject.net.behaviour.InterfaceConfig; +import org.onosproject.net.behaviour.TunnelDescription; +import org.onosproject.net.behaviour.TunnelEndPoints; +import org.onosproject.net.behaviour.TunnelKey; import org.onosproject.net.device.DeviceEvent; import org.onosproject.net.device.DeviceListener; import org.onosproject.net.device.DeviceService; -import org.onosproject.net.driver.DefaultDriverData; -import org.onosproject.net.driver.DefaultDriverHandler; -import org.onosproject.net.driver.Driver; -import org.onosproject.net.driver.DriverHandler; -import org.onosproject.net.driver.DriverService; import org.onosproject.net.flow.DefaultFlowRule; import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficTreatment; @@ -59,6 +62,7 @@ import org.onosproject.net.flow.FlowRuleOperationsContext; import org.onosproject.net.flow.FlowRuleService; import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.instructions.ExtensionPropertyException; import org.onosproject.net.flow.instructions.ExtensionTreatment; import org.onosproject.net.group.DefaultGroupBucket; import org.onosproject.net.group.DefaultGroupDescription; @@ -69,6 +73,7 @@ import org.onosproject.net.group.GroupService; import org.onosproject.net.host.HostEvent; import org.onosproject.net.host.HostListener; import org.onosproject.net.host.HostService; +import org.onosproject.openstacknode.api.OpenstackNode; import org.onosproject.openstacknode.api.OpenstackNodeEvent; import org.onosproject.openstacknode.api.OpenstackNodeListener; import org.onosproject.openstacknode.api.OpenstackNodeService; @@ -79,17 +84,19 @@ import org.onosproject.openstackvtap.api.OpenstackVtapCriterion; import org.onosproject.openstackvtap.api.OpenstackVtapEvent; import org.onosproject.openstackvtap.api.OpenstackVtapId; import org.onosproject.openstackvtap.api.OpenstackVtapListener; +import org.onosproject.openstackvtap.api.OpenstackVtapNetwork; +import org.onosproject.openstackvtap.api.OpenstackVtapNetwork.Mode; import org.onosproject.openstackvtap.api.OpenstackVtapService; import org.onosproject.openstackvtap.api.OpenstackVtapStore; import org.onosproject.openstackvtap.api.OpenstackVtapStoreDelegate; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; +import java.util.Dictionary; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.concurrent.ScheduledExecutorService; -import java.util.function.BiFunction; import java.util.stream.Collectors; import java.util.stream.StreamSupport; @@ -99,9 +106,10 @@ import static org.onlab.packet.Ethernet.TYPE_IPV4; import static org.onlab.packet.IPv4.PROTOCOL_ICMP; import static org.onlab.packet.IPv4.PROTOCOL_TCP; import static org.onlab.packet.IPv4.PROTOCOL_UDP; -import static org.onlab.packet.VlanId.UNTAGGED; import static org.onlab.util.Tools.groupedThreads; +import static org.onosproject.net.AnnotationKeys.PORT_NAME; import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_RESUBMIT_TABLE; +import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST; import static org.onosproject.openstacknetworking.api.Constants.DHCP_ARP_TABLE; import static org.onosproject.openstacknetworking.api.Constants.FLAT_TABLE; import static org.onosproject.openstacknetworking.api.Constants.FORWARDING_TABLE; @@ -114,12 +122,20 @@ import static org.onosproject.openstacknetworking.api.Constants.VTAP_INBOUND_TAB import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_GROUP_TABLE; import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_MIRROR_TABLE; import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_TABLE; +import static org.onosproject.openstacknode.api.Constants.INTEGRATION_BRIDGE; +import static org.onosproject.openstacknode.api.NodeState.COMPLETE; import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE; +import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.containsIp; +import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.dumpStackTrace; import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getGroupKey; +import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getTunnelName; +import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getTunnelType; +import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.hostCompareIp; +import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.isValidHost; import static org.slf4j.LoggerFactory.getLogger; /** - * Provides basic implementation of the user APIs. + * Provides implementation of the openstack vtap and openstack vtap network APIs. */ @Component(immediate = true) @Service @@ -138,12 +154,6 @@ public class OpenstackVtapManager @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected LeadershipService leadershipService; - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected MastershipService mastershipService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected DriverService driverService; - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected FlowRuleService flowRuleService; @@ -153,6 +163,9 @@ public class OpenstackVtapManager @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected DeviceService deviceService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected OpenstackNodeService osNodeService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected HostService hostService; @@ -160,30 +173,50 @@ public class OpenstackVtapManager protected OpenstackVtapStore store; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected OpenstackNodeService osNodeService; + protected ComponentConfigService componentConfigService; + + private static final boolean DEFAULT_TUNNEL_NICRA = false; + @Property(name = TUNNEL_NICIRA, boolValue = DEFAULT_TUNNEL_NICRA, + label = "Use nicra extension for tunneling") + private boolean tunnelNicira = DEFAULT_TUNNEL_NICRA; public static final String APP_ID = "org.onosproject.openstackvtap"; - - public static final String VTAP_ID_NULL = "OpenstackVtap ID cannot be null"; - public static final String VTAP_DESC_NULL = "OpenstackVtap fields cannot be null"; - public static final String DEVICE_ID_NULL = "Device ID cannot be null"; + public static final String VTAP_DESC_NULL = "vtap field %s cannot be null"; private static final int PRIORITY_VTAP_RULE = 50000; - private static final int PRIORITY_VTAP_OUTPORT_RULE = 1000; - private static final int PRIORITY_VTAP_DROP = 0; + private static final int PRIORITY_VTAP_OUTPUT_RULE = 1000; + private static final int PRIORITY_VTAP_OUTPUT_DROP = 0; - private static final int NONE_TABLE = -1; private static final int INBOUND_NEXT_TABLE = DHCP_ARP_TABLE; private static final int FLAT_OUTBOUND_NEXT_TABLE = FLAT_TABLE; private static final int OUTBOUND_NEXT_TABLE = FORWARDING_TABLE; + private static final int[][] VTAP_TABLES = { + {VTAP_INBOUND_TABLE, VTAP_INBOUND_GROUP_TABLE, + INBOUND_NEXT_TABLE, VTAP_INBOUND_MIRROR_TABLE}, + {VTAP_FLAT_OUTBOUND_TABLE, VTAP_FLAT_OUTBOUND_GROUP_TABLE, + FLAT_OUTBOUND_NEXT_TABLE, VTAP_FLAT_OUTBOUND_MIRROR_TABLE}, + {VTAP_OUTBOUND_TABLE, VTAP_OUTBOUND_GROUP_TABLE, + OUTBOUND_NEXT_TABLE, VTAP_OUTBOUND_MIRROR_TABLE}}; + private static final int VTAP_TABLE_INBOUND_IDX = 0; + private static final int VTAP_TABLE_FLAT_OUTBOUND_IDX = 1; + private static final int VTAP_TABLE_OUTBOUND_IDX = 2; + private static final int VTAP_TABLE_INPUT_IDX = 0; + private static final int VTAP_TABLE_GROUP_IDX = 1; + private static final int VTAP_TABLE_NEXT_IDX = 2; + private static final int VTAP_TABLE_OUTPUT_IDX = 3; + private static final IpPrefix ARBITRARY_IP_PREFIX = IpPrefix.valueOf(IpAddress.valueOf("0.0.0.0"), 0); - private static final String TABLE_PROPERTY_KEY = "table"; + private static final String TABLE_EXTENSION = "table"; + private static final String TUNNEL_DST_EXTENSION = "tunnelDst"; + private static final String TUNNEL_NICIRA = "tunnelNicira"; + + private static final int VTAP_NETWORK_KEY = 0; private final DeviceListener deviceListener = new InternalDeviceListener(); - private final HostListener hostListener = new InternalHostListener(); private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener(); + private final HostListener hostListener = new InternalHostListener(); private OpenstackVtapStoreDelegate delegate = new InternalStoreDelegate(); @@ -191,12 +224,16 @@ public class OpenstackVtapManager private NodeId localNodeId; private ScheduledExecutorService eventExecutor; + private final Object syncInterface = new Object(); // notification of tunnel interface + private static final int INTERFACE_MANIPULATION_TIMEOUT = 1000; // 1000msec + private static final int INTERFACE_MANIPULATION_RETRY = 10; // 10 times (totally 10sec) @Activate public void activate(ComponentContext context) { appId = coreService.registerApplication(APP_ID); localNodeId = clusterService.getLocalNode().id(); leadershipService.runForLeadership(appId.name()); + componentConfigService.registerProperties(getClass()); eventExecutor = newSingleThreadScheduledExecutor( groupedThreads(this.getClass().getSimpleName(), "event-handler", log)); @@ -205,29 +242,200 @@ public class OpenstackVtapManager eventDispatcher.addSink(OpenstackVtapEvent.class, listenerRegistry); deviceService.addListener(deviceListener); - hostService.addListener(hostListener); osNodeService.addListener(osNodeListener); + hostService.addListener(hostListener); - initFlowAndGroupForCompNodes(); + initVtap(); - log.info("Started {} - {}", appId.name(), this.getClass().getSimpleName()); + log.info("Started"); } @Deactivate public void deactivate() { - clearFlowAndGroupForCompNodes(); + clearVtap(); - osNodeService.removeListener(osNodeListener); hostService.removeListener(hostListener); + osNodeService.removeListener(osNodeListener); deviceService.removeListener(deviceListener); eventDispatcher.removeSink(OpenstackVtapEvent.class); store.unsetDelegate(delegate); eventExecutor.shutdown(); + + componentConfigService.unregisterProperties(getClass(), false); leadershipService.withdraw(appId.name()); - log.info("Stopped {} - {}", appId.name(), this.getClass().getSimpleName()); + log.info("Stopped"); + } + + @Modified + protected void modified(ComponentContext context) { + Dictionary properties = context.getProperties(); + + boolean updatedTunnelNicira = Tools.isPropertyEnabled(properties, TUNNEL_NICIRA); + if (tunnelNicira != updatedTunnelNicira) { + if (Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))) { + // Update the tunnel flow rule by reflecting the change. + osNodeService.completeNodes(COMPUTE) + .forEach(osNode -> applyVtapNetwork(getVtapNetwork(), osNode, false)); + tunnelNicira = updatedTunnelNicira; + osNodeService.completeNodes(COMPUTE).stream() + .filter(osNode -> osNode.state() == COMPLETE) + .forEach(osNode -> applyVtapNetwork(getVtapNetwork(), osNode, true)); + log.debug("Apply {} nicira extension for tunneling", tunnelNicira ? "enable" : "disable"); + } else { + tunnelNicira = updatedTunnelNicira; + } + } + + log.info("Modified"); + } + + /** + * Initializes the flow rules and group tables, tunneling interface for all completed compute nodes. + */ + @Override + public void initVtap() { + if (Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))) { + osNodeService.completeNodes(COMPUTE).stream() + .filter(osNode -> osNode.state() == COMPLETE) + .forEach(osNode -> initVtapForNode(osNode)); + log.trace("{} flow rules, groups, tunnel interface are initialized", appId.name()); + } + } + + /** + * Clears the flow rules and group tables, tunneling interface for all compute nodes. + */ + @Override + public void clearVtap() { + if (Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))) { + osNodeService.completeNodes(COMPUTE).stream() + .forEach(osNode -> clearVtapForNode(osNode)); + log.trace("{} flow rules, groups, tunnel interface are cleared", appId.name()); + } + } + + /** + * Purges all flow rules and group tables, tunneling interface for openstack vtap. + */ + @Override + public void purgeVtap() { + // Remove all flow rules + flowRuleService.removeFlowRulesById(appId); + + // Remove all groups and tunnel interfaces + osNodeService.completeNodes(COMPUTE).stream() + .filter(osNode -> osNode.state() == COMPLETE) + .forEach(osNode -> { + groupService.getGroups(osNode.intgBridge(), appId) + .forEach(group -> + groupService.removeGroup(osNode.intgBridge(), group.appCookie(), appId)); + + OpenstackVtapNetwork vtapNetwork = getVtapNetwork(); + setTunnelInterface(osNode, vtapNetwork, false); + }); + + log.trace("{} all flow rules, groups, tunnel interface are purged", appId.name()); + } + + private void initVtapForNode(OpenstackNode osNode) { + // Make base vtap network + initVtapNetwork(osNode); + + // Make vtap connections by OpenstackVtap config + getVtapsByDeviceId(osNode.intgBridge()) + .forEach(vtap -> applyVtap(vtap, osNode, true)); + + // Make vtap networks by OpenstackVtapNetwork config + applyVtapNetwork(getVtapNetwork(), osNode, true); + } + + private void clearVtapForNode(OpenstackNode osNode) { + // Clear vtap networks by OpenstackVtapNetwork config + applyVtapNetwork(getVtapNetwork(), osNode, false); + + // Clear vtap connections by OpenstackVtap config + getVtapsByDeviceId(osNode.intgBridge()) + .forEach(vtap -> applyVtap(vtap, osNode, false)); + + // Clear base vtap network + clearVtapNetwork(osNode); + } + + /** + * Initializes vtap pipeline of the given device. + * + * @param osNode device identifier + */ + private void initVtapNetwork(OpenstackNode osNode) { + // Create default output tables + for (int idx = 0; idx < VTAP_TABLES.length; idx++) { + setOutputTableForDrop(osNode.intgBridge(), + VTAP_TABLES[idx][VTAP_TABLE_OUTPUT_IDX], true); + } + + // Create group tables + for (int idx = 0; idx < VTAP_TABLES.length; idx++) { + createGroupTable(osNode.intgBridge(), + VTAP_TABLES[idx][VTAP_TABLE_GROUP_IDX], + ImmutableList.of(VTAP_TABLES[idx][VTAP_TABLE_NEXT_IDX], + VTAP_TABLES[idx][VTAP_TABLE_OUTPUT_IDX]), + null); + } + } + + /** + * Clear vtap pipeline of the given device. + * + * @param osNode device identifier + */ + private void clearVtapNetwork(OpenstackNode osNode) { + // Clear group tables + for (int idx = 0; idx < VTAP_TABLES.length; idx++) { + removeGroupTable(osNode.intgBridge(), + VTAP_TABLES[idx][VTAP_TABLE_GROUP_IDX]); + } + + // Clear default output tables + for (int idx = 0; idx < VTAP_TABLES.length; idx++) { + setOutputTableForDrop(osNode.intgBridge(), + VTAP_TABLES[idx][VTAP_TABLE_OUTPUT_IDX], false); + } + } + + @Override + public OpenstackVtapNetwork getVtapNetwork() { + return store.getVtapNetwork(VTAP_NETWORK_KEY); + } + + @Override + public OpenstackVtapNetwork createVtapNetwork(Mode mode, Integer networkId, IpAddress serverIp) { + checkNotNull(mode, VTAP_DESC_NULL, "mode"); + checkNotNull(serverIp, VTAP_DESC_NULL, "serverIp"); + DefaultOpenstackVtapNetwork vtapNetwork = DefaultOpenstackVtapNetwork.builder() + .mode(mode) + .networkId(networkId) + .serverIp(serverIp) + .build(); + return store.createVtapNetwork(VTAP_NETWORK_KEY, vtapNetwork); + } + + @Override + public OpenstackVtapNetwork updateVtapNetwork(OpenstackVtapNetwork description) { + checkNotNull(description, VTAP_DESC_NULL, "vtapNetwork"); + return store.updateVtapNetwork(VTAP_NETWORK_KEY, description); + } + + @Override + public OpenstackVtapNetwork removeVtapNetwork() { + return store.removeVtapNetwork(VTAP_NETWORK_KEY); + } + + @Override + public Set getVtapNetworkDevices() { + return store.getVtapNetworkDevices(VTAP_NETWORK_KEY); } @Override @@ -241,87 +449,54 @@ public class OpenstackVtapManager } @Override - public OpenstackVtap getVtap(OpenstackVtapId vTapId) { - checkNotNull(vTapId, VTAP_ID_NULL); - return store.getVtap(vTapId); + public OpenstackVtap getVtap(OpenstackVtapId vtapId) { + return store.getVtap(vtapId); } @Override - public Set getVtapsByDeviceId(OpenstackVtap.Type type, - DeviceId deviceId) { - checkNotNull(deviceId, DEVICE_ID_NULL); - return store.getVtapsByDeviceId(type, deviceId); + public Set getVtapsByDeviceId(DeviceId deviceId) { + return store.getVtapsByDeviceId(deviceId); } @Override - public OpenstackVtap createVtap(Type type, OpenstackVtapCriterion vTapCriterion) { - checkNotNull(vTapCriterion, VTAP_DESC_NULL); + public OpenstackVtap createVtap(Type type, OpenstackVtapCriterion vtapCriterion) { + checkNotNull(type, VTAP_DESC_NULL, "type"); + checkNotNull(vtapCriterion, VTAP_DESC_NULL, "vtapCriterion"); Set txDevices = type.isValid(Type.VTAP_TX) ? - getEdgeDevice(type, vTapCriterion) : ImmutableSet.of(); + getEdgeDevice(Type.VTAP_TX, vtapCriterion) : ImmutableSet.of(); Set rxDevices = type.isValid(Type.VTAP_RX) ? - getEdgeDevice(type, vTapCriterion) : ImmutableSet.of(); + getEdgeDevice(Type.VTAP_RX, vtapCriterion) : ImmutableSet.of(); - OpenstackVtap description = - DefaultOpenstackVtap.builder() - .id(OpenstackVtapId.vTapId()) - .type(type) - .vTapCriterion(vTapCriterion) - .txDeviceIds(txDevices) - .rxDeviceIds(rxDevices) - .build(); - return store.createOrUpdateVtap(description.id(), description, true); + DefaultOpenstackVtap description = DefaultOpenstackVtap.builder() + .id(OpenstackVtapId.vtapId()) + .type(type) + .vtapCriterion(vtapCriterion) + .txDeviceIds(txDevices) + .rxDeviceIds(rxDevices) + .build(); + return store.createVtap(description); } @Override - public OpenstackVtap updateVtap(OpenstackVtapId vTapId, OpenstackVtap vTap) { - checkNotNull(vTapId, VTAP_ID_NULL); - checkNotNull(vTap, VTAP_DESC_NULL); + public OpenstackVtap updateVtap(OpenstackVtap description) { + checkNotNull(description, VTAP_DESC_NULL, "vtap"); - if (store.getVtap(vTapId) == null) { - return null; - } + Set txDevices = description.type().isValid(Type.VTAP_TX) ? + getEdgeDevice(Type.VTAP_TX, description.vtapCriterion()) : ImmutableSet.of(); + Set rxDevices = description.type().isValid(Type.VTAP_RX) ? + getEdgeDevice(Type.VTAP_RX, description.vtapCriterion()) : ImmutableSet.of(); - Set txDevices = vTap.type().isValid(Type.VTAP_TX) ? - getEdgeDevice(vTap.type(), vTap.vTapCriterion()) : ImmutableSet.of(); - Set rxDevices = vTap.type().isValid(Type.VTAP_RX) ? - getEdgeDevice(vTap.type(), vTap.vTapCriterion()) : ImmutableSet.of(); - - DefaultOpenstackVtap description = - DefaultOpenstackVtap.builder() - .id(vTapId) - .type(vTap.type()) - .vTapCriterion(vTap.vTapCriterion()) - .txDeviceIds(txDevices) - .rxDeviceIds(rxDevices) - .build(); - return store.createOrUpdateVtap(vTapId, description, true); + DefaultOpenstackVtap vtap = DefaultOpenstackVtap.builder(description) + .txDeviceIds(txDevices) + .rxDeviceIds(rxDevices) + .build(); + return store.updateVtap(vtap, true); } @Override - public OpenstackVtap removeVtap(OpenstackVtapId vTapId) { - checkNotNull(vTapId, VTAP_ID_NULL); - return store.removeVtapById(vTapId); - } - - @Override - public void setVtapOutput(DeviceId deviceId, OpenstackVtap.Type type, - PortNumber portNumber, VlanId vlanId) { - - // Make output table - if (type.isValid(Type.VTAP_TX)) { - createOutputTable(deviceId, VTAP_INBOUND_MIRROR_TABLE, portNumber, vlanId); - } - - if (type.isValid(Type.VTAP_RX)) { - createOutputTable(deviceId, VTAP_FLAT_OUTBOUND_MIRROR_TABLE, portNumber, vlanId); - createOutputTable(deviceId, VTAP_OUTBOUND_MIRROR_TABLE, portNumber, vlanId); - } - } - - @Override - public void setVtapOutput(DeviceId deviceId, Type type, PortNumber portNumber, int vni) { - // TODO: need to provide implementation + public OpenstackVtap removeVtap(OpenstackVtapId vtapId) { + return store.removeVtap(vtapId); } /** @@ -329,274 +504,138 @@ public class OpenstackVtapManager * Note that, in most of cases target host is attached to one device, * however, in some cases, the host can be attached to multiple devices. * - * @param type vTap type - * @param criterion vTap criterion + * @param type vtap type + * @param criterion vtap criterion * @return a collection of device identifiers */ private Set getEdgeDevice(Type type, OpenstackVtapCriterion criterion) { Set deviceIds = Sets.newConcurrentHashSet(); StreamSupport.stream(hostService.getHosts().spliterator(), true) - .forEach(host -> { - if (host.ipAddresses().stream() - .anyMatch(ip -> containsIp(type, criterion, ip))) { - deviceIds.addAll(host.locations().stream() - .map(HostLocation::deviceId) - .collect(Collectors.toSet())); - } - }); + .filter(host -> isValidHost(host) && + host.ipAddresses().stream().anyMatch(ip -> containsIp(type, criterion, ip))) + .forEach(host -> { + Set hostDeviceIds = + host.locations().stream() + .map(HostLocation::deviceId) + .filter(deviceId -> Objects.nonNull(osNodeService.node(deviceId))) + .collect(Collectors.toSet()); + deviceIds.addAll(hostDeviceIds); + }); return deviceIds; } /** - * Checks whether the given IP address is included in vTap criterion. - * We both check the TX and RX directions. - * - * @param type vTap type - * @param criterion vTap criterion - * @param ip IP address - * @return boolean value indicates the check result - */ - private boolean containsIp(Type type, OpenstackVtapCriterion criterion, IpAddress ip) { - boolean isTxEdge = type.isValid(Type.VTAP_TX) && - criterion.srcIpPrefix().contains(ip); - boolean isRxEdge = type.isValid(Type.VTAP_RX) && - criterion.dstIpPrefix().contains(ip); - - return isTxEdge || isRxEdge; - } - - /** - * Updates device list of vTaps with respect to the host changes. + * Updates device list of vtaps with respect to the host changes. * * @param newHost new host instance * @param oldHost old host instance */ + private void updateHostbyType(Type type, Host newHost, Host oldHost) { + getVtaps(type).forEach(vtap -> { + IpPrefix prefix = (type == Type.VTAP_TX) ? + vtap.vtapCriterion().srcIpPrefix() : + vtap.vtapCriterion().dstIpPrefix(); + + int hostDiff = hostCompareIp(newHost, oldHost, prefix); + if (hostDiff < 0) { + oldHost.locations().stream() + .map(HostLocation::deviceId) + .forEach(deviceId -> + store.removeDeviceFromVtap(vtap.id(), type, deviceId)); + } else if (hostDiff > 0) { + newHost.locations().stream() + .map(HostLocation::deviceId) + .filter(deviceId -> Objects.nonNull(osNodeService.node(deviceId))) + .forEach(deviceId -> + store.addDeviceToVtap(vtap.id(), type, deviceId)); + } + }); + } + private void updateHost(Host newHost, Host oldHost) { - // update devices for vTap tx - getVtaps(Type.VTAP_TX).parallelStream().forEach(vTap -> { + // update devices for vtap tx + updateHostbyType(Type.VTAP_TX, newHost, oldHost); - if (hostDiff(oldHost, newHost, vTap.vTapCriterion().srcIpPrefix())) { - oldHost.locations().stream().map(HostLocation::deviceId) - .forEach(deviceId -> - store.removeDeviceFromVtap(vTap.id(), Type.VTAP_TX, - oldHost.location().deviceId())); - } - - if (hostDiff(newHost, oldHost, vTap.vTapCriterion().srcIpPrefix())) { - newHost.locations().stream().map(HostLocation::deviceId) - .forEach(deviceId -> - store.addDeviceToVtap(vTap.id(), Type.VTAP_TX, - newHost.location().deviceId())); - } - }); - - // update devices for vTap rx - getVtaps(Type.VTAP_RX).parallelStream().forEach(vTap -> { - - if (hostDiff(oldHost, newHost, vTap.vTapCriterion().dstIpPrefix())) { - oldHost.locations().stream().map(HostLocation::deviceId) - .forEach(deviceId -> - store.removeDeviceFromVtap(vTap.id(), Type.VTAP_RX, - oldHost.location().deviceId())); - } - - if (hostDiff(newHost, oldHost, vTap.vTapCriterion().dstIpPrefix())) { - newHost.locations().stream().map(HostLocation::deviceId) - .forEach(deviceId -> - store.addDeviceToVtap(vTap.id(), Type.VTAP_RX, - newHost.location().deviceId())); - } - }); + // update devices for vtap rx + updateHostbyType(Type.VTAP_RX, newHost, oldHost); } - /** - * Checks whether the given IP prefix is contained in the first host rather - * than in the second host. - * - * @param host1 first host instance - * @param host2 second host instance - * @param ipPrefix IP prefix to be looked up - * @return boolean value - */ - private boolean hostDiff(Host host1, Host host2, IpPrefix ipPrefix) { - return ((host1 != null && host1.ipAddresses().stream().anyMatch(ipPrefix::contains)) && - (host2 == null || host2.ipAddresses().stream().noneMatch(ipPrefix::contains))); - } + private void applyFlowRule(FlowRule flowRule, boolean install) { + FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder(); - /** - * Initializes the flow rules and group tables for all completed compute nodes. - */ - private void initFlowAndGroupForCompNodes() { - osNodeService.completeNodes(COMPUTE).forEach(node -> - initFlowAndGroupByDeviceId(node.intgBridge())); - } - - /** - * Initializes the flow rules and group table of the given device identifier. - * - * @param deviceId device identifier - */ - private void initFlowAndGroupByDeviceId(DeviceId deviceId) { - // Make vTap pipeline - // TODO: need to selective creation by store device consistentMap - initVtapPipeline(deviceId); - - // Install tx filter - getVtapsByDeviceId(Type.VTAP_TX, deviceId).forEach(vTap -> { - connectTables(deviceId, - VTAP_INBOUND_TABLE, NONE_TABLE, VTAP_INBOUND_GROUP_TABLE, - vTap.vTapCriterion(), PRIORITY_VTAP_RULE, true); - }); - - // Install rx filter - getVtapsByDeviceId(Type.VTAP_RX, deviceId).forEach(vTap -> { - connectTables(deviceId, - VTAP_FLAT_OUTBOUND_TABLE, NONE_TABLE, VTAP_FLAT_OUTBOUND_GROUP_TABLE, - vTap.vTapCriterion(), PRIORITY_VTAP_RULE, true); - connectTables(deviceId, - VTAP_OUTBOUND_TABLE, NONE_TABLE, VTAP_OUTBOUND_GROUP_TABLE, - vTap.vTapCriterion(), PRIORITY_VTAP_RULE, true); - }); - } - - /** - * Initializes vTap pipeline of the given device. - * - * @param deviceId device identifier - */ - private void initVtapPipeline(DeviceId deviceId) { - // Make output table - createOutputTable(deviceId, VTAP_INBOUND_MIRROR_TABLE, null, null); - createOutputTable(deviceId, VTAP_FLAT_OUTBOUND_MIRROR_TABLE, null, null); - createOutputTable(deviceId, VTAP_OUTBOUND_MIRROR_TABLE, null, null); - - // Make tx group table - createGroupTable(deviceId, VTAP_INBOUND_GROUP_TABLE, - ImmutableList.of(INBOUND_NEXT_TABLE, VTAP_INBOUND_MIRROR_TABLE), - ImmutableList.of()); - - // Make rx group table - createGroupTable(deviceId, VTAP_FLAT_OUTBOUND_GROUP_TABLE, - ImmutableList.of(FLAT_OUTBOUND_NEXT_TABLE, VTAP_FLAT_OUTBOUND_MIRROR_TABLE), - ImmutableList.of()); - createGroupTable(deviceId, VTAP_OUTBOUND_GROUP_TABLE, - ImmutableList.of(OUTBOUND_NEXT_TABLE, VTAP_OUTBOUND_MIRROR_TABLE), - ImmutableList.of()); - } - - /** - * Purges all flow rules and group tables for completed compute nodes. - */ - private void clearFlowAndGroupForCompNodes() { - osNodeService.completeNodes(COMPUTE).forEach(node -> - clearFlowAndGroupByDeviceId(node.intgBridge())); - } - - /** - * Purges all flow rules and group tables using the given device identifier. - * - * @param deviceId device identifier - */ - private void clearFlowAndGroupByDeviceId(DeviceId deviceId) { - Set purgedRules = Sets.newConcurrentHashSet(); - for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) { - if (flowRule.deviceId().equals(deviceId)) { - purgedRules.add(flowRule); - } + if (install) { + flowOpsBuilder.add(flowRule); + } else { + flowOpsBuilder.remove(flowRule); } - flowRuleService.removeFlowRules(purgedRules.toArray(new FlowRule[0])); + flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() { + @Override + public void onSuccess(FlowRuleOperations ops) { + log.debug("Installed flow rules for vtap"); + } - groupService.getGroups(deviceId, appId).forEach(group -> { - groupService.removeGroup(deviceId, group.appCookie(), appId); - }); - log.info("OpenstackVtap flow rules and groups are purged"); + @Override + public void onError(FlowRuleOperations ops) { + log.warn("Failed to install flow rules for vtap"); + } + })); } - private void installFilterRule(Set txDeviceIds, Set rxDeviceIds, - OpenstackVtapCriterion vTapCriterion, boolean install) { - final int inbound = 0; - final int flatOutbound = 1; - final int outbound = 2; - - BiFunction, Integer, Void> installFlow = (deviceIds, table) -> { - int inTable = (table == inbound ? VTAP_INBOUND_TABLE : - (table == flatOutbound ? VTAP_FLAT_OUTBOUND_TABLE : - VTAP_OUTBOUND_TABLE)); - - int outGroup = (table == inbound ? VTAP_INBOUND_GROUP_TABLE : - (table == flatOutbound ? VTAP_FLAT_OUTBOUND_GROUP_TABLE : - VTAP_OUTBOUND_GROUP_TABLE)); - - deviceIds.stream() - .filter(deviceId -> mastershipService.isLocalMaster(deviceId)) - .forEach(deviceId -> { - connectTables(deviceId, inTable, NONE_TABLE, outGroup, - vTapCriterion, PRIORITY_VTAP_RULE, install); - }); - return null; - }; - - installFlow.apply(txDeviceIds, inbound); - installFlow.apply(rxDeviceIds, flatOutbound); - installFlow.apply(rxDeviceIds, outbound); - } - - private void connectTables(DeviceId deviceId, int fromTable, int toTable, int toGroup, - OpenstackVtapCriterion vTapCriterion, int rulePriority, - boolean install) { - log.trace("Table Transition: table[{}] -> table[{}] or group[{}]", fromTable, toTable, toGroup); + private void connectTables(DeviceId deviceId, + int fromTable, + int toTableOrGroup, boolean isGroup, + OpenstackVtapCriterion vtapCriterion, + int rulePriority, boolean install) { + log.debug("Table Transition: table[{}] -> table/group[{}]", fromTable, toTableOrGroup); TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder() .matchEthType(TYPE_IPV4); // if the IpPrefix is "0.0.0.0/0", we do not include such a match into the flow rule - if (!vTapCriterion.srcIpPrefix().equals(ARBITRARY_IP_PREFIX)) { - selectorBuilder.matchIPSrc(vTapCriterion.srcIpPrefix()); + if (!vtapCriterion.srcIpPrefix().equals(ARBITRARY_IP_PREFIX)) { + selectorBuilder.matchIPSrc(vtapCriterion.srcIpPrefix()); } - if (!vTapCriterion.dstIpPrefix().equals(ARBITRARY_IP_PREFIX)) { - selectorBuilder.matchIPDst(vTapCriterion.dstIpPrefix()); + if (!vtapCriterion.dstIpPrefix().equals(ARBITRARY_IP_PREFIX)) { + selectorBuilder.matchIPDst(vtapCriterion.dstIpPrefix()); } - switch (vTapCriterion.ipProtocol()) { + switch (vtapCriterion.ipProtocol()) { case PROTOCOL_TCP: - selectorBuilder.matchIPProtocol(vTapCriterion.ipProtocol()); + selectorBuilder.matchIPProtocol(vtapCriterion.ipProtocol()); // Add port match only if the port number is greater than zero - if (vTapCriterion.srcTpPort().toInt() > 0) { - selectorBuilder.matchTcpSrc(vTapCriterion.srcTpPort()); + if (vtapCriterion.srcTpPort().toInt() > 0) { + selectorBuilder.matchTcpSrc(vtapCriterion.srcTpPort()); } - if (vTapCriterion.dstTpPort().toInt() > 0) { - selectorBuilder.matchTcpDst(vTapCriterion.dstTpPort()); + if (vtapCriterion.dstTpPort().toInt() > 0) { + selectorBuilder.matchTcpDst(vtapCriterion.dstTpPort()); } break; case PROTOCOL_UDP: - selectorBuilder.matchIPProtocol(vTapCriterion.ipProtocol()); + selectorBuilder.matchIPProtocol(vtapCriterion.ipProtocol()); // Add port match only if the port number is greater than zero - if (vTapCriterion.srcTpPort().toInt() > 0) { - selectorBuilder.matchUdpSrc(vTapCriterion.srcTpPort()); + if (vtapCriterion.srcTpPort().toInt() > 0) { + selectorBuilder.matchUdpSrc(vtapCriterion.srcTpPort()); } - if (vTapCriterion.dstTpPort().toInt() > 0) { - selectorBuilder.matchUdpDst(vTapCriterion.dstTpPort()); + if (vtapCriterion.dstTpPort().toInt() > 0) { + selectorBuilder.matchUdpDst(vtapCriterion.dstTpPort()); } break; case PROTOCOL_ICMP: - selectorBuilder.matchIPProtocol(vTapCriterion.ipProtocol()); + selectorBuilder.matchIPProtocol(vtapCriterion.ipProtocol()); break; default: break; } TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder(); - if (toTable != NONE_TABLE) { - treatmentBuilder.transition(toTable); - } else if (toGroup != NONE_TABLE) { - treatmentBuilder.group(GroupId.valueOf(toGroup)); + if (isGroup) { + treatmentBuilder.group(GroupId.valueOf(toTableOrGroup)); } else { - log.warn("Not specified toTable or toGroup value"); - return; + treatmentBuilder.transition(toTableOrGroup); } FlowRule flowRule = DefaultFlowRule.builder() @@ -612,69 +651,246 @@ public class OpenstackVtapManager applyFlowRule(flowRule, install); } - private void createOutputTable(DeviceId deviceId, int tableId, - PortNumber outPort, VlanId vlanId) { - TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); - TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); - - // Set output port & vlan - int priority = PRIORITY_VTAP_DROP; - if (vlanId != null && vlanId.toShort() != UNTAGGED) { - treatment.pushVlan().setVlanId(vlanId); + /** + * Creates/Removes a tunnel interface in a given openstack node by vtap network information. + * + * @param osNode openstack node + * @param vtapNetwork openstack vtap network for making + * + */ + private boolean setTunnelInterface(OpenstackNode osNode, + OpenstackVtapNetwork vtapNetwork, + boolean install) { + String tunnelName = getTunnelName(vtapNetwork.mode()); + if (tunnelName == null) { + return false; } - if (outPort != null) { - treatment.setOutput(outPort); - priority = PRIORITY_VTAP_OUTPORT_RULE; + + if (!deviceService.isAvailable(osNode.ovsdb())) { + log.warn("Not available osNode {} ovs {}", osNode.hostname(), osNode.ovsdb()); + return false; + } + + if (install == isInterfaceEnabled(osNode.intgBridge(), tunnelName)) { + log.warn("Already {} {} interface on osNode ovs {}, bridge {}", + install ? "add" : "remove", + tunnelName, osNode.ovsdb(), osNode.intgBridge()); + return true; + } + + Device device = deviceService.getDevice(osNode.ovsdb()); + if (device == null || !device.is(InterfaceConfig.class)) { + log.warn("Not able to get InterfaceConfig on osNode ovs {}", osNode.ovsdb()); + return false; + } + + InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class); + if (install) { + TunnelDescription.Builder tunnelDesc = DefaultTunnelDescription.builder() + .deviceId(INTEGRATION_BRIDGE) + .ifaceName(tunnelName) + .type(getTunnelType(vtapNetwork.mode())) + .key((vtapNetwork.networkId() == 0) ? null : new TunnelKey<>(vtapNetwork.networkId())) + .remote(TunnelEndPoints.ipTunnelEndpoint(vtapNetwork.serverIp())); + if (!ifaceConfig.addTunnelMode(tunnelName, tunnelDesc.build())) { + log.error("Fail to create {} interface on osNode ovs {}", tunnelName, osNode.ovsdb()); + return false; + } + } else { + if (!ifaceConfig.removeTunnelMode(tunnelName)) { + log.error("Fail to remove {} interface on osNode ovs {}", tunnelName, osNode.ovsdb()); + return false; + } + } + + // Wait for tunnel interface create/remove complete + synchronized (syncInterface) { + for (int i = 0; i < INTERFACE_MANIPULATION_RETRY; i++) { + try { + syncInterface.wait(INTERFACE_MANIPULATION_TIMEOUT); + if (install == isInterfaceEnabled(osNode.intgBridge(), tunnelName)) { + log.debug("Success to {} {} interface on osNode ovs {}, bridge {}", + install ? "add" : "remove", + tunnelName, osNode.ovsdb(), osNode.intgBridge()); + return true; + } + } catch (InterruptedException e) { + break; + } + } + } + log.warn("Fail to {} {} interface on osNode ovs {}, bridge {}", + install ? "add" : "remove", + tunnelName, osNode.ovsdb(), osNode.intgBridge()); + return false; + } + + /** + * Checks whether a given network interface in a given openstack node is enabled or not. + * + * @param deviceId openstack node + * @param interfaceName network interface name + * @return true if the given interface is enabled, false otherwise + */ + private boolean isInterfaceEnabled(DeviceId deviceId, String interfaceName) { + return deviceService.isAvailable(deviceId) && + deviceService.getPorts(deviceId).parallelStream().anyMatch(port -> + Objects.equals(port.annotations().value(PORT_NAME), interfaceName) && port.isEnabled()); + } + + private PortNumber portNumber(DeviceId deviceId, String interfaceName) { + Port port = deviceService.getPorts(deviceId).stream() + .filter(p -> p.isEnabled() && + Objects.equals(p.annotations().value(PORT_NAME), interfaceName)) + .findAny().orElse(null); + return port != null ? port.number() : null; + } + + private void setOutputTableForTunnel(DeviceId deviceId, int tableId, + PortNumber outPort, IpAddress serverIp, + boolean install) { + log.debug("setOutputTableForTunnel[{}]: deviceId={}, tableId={}, outPort={}, serverIp={}", + install ? "add" : "remove", deviceId, tableId, outPort, serverIp); + + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder() + .setOutput(outPort); + + if (tunnelNicira) { + ExtensionTreatment extensionTreatment = buildTunnelExtension(deviceId, serverIp); + if (extensionTreatment == null) { + return; + } + treatment.extension(extensionTreatment, deviceId); } FlowRule flowRule = DefaultFlowRule.builder() .forDevice(deviceId) .withSelector(selector.build()) .withTreatment(treatment.build()) - .withPriority(priority) + .withPriority(PRIORITY_VTAP_OUTPUT_RULE) .makePermanent() .forTable(tableId) .fromApp(appId) .build(); - applyFlowRule(flowRule, true); + + log.debug("setOutputTableForTunnel flowRule={}, install={}", flowRule, install); + applyFlowRule(flowRule, install); } - private ExtensionTreatment buildNiciraExtension(DeviceId id, int tableId) { - Driver driver = driverService.getDriver(id); - DriverHandler driverHandler = - new DefaultDriverHandler(new DefaultDriverData(driver, id)); - ExtensionTreatmentResolver resolver = - driverHandler.behaviour(ExtensionTreatmentResolver.class); + private void setOutputTableForDrop(DeviceId deviceId, int tableId, + boolean install) { + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); - ExtensionTreatment extensionInstruction = - resolver.getExtensionInstruction(NICIRA_RESUBMIT_TABLE.type()); + FlowRule flowRule = DefaultFlowRule.builder() + .forDevice(deviceId) + .withSelector(selector.build()) + .withTreatment(treatment.build()) + .withPriority(PRIORITY_VTAP_OUTPUT_DROP) + .makePermanent() + .forTable(tableId) + .fromApp(appId) + .build(); + applyFlowRule(flowRule, install); + } - try { - extensionInstruction.setPropertyValue(TABLE_PROPERTY_KEY, ((short) tableId)); - } catch (Exception e) { - log.error("Failed to set extension treatment for resubmit table {}", id); + private void setOutputTable(DeviceId deviceId, Mode mode, + IpAddress serverIp, boolean install) { + log.debug("setOutputTable[{}]: deviceId={}, mode={}, serverIp={}", + install ? "add" : "remove", deviceId, mode, serverIp); + + if (deviceId == null) { + return; } - return extensionInstruction; + switch (mode) { + case GRE: + case VXLAN: + String tunnelName = getTunnelName(mode); + PortNumber vtapPort = portNumber(deviceId, tunnelName); + if (vtapPort != null) { + for (int idx = 0; idx < VTAP_TABLES.length; idx++) { + setOutputTableForTunnel(deviceId, VTAP_TABLES[idx][VTAP_TABLE_OUTPUT_IDX], + vtapPort, serverIp, install); + } + } else { + log.warn("Vtap tunnel port {} doesn't exist", tunnelName); + } + break; + default: + log.warn("Invalid vtap network mode {}", mode); + break; + } + } + + /** + * Returns tunnel destination extension treatment object. + * + * @param deviceId device id to apply this treatment + * @param remoteIp tunnel destination ip address + * @return extension treatment + */ + private ExtensionTreatment buildTunnelExtension(DeviceId deviceId, IpAddress remoteIp) { + Device device = deviceService.getDevice(deviceId); + if (device == null || !device.is(ExtensionTreatmentResolver.class)) { + log.warn("Nicira extension treatment is not supported"); + return null; + } + + ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class); + ExtensionTreatment treatment = + resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type()); + try { + treatment.setPropertyValue(TUNNEL_DST_EXTENSION, remoteIp.getIp4Address()); + return treatment; + } catch (ExtensionPropertyException e) { + log.error("Failed to set nicira tunnelDst extension treatment for {}", deviceId); + return null; + } + } + + private ExtensionTreatment buildResubmitExtension(DeviceId deviceId, int tableId) { + Device device = deviceService.getDevice(deviceId); + if (device == null || !device.is(ExtensionTreatmentResolver.class)) { + log.warn("Nicira extension treatment is not supported"); + return null; + } + + ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class); + ExtensionTreatment treatment = + resolver.getExtensionInstruction(NICIRA_RESUBMIT_TABLE.type()); + + try { + treatment.setPropertyValue(TABLE_EXTENSION, ((short) tableId)); + return treatment; + } catch (ExtensionPropertyException e) { + log.error("Failed to set nicira resubmit extension treatment for {}", deviceId); + return null; + } } private void createGroupTable(DeviceId deviceId, int groupId, List tableIds, List ports) { List buckets = Lists.newArrayList(); - tableIds.forEach(tableId -> { - TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder() - .extension(buildNiciraExtension(deviceId, tableId), deviceId); - GroupBucket bucket = DefaultGroupBucket - .createAllGroupBucket(treatment.build()); - buckets.add(bucket); - }); - ports.forEach(port -> { - TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder() - .setOutput(port); - GroupBucket bucket = DefaultGroupBucket - .createAllGroupBucket(treatment.build()); - buckets.add(bucket); - }); + if (tableIds != null) { + tableIds.forEach(tableId -> { + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder() + .extension(buildResubmitExtension(deviceId, tableId), deviceId); + GroupBucket bucket = DefaultGroupBucket + .createAllGroupBucket(treatment.build()); + buckets.add(bucket); + }); + } + if (ports != null) { + ports.forEach(port -> { + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder() + .setOutput(port); + GroupBucket bucket = DefaultGroupBucket + .createAllGroupBucket(treatment.build()); + buckets.add(bucket); + }); + } GroupDescription groupDescription = new DefaultGroupDescription(deviceId, GroupDescription.Type.ALL, @@ -685,42 +901,36 @@ public class OpenstackVtapManager groupService.addGroup(groupDescription); } - private void applyFlowRule(FlowRule flowRule, boolean install) { - FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder(); - - flowOpsBuilder = install ? flowOpsBuilder.add(flowRule) : flowOpsBuilder.remove(flowRule); - - flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() { - @Override - public void onSuccess(FlowRuleOperations ops) { - log.debug("Installed flow rules for tapping"); - } - - @Override - public void onError(FlowRuleOperations ops) { - log.debug("Failed to install flow rules for tapping"); - } - })); + private void removeGroupTable(DeviceId deviceId, int groupId) { + groupService.removeGroup(deviceId, getGroupKey(groupId), appId); } + /** + * Internal listener for device events. + */ private class InternalDeviceListener implements DeviceListener { - @Override - public boolean isRelevant(DeviceEvent event) { - // do not allow to proceed without Mastership - DeviceId deviceId = event.subject().id(); - return mastershipService.isLocalMaster(deviceId) && - event.subject().type() == Device.Type.SWITCH; - } @Override public void event(DeviceEvent event) { DeviceEvent.Type type = event.type(); - DeviceId deviceId = event.subject().id(); - log.trace("InternalDeviceListener deviceId={}, type={}", deviceId, type); + Device device = event.subject(); switch (type) { - case DEVICE_ADDED: - eventExecutor.execute(() -> initFlowAndGroupByDeviceId(deviceId)); + case PORT_ADDED: + case PORT_UPDATED: + case PORT_REMOVED: + String portName = event.port().annotations().value(PORT_NAME); + if (portName.equals(getTunnelName(Mode.GRE)) || + portName.equals(getTunnelName(Mode.VXLAN))) { + log.trace("InternalDeviceListener type={}, host={}", type, device); + synchronized (syncInterface) { + try { + syncInterface.notifyAll(); + } catch (IllegalMonitorStateException e) { + log.warn("Already syncInterface exited"); + } + } + } break; default: break; @@ -728,9 +938,60 @@ public class OpenstackVtapManager } } + /** + * Internal listener for openstack node events. + */ + private class InternalOpenstackNodeListener implements OpenstackNodeListener { + + @Override + public boolean isRelevant(OpenstackNodeEvent event) { + // do not allow to proceed without leadership and compute node + NodeId leader = leadershipService.getLeader(appId.name()); + OpenstackNode osNode = event.subject(); + + return Objects.equals(localNodeId, leader) && osNode.type() == COMPUTE; + } + + @Override + public void event(OpenstackNodeEvent event) { + OpenstackNodeEvent.Type type = event.type(); + OpenstackNode osNode = event.subject(); + log.trace("InternalOpenstackNodeListener type={}, osNode={}", type, osNode); + + eventExecutor.execute(() -> { + try { + switch (type) { + case OPENSTACK_NODE_COMPLETE: + initVtapForNode(osNode); + break; + + case OPENSTACK_NODE_REMOVED: + clearVtapForNode(osNode); + break; + + default: + break; + } + } catch (Exception e) { + dumpStackTrace(log, e); + } + }); + } + } + + /** + * Internal listener for host events. + */ private class InternalHostListener implements HostListener { + @Override public boolean isRelevant(HostEvent event) { + Host host = event.subject(); + if (!isValidHost(host)) { + log.debug("Invalid host detected, ignore it {}", host); + return false; + } + // do not allow to proceed without leadership NodeId leader = leadershipService.getLeader(appId.name()); return Objects.equals(localNodeId, leader); @@ -740,102 +1001,173 @@ public class OpenstackVtapManager public void event(HostEvent event) { HostEvent.Type type = event.type(); Host host = event.subject(); - log.trace("InternalHostListener hostId={}, type={}", host.id(), type); + Host prevHost = event.prevSubject(); + log.trace("InternalHostListener {}: {} -> {}", type, prevHost, host); - switch (type) { - case HOST_ADDED: - eventExecutor.execute(() -> updateHost(host, null)); - break; + eventExecutor.execute(() -> { + try { + switch (event.type()) { + case HOST_ADDED: + updateHost(host, null); + break; - case HOST_REMOVED: - eventExecutor.execute(() -> updateHost(null, host)); - break; + case HOST_REMOVED: + updateHost(null, host); + break; - case HOST_UPDATED: - case HOST_MOVED: - eventExecutor.execute(() -> updateHost(host, event.prevSubject())); - break; - default: - break; - } - } - } + case HOST_MOVED: + case HOST_UPDATED: + updateHost(host, prevHost); + break; - private class InternalOpenstackNodeListener implements OpenstackNodeListener { - - @Override - public boolean isRelevant(OpenstackNodeEvent event) { - // do not allow to proceed without leadership - NodeId leader = leadershipService.getLeader(appId.name()); - return Objects.equals(localNodeId, leader) && event.subject().type() == COMPUTE; - } - - @Override - public void event(OpenstackNodeEvent event) { - DeviceId deviceId = event.subject().intgBridge(); - switch (event.type()) { - case OPENSTACK_NODE_CREATED: - case OPENSTACK_NODE_UPDATED: - eventExecutor.execute(() -> initFlowAndGroupByDeviceId(deviceId)); - break; - case OPENSTACK_NODE_REMOVED: - eventExecutor.execute(() -> clearFlowAndGroupByDeviceId(deviceId)); - break; - default: - break; - } + default: + break; + } + } catch (Exception e) { + dumpStackTrace(log, e); + } + }); } } // Store delegate to re-post events emitted from the store. private class InternalStoreDelegate implements OpenstackVtapStoreDelegate { + @Override public void notify(OpenstackVtapEvent event) { OpenstackVtapEvent.Type type = event.type(); - OpenstackVtap vTap = event.subject(); - log.trace("vTapStoreDelegate vTap={}, type={}", vTap, type); + log.trace("InternalStoreDelegate {}: {} -> {}", type, event.prevSubject(), event.subject()); - switch (type) { - case VTAP_ADDED: - eventExecutor.execute(() -> { - // Add new devices - installFilterRule(vTap.txDeviceIds(), vTap.rxDeviceIds(), - vTap.vTapCriterion(), true); - }); - break; + if (Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))) { + eventExecutor.execute(() -> { + try { + switch (type) { + case VTAP_NETWORK_ADDED: + case VTAP_NETWORK_UPDATED: + case VTAP_NETWORK_REMOVED: + // Update network + updateVtapNetwork(event.openstackVtapNetwork(), + event.prevOpenstackVtapNetwork()); + break; - case VTAP_UPDATED: - OpenstackVtap oldOpenstackVtap = event.prevSubject(); - eventExecutor.execute(() -> { - // Remove excluded devices - installFilterRule( - Sets.difference(oldOpenstackVtap.txDeviceIds(), - vTap.txDeviceIds()), - Sets.difference(oldOpenstackVtap.rxDeviceIds(), - vTap.rxDeviceIds()), - oldOpenstackVtap.vTapCriterion(), false); + case VTAP_ADDED: + case VTAP_UPDATED: + case VTAP_REMOVED: + // Update vtap rule + updateVtap(event.openstackVtap(), + event.prevOpenstackVtap()); + break; - // Add new devices - installFilterRule( - Sets.difference(vTap.txDeviceIds(), - oldOpenstackVtap.txDeviceIds()), - Sets.difference(vTap.rxDeviceIds(), - oldOpenstackVtap.rxDeviceIds()), - vTap.vTapCriterion(), true); - }); - break; - - case VTAP_REMOVED: - eventExecutor.execute(() -> { - // Remove excluded devices - installFilterRule(vTap.txDeviceIds(), vTap.rxDeviceIds(), - vTap.vTapCriterion(), false); - }); - break; - default: - break; + default: + break; + } + } catch (Exception e) { + dumpStackTrace(log, e); + } + }); } post(event); } } + + private void applyVtap(OpenstackVtap vtap, + OpenstackNode osNode, + boolean install) { + if (vtap == null || osNode == null) { + return; + } + + log.debug("applyVtap vtap={}, osNode={}, install={}", vtap, osNode, install); + + DeviceId deviceId = osNode.intgBridge(); + for (int idx = 0; idx < VTAP_TABLES.length; idx++) { + if ((idx == VTAP_TABLE_INBOUND_IDX && + vtap.type().isValid(Type.VTAP_TX) && + vtap.txDeviceIds().contains(deviceId)) || + (idx != VTAP_TABLE_INBOUND_IDX && + vtap.type().isValid(Type.VTAP_RX) && + vtap.rxDeviceIds().contains(deviceId))) { + connectTables(deviceId, + VTAP_TABLES[idx][VTAP_TABLE_INPUT_IDX], + VTAP_TABLES[idx][VTAP_TABLE_GROUP_IDX], + true, + vtap.vtapCriterion(), PRIORITY_VTAP_RULE, install); + } + } + } + + private void updateVtap(OpenstackVtap vtap, + OpenstackVtap prevVtap) { + if (Objects.equals(vtap, prevVtap)) { + return; + } + + Set prevTxDeviceIds = (prevVtap != null ? prevVtap.txDeviceIds() : ImmutableSet.of()); + Set txDeviceIds = (vtap != null ? vtap.txDeviceIds() : ImmutableSet.of()); + Set prevRxDeviceIds = (prevVtap != null ? prevVtap.rxDeviceIds() : ImmutableSet.of()); + Set rxDeviceIds = (vtap != null ? vtap.rxDeviceIds() : ImmutableSet.of()); + + // Remake all vtap rule + if (prevVtap != null) { + Set deviceIds = Sets.newHashSet(); + deviceIds.addAll(Sets.difference(prevTxDeviceIds, txDeviceIds)); + deviceIds.addAll(Sets.difference(prevRxDeviceIds, rxDeviceIds)); + deviceIds.stream() + .map(deviceId -> osNodeService.node(deviceId)) + .filter(osNode -> Objects.nonNull(osNode) && + osNode.type() == COMPUTE) + .forEach(osNode -> applyVtap(prevVtap, osNode, false)); + } + if (vtap != null) { + Set deviceIds = Sets.newHashSet(); + deviceIds.addAll(Sets.difference(txDeviceIds, prevTxDeviceIds)); + deviceIds.addAll(Sets.difference(rxDeviceIds, prevRxDeviceIds)); + deviceIds.stream() + .map(deviceId -> osNodeService.node(deviceId)) + .filter(osNode -> Objects.nonNull(osNode) && + osNode.type() == COMPUTE && osNode.state() == COMPLETE) + .forEach(osNode -> applyVtap(vtap, osNode, true)); + } + } + + // create/remove tunnel interface and output table + private boolean applyVtapNetwork(OpenstackVtapNetwork vtapNetwork, + OpenstackNode osNode, + boolean install) { + if (vtapNetwork == null || osNode == null) { + return false; + } + + if (install) { + if (setTunnelInterface(osNode, vtapNetwork, true)) { + setOutputTable(osNode.intgBridge(), vtapNetwork.mode(), vtapNetwork.serverIp(), true); + store.addDeviceToVtapNetwork(VTAP_NETWORK_KEY, osNode.intgBridge()); + return true; + } + } else { + Set deviceIds = getVtapNetworkDevices(); + if (deviceIds != null && deviceIds.contains(osNode.intgBridge())) { + store.removeDeviceFromVtapNetwork(VTAP_NETWORK_KEY, osNode.intgBridge()); + setOutputTable(osNode.intgBridge(), vtapNetwork.mode(), vtapNetwork.serverIp(), false); + setTunnelInterface(osNode, vtapNetwork, false); + return true; + } + } + return false; + } + + private void updateVtapNetwork(OpenstackVtapNetwork network, + OpenstackVtapNetwork prevNetwork) { + // Remake all output tables + if (prevNetwork != null) { + osNodeService.completeNodes(COMPUTE) + .forEach(osNode -> applyVtapNetwork(prevNetwork, osNode, false)); + } + if (network != null) { + osNodeService.completeNodes(COMPUTE).stream() + .filter(osNode -> osNode.state() == COMPLETE) + .forEach(osNode -> applyVtapNetwork(network, osNode, true)); + } + } + } diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/package-info.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/package-info.java index cc5feb749f..7790c8a940 100644 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/package-info.java +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/package-info.java @@ -15,6 +15,6 @@ */ /** - * Implementation for Openstack vtap. + * Implementation for openstack vtap. */ package org.onosproject.openstackvtap.impl; \ No newline at end of file diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/OpenstackVtapUtil.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/OpenstackVtapUtil.java index 1e676930e9..69919be109 100644 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/OpenstackVtapUtil.java +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/OpenstackVtapUtil.java @@ -16,15 +16,31 @@ package org.onosproject.openstackvtap.util; import org.onlab.packet.IPv4; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; +import org.onosproject.net.Host; +import org.onosproject.net.behaviour.TunnelDescription; import org.onosproject.net.group.DefaultGroupKey; import org.onosproject.net.group.GroupKey; import org.onosproject.openstackvtap.api.OpenstackVtap; +import org.onosproject.openstackvtap.api.OpenstackVtapCriterion; +import org.onosproject.openstackvtap.api.OpenstackVtapNetwork; +import org.slf4j.Logger; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; + +import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_NETWORK_ID; +import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_PORT_ID; /** - * An utility that used in openstack vTap app. + * An utilities that used in openstack vtap app. */ public final class OpenstackVtapUtil { + private static final String VTAP_TUNNEL_GRE = "vtap_gre"; + private static final String VTAP_TUNNEL_VXLAN = "vtap_vxlan"; private static final String VTAP_GROUP_KEY = "VTAP_GROUP_KEY"; /** @@ -33,32 +49,11 @@ public final class OpenstackVtapUtil { private OpenstackVtapUtil() { } - /** - * Obtains vTap type from the given string. - * - * @param str string - * @return vTap type - */ - public static OpenstackVtap.Type getVtapTypeFromString(String str) { - switch (str) { - case "all": - return OpenstackVtap.Type.VTAP_ALL; - case "tx": - return OpenstackVtap.Type.VTAP_TX; - case "rx": - return OpenstackVtap.Type.VTAP_RX; - case "none": - return OpenstackVtap.Type.VTAP_NONE; - default: - throw new IllegalArgumentException("Invalid vTap type string"); - } - } - /** * Obtains IP protocol type from the given string. * - * @param str string - * @return vTap type + * @param str protocol string + * @return IP protocol number */ public static byte getProtocolTypeFromString(String str) { switch (str) { @@ -68,12 +63,132 @@ public final class OpenstackVtapUtil { return IPv4.PROTOCOL_UDP; case "icmp": return IPv4.PROTOCOL_ICMP; - default: + case "any": return 0; + default: + throw new IllegalArgumentException("Invalid vtap protocol string"); } } + /** + * Obtains openstack vtap type from the given string. + * + * @param str vtap type string + * @return vtap type + */ + public static OpenstackVtap.Type getVtapTypeFromString(String str) { + switch (str) { + case "all": + return OpenstackVtap.Type.VTAP_ALL; + case "rx": + return OpenstackVtap.Type.VTAP_RX; + case "tx": + return OpenstackVtap.Type.VTAP_TX; + case "any": + return OpenstackVtap.Type.VTAP_ANY; + default: + throw new IllegalArgumentException("Invalid vtap type string"); + } + } + + /** + * Checks whether the given IP address is included in vtap criterion with + * TX and RX directions by given vtap type. + * + * @param type vtap type + * @param criterion vtap criterion + * @param ip IP address to check + * @return true on match address, false otherwise + */ + public static boolean containsIp(OpenstackVtap.Type type, OpenstackVtapCriterion criterion, IpAddress ip) { + boolean isTxEdge = type.isValid(OpenstackVtap.Type.VTAP_TX) && + criterion.srcIpPrefix().contains(ip); + boolean isRxEdge = type.isValid(OpenstackVtap.Type.VTAP_RX) && + criterion.dstIpPrefix().contains(ip); + return isTxEdge || isRxEdge; + } + + /** + * Checks the host validation from annotation information. + * + * @param host host to check + * @return true on validate, false otherwise + */ + public static boolean isValidHost(Host host) { + return !host.ipAddresses().isEmpty() && + host.annotations().value(ANNOTATION_NETWORK_ID) != null && + host.annotations().value(ANNOTATION_PORT_ID) != null; + } + + /** + * Checks whether the given IP prefix is contained in the first host rather + * than in the second host. + * + * @param host1 first host instance + * @param host2 second host instance + * @param ipPrefix IP prefix to be looked up + * @return a negative integer, zero, or a positive integer as the + * first argument is less than, equal to, or greater than the + * second. + */ + public static int hostCompareIp(Host host1, Host host2, IpPrefix ipPrefix) { + if ((host1 == null || host1.ipAddresses().stream().noneMatch(ipPrefix::contains)) && + (host2 != null || host2.ipAddresses().stream().anyMatch(ipPrefix::contains))) { + return -1; + } else if ((host1 != null && host1.ipAddresses().stream().anyMatch(ipPrefix::contains)) && + (host2 == null || host2.ipAddresses().stream().noneMatch(ipPrefix::contains))) { + return 1; + } + return 0; + } + + /** + * Obtains flow group key from the given id. + * + * @param groupId flow group identifier + * @return flow group key + */ public static GroupKey getGroupKey(int groupId) { return new DefaultGroupKey((VTAP_GROUP_KEY + Integer.toString(groupId)).getBytes()); } + + /** + * Obtains tunnel interface name from the given openstack vtap network mode. + * + * @param mode vtap network mode + * @return tunnel interface name + */ + public static String getTunnelName(OpenstackVtapNetwork.Mode mode) { + switch (mode) { + case GRE: + return VTAP_TUNNEL_GRE; + case VXLAN: + return VTAP_TUNNEL_VXLAN; + default: + return null; + } + } + + /** + * Obtains tunnel description type from the given openstack vtap network mode. + * + * @param mode vtap network mode + * @return tunnel description type + */ + public static TunnelDescription.Type getTunnelType(OpenstackVtapNetwork.Mode mode) { + return TunnelDescription.Type.valueOf(mode.toString()); + } + + /** + * Print stack trace of given exception. + * + * @param log logger for using print + * @param e exception to print + */ + public static void dumpStackTrace(Logger log, Exception e) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + e.printStackTrace(new PrintStream(outputStream)); + log.error("\n{}", new String(outputStream.toByteArray(), StandardCharsets.UTF_8)); + } + } diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/package-info.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/package-info.java index 905d1c4393..b9a3906594 100644 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/package-info.java +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/package-info.java @@ -15,6 +15,6 @@ */ /** - * Openstack vTap utility package. + * Utility package that used in openstack vtap app. */ package org.onosproject.openstackvtap.util; \ No newline at end of file diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapNetworkCodecRegister.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapNetworkCodecRegister.java new file mode 100644 index 0000000000..7ac689a85c --- /dev/null +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapNetworkCodecRegister.java @@ -0,0 +1,53 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.openstackvtap.web; + +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.onosproject.codec.CodecService; +import org.onosproject.openstackvtap.api.OpenstackVtapNetwork; +import org.onosproject.openstackvtap.codec.OpenstackVtapNetworkCodec; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Implementation of the JSON codec brokering service for openstack vtap network. + */ +@Component(immediate = true) +public class OpenstackVtapNetworkCodecRegister { + + private final org.slf4j.Logger log = getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CodecService codecService; + + @Activate + protected void activate() { + codecService.registerCodec(OpenstackVtapNetwork.class, new OpenstackVtapNetworkCodec()); + + log.info("Started"); + } + + @Deactivate + protected void deactivate() { + codecService.unregisterCodec(OpenstackVtapNetwork.class); + + log.info("Stopped"); + } +} diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapNetworkWebResource.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapNetworkWebResource.java new file mode 100644 index 0000000000..3900bbd09e --- /dev/null +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapNetworkWebResource.java @@ -0,0 +1,165 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.openstackvtap.web; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.onosproject.openstackvtap.api.OpenstackVtapAdminService; +import org.onosproject.openstackvtap.api.OpenstackVtapNetwork; +import org.onosproject.rest.AbstractWebResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.InputStream; + +import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT; +import static org.onlab.util.Tools.readTreeFromStream; + +/** + * Handles REST API call of openstack vtap network. + */ +@Path("vtap-network") +public class OpenstackVtapNetworkWebResource extends AbstractWebResource { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private static final String MESSAGE_NETWORK = "Received openstackVtapNetwork {} request"; + private static final String ERROR_DUPLICATE = "Already has data {}"; + private static final String ERROR_NOTFOUND = "No data to update {}"; + private static final String CREATE = "CREATE"; + private static final String READ = "READ"; + private static final String UPDATE = "UPDATE"; + private static final String DELETE = "DELETE"; + + private static final String NETWORK = "network"; + + private final OpenstackVtapAdminService vtapAdminService = get(OpenstackVtapAdminService.class); + + /** + * Creates a openstack vtap network from the JSON input stream. + * + * @param input openstack vtap network JSON input stream + * @return 200 OK if the JSON is correct + * 400 BAD_REQUEST if the JSON is malformed + * 409 CONFLICT if already the vtap network exists + * @onos.rsModel OpenstackVtapNetwork + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + public Response createNetwork(InputStream input) { + log.info(MESSAGE_NETWORK, CREATE); + + OpenstackVtapNetwork vtapNetwork = readNetworkConfiguration(input); + if (vtapNetwork == null) { + return Response.status(Response.Status.BAD_REQUEST).build(); + } + + if (vtapAdminService.createVtapNetwork(vtapNetwork.mode(), + vtapNetwork.networkId(), vtapNetwork.serverIp()) == null) { + log.warn(ERROR_DUPLICATE, vtapNetwork); + return Response.status(Response.Status.CONFLICT).build(); + } + return Response.ok().build(); + } + + /** + * Updates openstack vtap network from the JSON input stream. + * + * @param input openstack vtap network JSON input stream + * @return 200 OK if the JSON is correct + * 400 BAD_REQUEST if the JSON is malformed + * 404 NOT_FOUND if vtap network is not exists + * @onos.rsModel OpenstackVtapNetwork + */ + @PUT + @Consumes(MediaType.APPLICATION_JSON) + public Response updateNetwork(InputStream input) { + log.info(MESSAGE_NETWORK, UPDATE); + + OpenstackVtapNetwork vtapNetwork = readNetworkConfiguration(input); + if (vtapNetwork == null) { + return Response.status(Response.Status.BAD_REQUEST).build(); + } + + if (vtapAdminService.updateVtapNetwork(vtapNetwork) == null) { + log.warn(ERROR_NOTFOUND, vtapNetwork); + return Response.status(Response.Status.NOT_FOUND).build(); + } + return Response.ok().build(); + } + + /** + * Get openstack vtap network. + * + * @return 200 OK with openstack vtap network + * 404 NOT_FOUND if openstackvtap network is not exists + * @onos.rsModel OpenstackVtapNetwork + */ + @GET + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response getNetwork() { + log.info(MESSAGE_NETWORK, READ); + + OpenstackVtapNetwork vtapNetwork = vtapAdminService.getVtapNetwork(); + if (vtapNetwork == null) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + + JsonNode jsonNode = codec(OpenstackVtapNetwork.class).encode(vtapNetwork, this); + return Response.ok(jsonNode, MediaType.APPLICATION_JSON_TYPE).build(); + } + + /** + * Removes openstack network. + * + * @return 200 OK on removing success + * 404 NOT_FOUND if openstackvtap network is not exists + * @onos.rsModel OpenstackVtapNetwork + */ + @DELETE + @Consumes(MediaType.APPLICATION_JSON) + public Response deleteNetwork() { + log.info(MESSAGE_NETWORK, DELETE); + + if (vtapAdminService.removeVtapNetwork() == null) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + + return Response.ok().build(); + } + + private OpenstackVtapNetwork readNetworkConfiguration(InputStream input) { + try { + JsonNode jsonTree = readTreeFromStream(mapper().enable(INDENT_OUTPUT), input); + ObjectNode vtap = (ObjectNode) jsonTree.get(NETWORK); + return codec(OpenstackVtapNetwork.class).decode(vtap, this); + } catch (Exception e) { + log.error(e.toString()); + return null; + } + } + +} diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapWebApplication.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapWebApplication.java index 5d0caf241f..96aa9a7e67 100644 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapWebApplication.java +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapWebApplication.java @@ -20,11 +20,12 @@ import org.onlab.rest.AbstractWebApplication; import java.util.Set; /** - * Openstack vTap REST APIs web application. + * Openstack vtap REST APIs web application. */ public class OpenstackVtapWebApplication extends AbstractWebApplication { @Override public Set> getClasses() { - return getClasses(OpenstackVtapWebResource.class); + return getClasses(OpenstackVtapWebResource.class, + OpenstackVtapNetworkWebResource.class); } } diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapWebResource.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapWebResource.java index 89468ac098..f4c3084069 100644 --- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapWebResource.java +++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapWebResource.java @@ -36,8 +36,6 @@ public class OpenstackVtapWebResource extends AbstractWebResource { * OpenstackVtapServiceImpl method. * * @return 200 OK - * - * @onos.rsModel dummy */ @GET @Produces(MediaType.APPLICATION_JSON) diff --git a/apps/openstackvtap/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/openstackvtap/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml index c3fa886b62..7fd8ccf2b6 100644 --- a/apps/openstackvtap/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml +++ b/apps/openstackvtap/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -42,7 +42,7 @@ - + diff --git a/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.css b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.css new file mode 100644 index 0000000000..2595410341 --- /dev/null +++ b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.css @@ -0,0 +1,50 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + ONOS GUI -- OpenstackVtap -- CSS file + */ + +/* --- Topo Vtap Panel --- */ +#topo-p-vtap { + padding: 16px; + top: 190px; + left: 20px; +} + +.topo-p td input.number-input { + width: 50px; +} + +.topo-p div.footer tbody { + float: right; +} + +.topo-p div.footer tbody input.button-input { + background-color: #5b99d2; + border: 0; + border-radius: 3px; + color: #fff; + height: 25px; + width: 60px; + padding: 1px 0 0 0; +} + +#vtap-dialog { + padding: 16px; + top: 190px; + left: 20px; +} diff --git a/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.html b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.html new file mode 100644 index 0000000000..f1a1b72bb6 --- /dev/null +++ b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.html @@ -0,0 +1,4 @@ + +
+

This is a hidden view .. just a placeholder to house the javascript

+
diff --git a/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.js b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.js new file mode 100644 index 0000000000..9b20eb0435 --- /dev/null +++ b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.js @@ -0,0 +1,406 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + ONOS GUI -- Openstack Vtap View Module + */ +(function () { + 'use strict'; + + // injected refs + var $log, tov, ovts, fs, wss, tts, api, gs, ps, ds; + + // constants + var pCls = 'topo-p', + idVta = 'topo-p-vtap', + idVDi = 'vtap-dialog', + vtapPanelOpts = { + edge: 'left', + width: 330, // vtap panel width + } + + // panels + var vtap; + + // selected info + var selectedItem; + + // constants + var osvIsActReq = 'openstackVtapIsActivatedRequest', + osvIsActResp = 'openstackVtapIsActivatedResponse', + osvCreateReq = 'openstackVtapCreateRequest', + osvCreateResp = 'openstackVtapCreateResponse'; + + // function to be replaced by the localization bundle function + var topoLion = function (x) { + return '#ttrafov#' + x + '#'; + }; + + var overlay = { + overlayId: 'vtap-overlay', + glyphId: 'm_details', + tooltip: 'Openstack Vtap Overlay', + + activate: function () { + $log.debug("Openstack Vtap topology overlay ACTIVATED"); + }, + deactivate: function () { + destroyVtapPanel(); + $log.debug("Openstack Vtap topology overlay DEACTIVATED"); + }, + + // detail panel button definitions + buttons: { + createHostVtapBtn: { + gid: 'm_details', + tt: 'Create Host-to-Host Vtap', + cb: displayVtap + }, + showRelatedTraffic: { + gid: 'm_relatedIntents', + tt: function () { return topoLion('tr_btn_show_related_traffic'); }, + cb: function (data) { tts.showRelatedIntents(); }, + }, + }, + + hooks: { + multi: function (selectOrder) { + var selectedHost = new Object(); + for (var index in selectOrder) { + if (index == 0) { + selectedHost.src = selectOrder[index]; + } else if (index == 1) { + selectedHost.dst = selectOrder[index]; + } + } + + selectedItem = selectedHost; + + tov.addDetailButton('showRelatedTraffic'); + if(selectOrder.length == 2) { + tov.addDetailButton('createHostVtapBtn'); + } + } + } + }; + + // Panel API + function createTopoPanel(id, opts) { + var p = ps.createPanel(id, opts), + pid = id, + header, body, footer; + p.classed(pCls, true); + + function panel() { + return p; + } + + function hAppend(x) { + return header.append(x); + } + + function bAppend(x) { + return body.append(x); + } + + function fAppend(x) { + return footer.append(x); + } + + function setup() { + p.empty(); + + p.append('div').classed('header', true); + p.append('div').classed('body', true); + p.append('div').classed('footer', true); + + header = p.el().select('.header'); + body = p.el().select('.body'); + footer = p.el().select('.footer'); + } + + function destroy() { + ps.destroyPanel(pid); + } + + return { + panel: panel, + setup: setup, + destroy: destroy, + appendHeader: hAppend, + appendBody: bAppend, + appendFooter: fAppend, + }; + } + + function hideVtapPanel() { + vtap.panel().hide(); + } + + function destroyVtapPanel() { + if(vtap != null) { + vtap.destroy(); + } + vtap = null; + } + + function addInput(tbody, type, id, label, value) { + var tr = tbody.append('tr'), + lab; + if (typeof label === 'string') { + lab = label.replace(/_/g, ' '); + } else { + lab = label; + } + + tr.append('td').attr('class', 'label').text(lab + ' :'); + + if (type == 'radio') { + var td = tr.append('td'); + for(var index in value) { + if(index == 0) { + td.append('input').classed( type + '-input', true) + .attr('type', type) + .attr('value', value[index]) + .attr('name', label) + .attr('id', id) + .attr('checked', 'true'); + } else { + td.append('input').classed( type + '-input', true) + .attr('type', type) + .attr('name', label) + .attr('id', id) + .attr('value', value[index]); + } + td.append('span').text(value[index]); + } + } else { + tr.append('td').append('input').classed(type + '-input', true).attr('type', type) + .attr('id', id).attr('value', value); + } + } + + function addButton(tr, callback, value) { + tr.append('td').append('input').classed('button-input', true).attr('type', 'button') + .attr('value', value).on('click', callback); + } + + function makeButton(callback, text, keyName) { + var cb = fs.isF(callback), + key = fs.isS(keyName); + + function invoke() { + cb && cb(); + } + + return createDiv('vtap-button') + .text(text) + .on('click', invoke); + } + + function createDiv(cls) { + var div = d3.select(document.createElement('div')); + if (cls) { + div.classed(cls, true); + } + return div; + } + + function addInput(tbody, type, id, label, value) { + var tr = tbody.append('tr'), + lab; + if (typeof label === 'string') { + lab = label.replace(/_/g, ' '); + } else { + lab = label; + } + + tr.append('td').attr('class', 'label').text(lab + ' :'); + + if (type == 'radio') { + var td = tr.append('td'); + for(var index in value) { + if(index == 0) { + td.append('input').classed( type + '-input', true) + .attr('type', type) + .attr('value', value[index]) + .attr('name', label) + .attr('id', id) + .attr('checked', 'true'); + } else { + td.append('input').classed( type + '-input', true) + .attr('type', type) + .attr('name', label) + .attr('id', id) + .attr('value', value[index]); + } + td.append('span').text(value[index]); + } + } else { + tr.append('td').append('input').classed(type + '-input', true).attr('type', type) + .attr('id', id).attr('value', value); + } + } + + function addButton(tr, callback, value) { + tr.append('td').append('input').classed('button-input', true).attr('type', 'button') + .attr('value', value).on('click', callback); + } + + function makeButton(callback, text, keyName) { + var cb = fs.isF(callback), + key = fs.isS(keyName); + + function invoke() { + cb && cb(); + } + + return createDiv('vtap-button') + .text(text) + .on('click', invoke); + } + + function createDiv(cls) { + var div = d3.select(document.createElement('div')); + if (cls) { + div.classed(cls, true); + } + return div; + } + + function displayVtap() { + $log.debug("sendEvent openstackVtapIsActivatedRequest: ", selectedItem); + wss.sendEvent(osvIsActReq, selectedItem); + } + + function respIsActCb(selected) { + $log.debug("openstackVtapIsActivatedResponse: ", selected); + if(vtap == null) { + vtap = createTopoPanel(idVta, vtapPanelOpts); + } + vtap.setup(); + + var svg = vtap.appendHeader('div') + .classed('icon', true) + .append('svg'), + title = vtap.appendHeader('h2'), + table = vtap.appendBody('table'), + tbody = table.append('tbody'), + glyphId = 'm_details'; + + gs.addGlyph(svg, glyphId, 30, 0, [1, 1]); + + title.text('Create OpenstackVtap'); + + addInput(tbody, 'text', 'srcIp', 'Source IP', selected.srcName); + addInput(tbody, 'text', 'dstIp', 'Destination IP', selected.dstName); + addInput(tbody, 'radio', 'ipProto', 'Protocol', selected.ipProtoList); + addInput(tbody, 'number', 'srcPort', 'Source Port', ""); + addInput(tbody, 'number', 'dstPort', 'Destination Port', ""); + addInput(tbody, 'radio', 'vtapType', 'Type', selected.vtapTypeList); + + vtap.appendFooter('hr'); + var footTr = vtap.appendFooter('table').append('tbody').append('tr'); + + addButton(footTr, createVtap, 'Create'); + addButton(footTr, hideVtapPanel, 'Cancel'); + + vtap.panel().show(); + } + + function createVtap() { + var vtapInfo = {}; + + vtapInfo.srcIp = document.getElementById('srcIp').value; + vtapInfo.dstIp = document.getElementById('dstIp').value; + vtapInfo.ipProto = document.querySelector('input[name="Protocol"]:checked').value; + vtapInfo.srcPort = document.getElementById('srcPort').value; + vtapInfo.dstPort = document.getElementById('dstPort').value; + vtapInfo.vtapType = document.querySelector('input[name="Type"]:checked').value; + + if(vtapInfo.srcPort == ""){ + vtapInfo.srcPort = 0; + } + if(vtapInfo.dstPort == ""){ + vtapInfo.dstPort = 0; + } + + $log.debug("sendEvent openstackVtapCreateRequest: ", vtapInfo); + wss.sendEvent(osvCreateReq, vtapInfo); + hideVtapPanel(); + + } + + function respCreateCb(result) { + $log.debug("respCreateCb: ", result); + + function dOK() { + if(result.result == "Failed"){ + displayVtap(selectedItem); + } else { + destroyVtapPanel(); + } + } + + function createContent(value) { + var content = ds.createDiv(); + content.append('p').text(value); + return content; + } + + ds.openDialog(idVDi, vtapPanelOpts) + .setTitle("Create Vtap " + result.result) + .addContent(createContent(result.value)) + .addOk(dOK); + } + + // invoke code to register with the overlay service + angular.module('ovOpenstackvtap', []) + .factory('OpenstackVtapTopovService', + ['$log', 'FnService', 'WebSocketService', 'GlyphService', 'PanelService', 'DialogService', + + function (_$log_, _fs_, _wss_, _gs_, _ps_, _ds_) { + $log = _$log_; + fs = _fs_; + wss = _wss_; + gs = _gs_; + ps = _ps_; + ds = _ds_; + + var handlers = {}, + vtapOverlay = 'vtap-overlay', + defaultOverlay = 'traffic'; + + handlers[osvIsActResp] = respIsActCb; + handlers[osvCreateResp] = respCreateCb; + + wss.bindHandlers(handlers); + + return { + displayVtap: displayVtap + }; + }]) + .run(['$log', 'TopoOverlayService', 'OpenstackVtapTopovService', 'TopoTrafficService', + + function (_$log_, _tov_, _ovts_, _tts_) { + $log = _$log_; + tov = _tov_; + ovts = _ovts_; + tts = _tts_; + tov.register(overlay); + }] + ); +}()); diff --git a/apps/openstackvtap/app/src/main/resources/definitions/OpenstackVtapNetwork.json b/apps/openstackvtap/app/src/main/resources/definitions/OpenstackVtapNetwork.json new file mode 100644 index 0000000000..32fa0015de --- /dev/null +++ b/apps/openstackvtap/app/src/main/resources/definitions/OpenstackVtapNetwork.json @@ -0,0 +1,30 @@ +{ + "type": "object", + "required": [ + "network" + ], + "properties": { + "network": { + "type": "object", + "required": [ + "mode", + "networkId", + "serverIp" + ], + "properties": { + "mode": { + "type": "string", + "example": "GRE" + }, + "networkId": { + "type": "int", + "example": 1000 + }, + "serverIp": { + "type": "string", + "example": "10.20.0.1" + } + } + } + } +} diff --git a/apps/openstackvtap/app/src/main/resources/definitions/dummy.json b/apps/openstackvtap/app/src/main/resources/definitions/dummy.json deleted file mode 100644 index 7a73a41bfd..0000000000 --- a/apps/openstackvtap/app/src/main/resources/definitions/dummy.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/apps/openstackvtap/app/src/main/resources/gui/css.html b/apps/openstackvtap/app/src/main/resources/gui/css.html new file mode 100644 index 0000000000..9c1889b341 --- /dev/null +++ b/apps/openstackvtap/app/src/main/resources/gui/css.html @@ -0,0 +1 @@ + diff --git a/apps/openstackvtap/app/src/main/resources/gui/js.html b/apps/openstackvtap/app/src/main/resources/gui/js.html new file mode 100644 index 0000000000..46a4bb4519 --- /dev/null +++ b/apps/openstackvtap/app/src/main/resources/gui/js.html @@ -0,0 +1 @@ + diff --git a/apps/openstackvtap/app/src/main/webapp/WEB-INF/web.xml b/apps/openstackvtap/app/src/main/webapp/WEB-INF/web.xml index 8b445374fa..109fbe7eb7 100644 --- a/apps/openstackvtap/app/src/main/webapp/WEB-INF/web.xml +++ b/apps/openstackvtap/app/src/main/webapp/WEB-INF/web.xml @@ -18,7 +18,7 @@ xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="ONOS" version="2.5"> - Openstack vTap REST API v1.0 + Openstack Vtap REST API v1.0 diff --git a/apps/openstackvtap/app/src/test/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapTest.java b/apps/openstackvtap/app/src/test/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapTest.java index fd9fbf5605..412d1d6a7b 100644 --- a/apps/openstackvtap/app/src/test/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapTest.java +++ b/apps/openstackvtap/app/src/test/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapTest.java @@ -34,8 +34,8 @@ import static org.hamcrest.Matchers.is; */ public class DefaultOpenstackVtapTest { - private static final OpenstackVtapId VTAP_ID_1 = OpenstackVtapId.vTapId(); - private static final OpenstackVtapId VTAP_ID_2 = OpenstackVtapId.vTapId(); + private static final OpenstackVtapId VTAP_ID_1 = OpenstackVtapId.vtapId(); + private static final OpenstackVtapId VTAP_ID_2 = OpenstackVtapId.vtapId(); private static final OpenstackVtap.Type VTAP_TYPE_1 = OpenstackVtap.Type.VTAP_TX; private static final OpenstackVtap.Type VTAP_TYPE_2 = OpenstackVtap.Type.VTAP_RX; @@ -74,10 +74,9 @@ public class DefaultOpenstackVtapTest { vtap1 = builder1 .id(VTAP_ID_1) .type(VTAP_TYPE_1) - .vTapCriterion(CRITERION_1) + .vtapCriterion(CRITERION_1) .txDeviceIds(Sets.newHashSet()) .rxDeviceIds(Sets.newHashSet()) - .annotations() .build(); OpenstackVtap.Builder builder2 = DefaultOpenstackVtap.builder(); @@ -85,10 +84,9 @@ public class DefaultOpenstackVtapTest { sameAsVtap1 = builder2 .id(VTAP_ID_1) .type(VTAP_TYPE_1) - .vTapCriterion(CRITERION_1) + .vtapCriterion(CRITERION_1) .txDeviceIds(Sets.newHashSet()) .rxDeviceIds(Sets.newHashSet()) - .annotations() .build(); OpenstackVtap.Builder builder3 = DefaultOpenstackVtap.builder(); @@ -96,10 +94,9 @@ public class DefaultOpenstackVtapTest { vtap2 = builder3 .id(VTAP_ID_2) .type(VTAP_TYPE_2) - .vTapCriterion(CRITERION_2) + .vtapCriterion(CRITERION_2) .txDeviceIds(Sets.newHashSet()) .rxDeviceIds(Sets.newHashSet()) - .annotations() .build(); } @@ -116,7 +113,7 @@ public class DefaultOpenstackVtapTest { DefaultOpenstackVtap vtap = (DefaultOpenstackVtap) vtap1; assertThat(vtap.id(), is(VTAP_ID_1)); - assertThat(vtap.vTapCriterion(), is(CRITERION_1)); + assertThat(vtap.vtapCriterion(), is(CRITERION_1)); assertThat(vtap.type(), is(VTAP_TYPE_1)); }