diff options
author | Ashlee Young <ashlee@onosfw.com> | 2015-10-09 18:32:44 -0700 |
---|---|---|
committer | Ashlee Young <ashlee@onosfw.com> | 2015-10-09 18:32:44 -0700 |
commit | 6a07d2d622eaa06953f3353e39c080984076e8de (patch) | |
tree | bfb50a2090fce186c2cc545a400c969bf2ea702b | |
parent | e6d71622143ff9b2421a1abbe8434b954b5b1099 (diff) |
Updated master to commit id 6ee8aa3e67ce89908a8c93aa9445c6f71a18f986
Change-Id: I94b055ee2f298daf71e2ec794fd0f2495bd8081f
578 files changed, 39571 insertions, 4451 deletions
diff --git a/framework/src/onos/apps/aaa/pom.xml b/framework/src/onos/apps/aaa/pom.xml index 78fd4a62..b03930a9 100644 --- a/framework/src/onos/apps/aaa/pom.xml +++ b/framework/src/onos/apps/aaa/pom.xml @@ -44,36 +44,41 @@ <dependency> <groupId>org.onosproject</groupId> - <artifactId>onlab-junit</artifactId> - <scope>test</scope> + <artifactId>onos-api</artifactId> + <version>${project.version}</version> </dependency> <dependency> <groupId>org.onosproject</groupId> - <artifactId>onos-api</artifactId> + <artifactId>onos-app-xos-integration</artifactId> <version>${project.version}</version> </dependency> - <dependency> + <dependency> <groupId>org.onosproject</groupId> - <artifactId>onlab-osgi</artifactId> - <version>${project.version}</version> + <artifactId>onlab-junit</artifactId> + <scope>test</scope> </dependency> <dependency> - <groupId>org.apache.felix</groupId> - <artifactId>org.apache.felix.scr.annotations</artifactId> + <groupId>org.onosproject</groupId> + <artifactId>onlab-osgi</artifactId> + <version>${project.version}</version> + <classifier>tests</classifier> + <scope>test</scope> </dependency> <dependency> <groupId>org.onosproject</groupId> - <artifactId>onos-app-xos-integration</artifactId> + <artifactId>onos-api</artifactId> <version>${project.version}</version> + <classifier>tests</classifier> + <scope>test</scope> </dependency> </dependencies> - <build> + <build> <plugins> <plugin> <groupId>org.apache.felix</groupId> diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java index 7e3de885..479ec7ed 100644 --- a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java +++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java @@ -15,13 +15,14 @@ */ package org.onosproject.aaa; -import com.google.common.base.Strings; -import com.google.common.collect.Maps; +import java.net.InetAddress; +import java.nio.ByteBuffer; +import java.util.Optional; +import java.util.Set; + 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.onlab.packet.DeserializationException; @@ -38,14 +39,16 @@ import org.onlab.packet.RADIUSAttribute; import org.onlab.packet.TpPort; import org.onlab.packet.UDP; import org.onlab.packet.VlanId; -import org.onlab.util.Tools; -import org.onosproject.cfg.ComponentConfigService; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; import org.onosproject.net.Host; import org.onosproject.net.PortNumber; +import org.onosproject.net.config.ConfigFactory; +import org.onosproject.net.config.NetworkConfigEvent; +import org.onosproject.net.config.NetworkConfigListener; +import org.onosproject.net.config.NetworkConfigRegistry; import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.TrafficSelector; @@ -58,28 +61,21 @@ import org.onosproject.net.packet.PacketContext; import org.onosproject.net.packet.PacketProcessor; import org.onosproject.net.packet.PacketService; import org.onosproject.xosintegration.VoltTenantService; -import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.util.Collections; -import java.util.Dictionary; -import java.util.Iterator; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - +import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY; import static org.onosproject.net.packet.PacketPriority.CONTROL; import static org.slf4j.LoggerFactory.getLogger; - /** * AAA application for ONOS. */ @Component(immediate = true) public class AAA { + + // for verbose output + private final Logger log = getLogger(getClass()); + // a list of our dependencies : // to register with ONOS as an application - described next @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) @@ -96,8 +92,28 @@ public class AAA { @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected VoltTenantService voltTenantService; - // for verbose output - private final Logger log = getLogger(getClass()); + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected NetworkConfigRegistry netCfgService; + + // Parsed RADIUS server addresses + protected InetAddress radiusIpAddress; + protected String radiusMacAddress; + + // NAS IP address + protected InetAddress nasIpAddress; + protected String nasMacAddress; + + // RADIUS server secret + protected String radiusSecret; + + // ID of RADIUS switch + protected String radiusSwitch; + + // RADIUS port number + protected long radiusPort; + + // RADIUS server TCP port number + protected short radiusServerPort; // our application-specific event handler private ReactivePacketProcessor processor = new ReactivePacketProcessor(); @@ -105,124 +121,80 @@ public class AAA { // our unique identifier private ApplicationId appId; - // Map of state machines. Each state machine is represented by an - // unique identifier on the switch: dpid + port number - Map stateMachineMap = null; - - // RADIUS server IP address - private static final String DEFAULT_RADIUS_IP = "192.168.1.10"; - // NAS IP address - private static final String DEFAULT_NAS_IP = "192.168.1.11"; - // RADIUS uplink port - private static final int DEFAULT_RADIUS_UPLINK = 2; - // RADIUS server shared secret - private static final String DEFAULT_RADIUS_SECRET = "ONOSecret"; - // RADIUS MAC address - private static final String RADIUS_MAC_ADDRESS = "00:00:00:00:01:10"; - // NAS MAC address - private static final String NAS_MAC_ADDRESS = "00:00:00:00:10:01"; - // Radius Switch Id - private static final String DEFAULT_RADIUS_SWITCH = "of:90e2ba82f97791e9"; - // Radius Port Number - private static final String DEFAULT_RADIUS_PORT = "129"; - - @Property(name = "radiusIpAddress", value = DEFAULT_RADIUS_IP, - label = "RADIUS IP Address") - private String radiusIpAddress = DEFAULT_RADIUS_IP; - - @Property(name = "nasIpAddress", value = DEFAULT_NAS_IP, - label = "NAS IP Address") - private String nasIpAddress = DEFAULT_NAS_IP; - - @Property(name = "radiusMacAddress", value = RADIUS_MAC_ADDRESS, - label = "RADIUS MAC Address") - private String radiusMacAddress = RADIUS_MAC_ADDRESS; - - @Property(name = "nasMacAddress", value = NAS_MAC_ADDRESS, - label = "NAS MAC Address") - private String nasMacAddress = NAS_MAC_ADDRESS; - - @Property(name = "radiusSecret", value = DEFAULT_RADIUS_SECRET, - label = "RADIUS shared secret") - private String radiusSecret = DEFAULT_RADIUS_SECRET; - - @Property(name = "radiusSwitchId", value = DEFAULT_RADIUS_SWITCH, - label = "Radius switch") - private String radiusSwitch = DEFAULT_RADIUS_SWITCH; - - @Property(name = "radiusPortNumber", value = DEFAULT_RADIUS_PORT, - label = "Radius port") - private String radiusPort = DEFAULT_RADIUS_PORT; - - // Parsed RADIUS server IP address - protected InetAddress parsedRadiusIpAddress; - - // Parsed NAS IP address - protected InetAddress parsedNasIpAddress; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected ComponentConfigService cfgService; - - @Modified - public void modified(ComponentContext context) { - Dictionary<?, ?> properties = context.getProperties(); - - String s = Tools.get(properties, "radiusIpAddress"); - try { - parsedRadiusIpAddress = InetAddress.getByName(s); - radiusIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_IP : s; - } catch (UnknownHostException e) { - log.error("Invalid RADIUS IP address specification: {}", s); - } - try { - s = Tools.get(properties, "nasIpAddress"); - parsedNasIpAddress = InetAddress.getByName(s); - nasIpAddress = Strings.isNullOrEmpty(s) ? DEFAULT_NAS_IP : s; - } catch (UnknownHostException e) { - log.error("Invalid NAS IP address specification: {}", s); - } + // Configuration properties factory + private final ConfigFactory factory = + new ConfigFactory<ApplicationId, AAAConfig>(APP_SUBJECT_FACTORY, + AAAConfig.class, + "AAA") { + @Override + public AAAConfig createConfig() { + return new AAAConfig(); + } + }; - s = Tools.get(properties, "radiusMacAddress"); - radiusMacAddress = Strings.isNullOrEmpty(s) ? RADIUS_MAC_ADDRESS : s; + // Listener for config changes + private final InternalConfigListener cfgListener = new InternalConfigListener(); - s = Tools.get(properties, "nasMacAddress"); - nasMacAddress = Strings.isNullOrEmpty(s) ? NAS_MAC_ADDRESS : s; + /** + * Builds an EAPOL packet based on the given parameters. + * + * @param dstMac destination MAC address + * @param srcMac source MAC address + * @param vlan vlan identifier + * @param eapolType EAPOL type + * @param eap EAP payload + * @return Ethernet frame + */ + private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac, + short vlan, byte eapolType, EAP eap) { - s = Tools.get(properties, "radiusSecret"); - radiusSecret = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SECRET : s; + Ethernet eth = new Ethernet(); + eth.setDestinationMACAddress(dstMac.toBytes()); + eth.setSourceMACAddress(srcMac.toBytes()); + eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort()); + if (vlan != Ethernet.VLAN_UNTAGGED) { + eth.setVlanID(vlan); + } + //eapol header + EAPOL eapol = new EAPOL(); + eapol.setEapolType(eapolType); + eapol.setPacketLength(eap.getLength()); - s = Tools.get(properties, "radiusSwitchId"); - radiusSwitch = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_SWITCH : s; + //eap part + eapol.setPayload(eap); - s = Tools.get(properties, "radiusPortNumber"); - radiusPort = Strings.isNullOrEmpty(s) ? DEFAULT_RADIUS_PORT : s; + eth.setPayload(eapol); + eth.setPad(true); + return eth; } @Activate - public void activate(ComponentContext context) { - cfgService.registerProperties(getClass()); - modified(context); + public void activate() { + netCfgService.addListener(cfgListener); + netCfgService.registerConfigFactory(factory); + // "org.onosproject.aaa" is the FQDN of our app appId = coreService.registerApplication("org.onosproject.aaa"); + + cfgListener.reconfigureNetwork(netCfgService.getConfig(appId, AAAConfig.class)); + // register our event handler packetService.addProcessor(processor, PacketProcessor.director(2)); requestIntercepts(); - // Instantiate the map of the state machines - stateMachineMap = Collections.synchronizedMap(Maps.newHashMap()); - hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress)); + StateMachine.initializeMaps(); + hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress)); } @Deactivate public void deactivate() { - cfgService.unregisterProperties(getClass(), false); - appId = coreService.registerApplication("org.onosproject.aaa"); withdrawIntercepts(); // de-register and null our handler packetService.removeProcessor(processor); processor = null; + StateMachine.destroyMaps(); } /** @@ -237,8 +209,8 @@ public class AAA { TrafficSelector radSelector = DefaultTrafficSelector.builder() .matchEthType(EthType.EtherType.IPV4.ethType().toShort()) .matchIPProtocol(IPv4.PROTOCOL_UDP) - .matchUdpDst(TpPort.tpPort(1812)) - .matchUdpSrc(TpPort.tpPort(1812)) + .matchUdpDst(TpPort.tpPort(radiusServerPort)) + .matchUdpSrc(TpPort.tpPort(radiusServerPort)) .build(); packetService.requestPackets(radSelector, CONTROL, appId); } @@ -254,45 +226,12 @@ public class AAA { TrafficSelector radSelector = DefaultTrafficSelector.builder() .matchEthType(EthType.EtherType.IPV4.ethType().toShort()) .matchIPProtocol(IPv4.PROTOCOL_UDP) - .matchUdpDst(TpPort.tpPort(1812)) - .matchUdpSrc(TpPort.tpPort(1812)) + .matchUdpDst(TpPort.tpPort(radiusServerPort)) + .matchUdpSrc(TpPort.tpPort(radiusServerPort)) .build(); packetService.cancelPackets(radSelector, CONTROL, appId); } - /** - * Builds an EAPOL packet based on the given parameters. - * - * @param dstMac destination MAC address - * @param srcMac source MAC address - * @param vlan vlan identifier - * @param eapolType EAPOL type - * @param eap EAP payload - * @return Ethernet frame - */ - private static Ethernet buildEapolResponse(MacAddress dstMac, MacAddress srcMac, - short vlan, byte eapolType, EAP eap) { - - Ethernet eth = new Ethernet(); - eth.setDestinationMACAddress(dstMac.toBytes()); - eth.setSourceMACAddress(srcMac.toBytes()); - eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort()); - if (vlan != Ethernet.VLAN_UNTAGGED) { - eth.setVlanID(vlan); - } - //eapol header - EAPOL eapol = new EAPOL(); - eapol.setEapolType(eapolType); - eapol.setPacketLength(eap.getLength()); - - //eap part - eapol.setPayload(eap); - - eth.setPayload(eapol); - eth.setPad(true); - return eth; - } - // our handler defined as a private inner class /** @@ -308,42 +247,66 @@ public class AAA { if (ethPkt == null) { return; } - // identify if incoming packet comes from supplicant (EAP) or RADIUS - switch (EthType.EtherType.lookup(ethPkt.getEtherType())) { - case EAPOL: - handleSupplicantPacket(context.inPacket()); - break; - case IPV4: - IPv4 ipv4Packet = (IPv4) ethPkt.getPayload(); - Ip4Address srcIp = Ip4Address.valueOf(ipv4Packet.getSourceAddress()); - Ip4Address radiusIp4Address = Ip4Address.valueOf(parsedRadiusIpAddress); - if (srcIp.equals(radiusIp4Address) && ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) { - // TODO: check for port as well when it's configurable - UDP udpPacket = (UDP) ipv4Packet.getPayload(); - - byte[] datagram = udpPacket.getPayload().serialize(); - RADIUS radiusPacket; - try { + try { + // identify if incoming packet comes from supplicant (EAP) or RADIUS + switch (EthType.EtherType.lookup(ethPkt.getEtherType())) { + case EAPOL: + handleSupplicantPacket(context.inPacket()); + break; + case IPV4: + IPv4 ipv4Packet = (IPv4) ethPkt.getPayload(); + Ip4Address srcIp = Ip4Address.valueOf(ipv4Packet.getSourceAddress()); + Ip4Address radiusIp4Address = Ip4Address.valueOf(radiusIpAddress); + if (srcIp.equals(radiusIp4Address) && ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) { + // TODO: check for port as well when it's configurable + UDP udpPacket = (UDP) ipv4Packet.getPayload(); + + byte[] datagram = udpPacket.getPayload().serialize(); + RADIUS radiusPacket; radiusPacket = RADIUS.deserializer().deserialize(datagram, 0, datagram.length); - } catch (DeserializationException e) { - log.warn("Unable to deserialize RADIUS packet:", e); - return; + handleRadiusPacket(radiusPacket); } - handleRadiusPacket(radiusPacket); - } - break; - default: - return; + + break; + default: + log.trace("Skipping Ethernet packet type {}", + EthType.EtherType.lookup(ethPkt.getEtherType())); + } + } catch (DeserializationException | StateMachineException e) { + log.warn("Unable to process RADIUS packet:", e); } } + /** + * Creates and initializes common fields of a RADIUS packet. + * + * @param identifier RADIUS identifier + * @param eapPacket EAP packet + * @return RADIUS packet + */ + private RADIUS getRadiusPayload(byte identifier, EAP eapPacket) { + RADIUS radiusPayload = + new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST, + eapPacket.getIdentifier()); + radiusPayload.setIdentifier(identifier); + radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME, + eapPacket.getData()); + + radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP, + AAA.this.nasIpAddress.getAddress()); + + radiusPayload.encapsulateMessage(eapPacket); + radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret); + + return radiusPayload; + } /** * Handles PAE packets (supplicant). * * @param inPacket Ethernet packet coming from the supplicant */ - private void handleSupplicantPacket(InboundPacket inPacket) { + private void handleSupplicantPacket(InboundPacket inPacket) throws StateMachineException { Ethernet ethPkt = inPacket.parsed(); // Where does it come from? MacAddress srcMAC = ethPkt.getSourceMAC(); @@ -351,114 +314,83 @@ public class AAA { DeviceId deviceId = inPacket.receivedFrom().deviceId(); PortNumber portNumber = inPacket.receivedFrom().port(); String sessionId = deviceId.toString() + portNumber.toString(); - StateMachine stateMachine = getStateMachine(sessionId); + StateMachine stateMachine = StateMachine.lookupStateMachineBySessionId(sessionId); + if (stateMachine == null) { + stateMachine = new StateMachine(sessionId, voltTenantService); + } + EAPOL eapol = (EAPOL) ethPkt.getPayload(); switch (eapol.getEapolType()) { case EAPOL.EAPOL_START: - try { - stateMachine.start(); - stateMachine.supplicantConnectpoint = inPacket.receivedFrom(); - - //send an EAP Request/Identify to the supplicant - EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.getIdentifier(), EAP.ATTR_IDENTITY, null); - Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L), - ethPkt.getVlanID(), EAPOL.EAPOL_PACKET, - eapPayload); - stateMachine.supplicantAddress = srcMAC; - stateMachine.vlanId = ethPkt.getVlanID(); - - this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint); - } catch (StateMachineException e) { - e.printStackTrace(); - } + stateMachine.start(); + stateMachine.setSupplicantConnectpoint(inPacket.receivedFrom()); + + //send an EAP Request/Identify to the supplicant + EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null); + Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L), + ethPkt.getVlanID(), EAPOL.EAPOL_PACKET, + eapPayload); + stateMachine.setSupplicantAddress(srcMAC); + stateMachine.setVlanId(ethPkt.getVlanID()); + + this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint()); break; case EAPOL.EAPOL_PACKET: - //check if this is a Response/Identify or a Response/TLS + RADIUS radiusPayload; + // check if this is a Response/Identify or a Response/TLS EAP eapPacket = (EAP) eapol.getPayload(); byte dataType = eapPacket.getDataType(); switch (dataType) { + case EAP.ATTR_IDENTITY: - try { - //request id access to RADIUS - RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST, - eapPacket.getIdentifier()); - radiusPayload.setIdentifier(stateMachine.getIdentifier()); - radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME, - eapPacket.getData()); - stateMachine.setUsername(eapPacket.getData()); - radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP, - AAA.this.parsedNasIpAddress.getAddress()); - - radiusPayload.encapsulateMessage(eapPacket); - - // set Request Authenticator in StateMachine - stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode()); - radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret); - sendRadiusMessage(radiusPayload); + // request id access to RADIUS + stateMachine.setUsername(eapPacket.getData()); - //change the state to "PENDING" - stateMachine.requestAccess(); - } catch (StateMachineException e) { - e.printStackTrace(); - } + radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket); + + // set Request Authenticator in StateMachine + stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode()); + sendRadiusMessage(radiusPayload); + + // change the state to "PENDING" + stateMachine.requestAccess(); break; case EAP.ATTR_MD5: - //verify if the EAP identifier corresponds to the challenge identifier from the client state - //machine. - if (eapPacket.getIdentifier() == stateMachine.getChallengeIdentifier()) { + // verify if the EAP identifier corresponds to the + // challenge identifier from the client state + // machine. + if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) { //send the RADIUS challenge response - RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST, - eapPacket.getIdentifier()); - radiusPayload.setIdentifier(stateMachine.getChallengeIdentifier()); - radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME, - stateMachine.getUsername()); - radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP, - AAA.this.parsedNasIpAddress.getAddress()); - - radiusPayload.encapsulateMessage(eapPacket); + radiusPayload = getRadiusPayload(stateMachine.challengeIdentifier(), eapPacket); radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE, - stateMachine.getChallengeState()); - radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret); + stateMachine.challengeState()); sendRadiusMessage(radiusPayload); } break; case EAP.ATTR_TLS: - try { - //request id access to RADIUS - RADIUS radiusPayload = new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST, - eapPacket.getIdentifier()); - radiusPayload.setIdentifier(stateMachine.getIdentifier()); - radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME, - stateMachine.getUsername()); - radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP, - AAA.this.parsedNasIpAddress.getAddress()); - - radiusPayload.encapsulateMessage(eapPacket); + // request id access to RADIUS + radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket); - radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE, - stateMachine.getChallengeState()); - stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode()); + radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE, + stateMachine.challengeState()); + stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode()); - radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret); + sendRadiusMessage(radiusPayload); + // TODO: this gets called on every fragment, should only be called at TLS-Start + stateMachine.requestAccess(); - sendRadiusMessage(radiusPayload); - // TODO: this gets called on every fragment, should only be called at TLS-Start - stateMachine.requestAccess(); - } catch (StateMachineException e) { - e.printStackTrace(); - } break; default: return; } break; default: - return; + log.trace("Skipping EAPOL message {}", eapol.getEapolType()); } } @@ -467,99 +399,46 @@ public class AAA { * * @param radiusPacket RADIUS packet coming from the RADIUS server. */ - private void handleRadiusPacket(RADIUS radiusPacket) { - StateMachine stateMachine = getStateMachineById(radiusPacket.getIdentifier()); + private void handleRadiusPacket(RADIUS radiusPacket) throws StateMachineException { + StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier()); if (stateMachine == null) { log.error("Invalid session identifier, exiting..."); return; } - EAP eapPayload = new EAP(); - Ethernet eth = null; + EAP eapPayload; + Ethernet eth; switch (radiusPacket.getCode()) { case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE: byte[] challengeState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue(); eapPayload = radiusPacket.decapsulateMessage(); stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState); - eth = buildEapolResponse(stateMachine.supplicantAddress, - MacAddress.valueOf(1L), stateMachine.vlanId, EAPOL.EAPOL_PACKET, + eth = buildEapolResponse(stateMachine.supplicantAddress(), + MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET, eapPayload); - this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint); + this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint()); break; case RADIUS.RADIUS_CODE_ACCESS_ACCEPT: - try { - //send an EAPOL - Success to the supplicant. - byte[] eapMessage = - radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue(); - eapPayload = new EAP(); - eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length); - eth = buildEapolResponse(stateMachine.supplicantAddress, - MacAddress.valueOf(1L), stateMachine.vlanId, EAPOL.EAPOL_PACKET, - eapPayload); - this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint); - - stateMachine.authorizeAccess(); - } catch (StateMachineException e) { - e.printStackTrace(); - } + //send an EAPOL - Success to the supplicant. + byte[] eapMessage = + radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE).getValue(); + eapPayload = new EAP(); + eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length); + eth = buildEapolResponse(stateMachine.supplicantAddress(), + MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET, + eapPayload); + this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint()); + + stateMachine.authorizeAccess(); break; case RADIUS.RADIUS_CODE_ACCESS_REJECT: - try { - stateMachine.denyAccess(); - } catch (StateMachineException e) { - e.printStackTrace(); - } + stateMachine.denyAccess(); break; default: log.warn("Unknown RADIUS message received with code: {}", radiusPacket.getCode()); } } - private StateMachine getStateMachineById(byte identifier) { - StateMachine stateMachine = null; - Set stateMachineSet = stateMachineMap.entrySet(); - - synchronized (stateMachineMap) { - Iterator itr = stateMachineSet.iterator(); - while (itr.hasNext()) { - Map.Entry entry = (Map.Entry) itr.next(); - stateMachine = (StateMachine) entry.getValue(); - if (identifier == stateMachine.getIdentifier()) { - //the state machine has already been created for this session session - stateMachine = (StateMachine) entry.getValue(); - break; - } - } - } - - return stateMachine; - } - - private StateMachine getStateMachine(String sessionId) { - StateMachine stateMachine = null; - Set stateMachineSet = stateMachineMap.entrySet(); - - synchronized (stateMachineMap) { - Iterator itr = stateMachineSet.iterator(); - while (itr.hasNext()) { - - Map.Entry entry = (Map.Entry) itr.next(); - if (sessionId.equals(entry.getKey())) { - //the state machine has already been created for this session session - stateMachine = (StateMachine) entry.getValue(); - break; - } - } - } - - if (stateMachine == null) { - stateMachine = new StateMachine(sessionId, voltTenantService); - stateMachineMap.put(sessionId, stateMachine); - } - - return stateMachine; - } - private void sendRadiusMessage(RADIUS radiusMessage) { Set<Host> hosts = hostService.getHostsByIp(IpAddress.valueOf(radiusIpAddress)); Optional<Host> odst = hosts.stream().filter(h -> h.vlan().toShort() == VlanId.UNTAGGED).findFirst(); @@ -576,12 +455,12 @@ public class AAA { IPv4 ip4Packet = new IPv4(); Ethernet ethPkt = new Ethernet(); radiusMessage.setParent(udp); - udp.setDestinationPort((short) 1812); - udp.setSourcePort((short) 1812); // TODO: make this configurable + udp.setDestinationPort(radiusServerPort); + udp.setSourcePort(radiusServerPort); udp.setPayload(radiusMessage); udp.setParent(ip4Packet); - ip4Packet.setSourceAddress(AAA.this.nasIpAddress); - ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress); + ip4Packet.setSourceAddress(AAA.this.nasIpAddress.getHostAddress()); + ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress.getHostAddress()); ip4Packet.setProtocol(IPv4.PROTOCOL_UDP); ip4Packet.setPayload(udp); ip4Packet.setParent(ethPkt); @@ -591,7 +470,7 @@ public class AAA { ethPkt.setPayload(ip4Packet); TrafficTreatment treatment = DefaultTrafficTreatment.builder() - .setOutput(PortNumber.portNumber(Integer.parseInt(radiusPort))).build(); + .setOutput(PortNumber.portNumber(radiusPort)).build(); OutboundPacket packet = new DefaultOutboundPacket(DeviceId.deviceId(radiusSwitch), treatment, ByteBuffer.wrap(ethPkt.serialize())); packetService.emit(packet); @@ -613,4 +492,59 @@ public class AAA { } + private class InternalConfigListener implements NetworkConfigListener { + + /** + * Reconfigures the DHCP Server according to the configuration parameters passed. + * + * @param cfg configuration object + */ + private void reconfigureNetwork(AAAConfig cfg) { + AAAConfig newCfg; + if (cfg == null) { + newCfg = new AAAConfig(); + } else { + newCfg = cfg; + } + if (newCfg.nasIp() != null) { + nasIpAddress = newCfg.nasIp(); + } + if (newCfg.radiusIp() != null) { + radiusIpAddress = newCfg.radiusIp(); + } + if (newCfg.radiusMac() != null) { + radiusMacAddress = newCfg.radiusMac(); + } + if (newCfg.nasMac() != null) { + nasMacAddress = newCfg.nasMac(); + } + if (newCfg.radiusSecret() != null) { + radiusSecret = newCfg.radiusSecret(); + } + if (newCfg.radiusSwitch() != null) { + radiusSwitch = newCfg.radiusSwitch(); + } + if (newCfg.radiusPort() != -1) { + radiusPort = newCfg.radiusPort(); + } + if (newCfg.radiusServerUDPPort() != -1) { + radiusServerPort = newCfg.radiusServerUDPPort(); + } + } + + @Override + public void event(NetworkConfigEvent event) { + + if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED || + event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) && + event.configClass().equals(AAAConfig.class)) { + + AAAConfig cfg = netCfgService.getConfig(appId, AAAConfig.class); + reconfigureNetwork(cfg); + log.info("Reconfigured"); + } + } + } + + } diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java new file mode 100644 index 00000000..c18d2bf2 --- /dev/null +++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java @@ -0,0 +1,239 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.aaa; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import org.onosproject.core.ApplicationId; +import org.onosproject.net.config.Config; +import org.onosproject.net.config.basics.BasicElementConfig; + +/** + * Network config for the AAA app. + */ +public class AAAConfig extends Config<ApplicationId> { + + private static final String RADIUS_IP = "radiusIp"; + private static final String RADIUS_SERVER_PORT = "1812"; + private static final String RADIUS_MAC = "radiusMac"; + private static final String NAS_IP = "nasIp"; + private static final String NAS_MAC = "nasMac"; + private static final String RADIUS_SECRET = "radiusSecret"; + private static final String RADIUS_SWITCH = "radiusSwitch"; + private static final String RADIUS_PORT = "radiusPort"; + + // RADIUS server IP address + protected static final String DEFAULT_RADIUS_IP = "192.168.1.10"; + + // RADIUS MAC address + protected static final String DEFAULT_RADIUS_MAC = "00:00:00:00:01:10"; + + // NAS IP address + protected static final String DEFAULT_NAS_IP = "192.168.1.11"; + + // NAS MAC address + protected static final String DEFAULT_NAS_MAC = "00:00:00:00:10:01"; + + // RADIUS server shared secret + protected static final String DEFAULT_RADIUS_SECRET = "ONOSecret"; + + // Radius Switch Id + protected static final String DEFAULT_RADIUS_SWITCH = "of:90e2ba82f97791e9"; + + // Radius Port Number + protected static final String DEFAULT_RADIUS_PORT = "129"; + + // Radius Server UDP Port Number + protected static final String DEFAULT_RADIUS_SERVER_PORT = "1812"; + + /** + * Gets the value of a string property, protecting for an empty + * JSON object. + * + * @param name name of the property + * @param defaultValue default value if none has been specified + * @return String value if one os found, default value otherwise + */ + private String getStringProperty(String name, String defaultValue) { + if (object == null) { + return defaultValue; + } + return get(name, defaultValue); + } + + /** + * Returns the NAS ip. + * + * @return ip address or null if not set + */ + public InetAddress nasIp() { + try { + return InetAddress.getByName(getStringProperty(NAS_IP, DEFAULT_NAS_IP)); + } catch (UnknownHostException e) { + return null; + } + } + + /** + * Sets the NAS ip. + * + * @param ip new ip address; null to clear + * @return self + */ + public BasicElementConfig nasIp(String ip) { + return (BasicElementConfig) setOrClear(NAS_IP, ip); + } + + /** + * Returns the RADIUS server ip. + * + * @return ip address or null if not set + */ + public InetAddress radiusIp() { + try { + return InetAddress.getByName(getStringProperty(RADIUS_IP, DEFAULT_RADIUS_IP)); + } catch (UnknownHostException e) { + return null; + } + } + + /** + * Sets the RADIUS server ip. + * + * @param ip new ip address; null to clear + * @return self + */ + public BasicElementConfig radiusIp(String ip) { + return (BasicElementConfig) setOrClear(RADIUS_IP, ip); + } + + /** + * Returns the RADIUS MAC address. + * + * @return mac address or null if not set + */ + public String radiusMac() { + return getStringProperty(RADIUS_MAC, DEFAULT_RADIUS_MAC); + } + + /** + * Sets the RADIUS MAC address. + * + * @param mac new MAC address; null to clear + * @return self + */ + public BasicElementConfig radiusMac(String mac) { + return (BasicElementConfig) setOrClear(RADIUS_MAC, mac); + } + + /** + * Returns the RADIUS MAC address. + * + * @return mac address or null if not set + */ + public String nasMac() { + return getStringProperty(NAS_MAC, DEFAULT_NAS_MAC); + } + + /** + * Sets the RADIUS MAC address. + * + * @param mac new MAC address; null to clear + * @return self + */ + public BasicElementConfig nasMac(String mac) { + return (BasicElementConfig) setOrClear(NAS_MAC, mac); + } + + /** + * Returns the RADIUS secret. + * + * @return radius secret or null if not set + */ + public String radiusSecret() { + return getStringProperty(RADIUS_SECRET, DEFAULT_RADIUS_SECRET); + } + + /** + * Sets the RADIUS secret. + * + * @param secret new MAC address; null to clear + * @return self + */ + public BasicElementConfig radiusSecret(String secret) { + return (BasicElementConfig) setOrClear(RADIUS_SECRET, secret); + } + + /** + * Returns the ID of the RADIUS switch. + * + * @return radius switch ID or null if not set + */ + public String radiusSwitch() { + return getStringProperty(RADIUS_SWITCH, DEFAULT_RADIUS_SWITCH); + } + + /** + * Sets the ID of the RADIUS switch. + * + * @param switchId new RADIUS switch ID; null to clear + * @return self + */ + public BasicElementConfig radiusSwitch(String switchId) { + return (BasicElementConfig) setOrClear(RADIUS_SWITCH, switchId); + } + + /** + * Returns the RADIUS port. + * + * @return radius port or null if not set + */ + public long radiusPort() { + return Integer.parseInt(getStringProperty(RADIUS_PORT, DEFAULT_RADIUS_PORT)); + } + + /** + * Sets the RADIUS port. + * + * @param port new RADIUS port; null to clear + * @return self + */ + public BasicElementConfig radiusPort(long port) { + return (BasicElementConfig) setOrClear(RADIUS_PORT, port); + } + + /** + * Returns the RADIUS server UDP port. + * + * @return radius server UDP port. + */ + public short radiusServerUDPPort() { + return Short.parseShort(getStringProperty(RADIUS_SERVER_PORT, + DEFAULT_RADIUS_SERVER_PORT)); + } + + /** + * Sets the RADIUS port. + * + * @param port new RADIUS UDP port; -1 to clear + * @return self + */ + public BasicElementConfig radiusServerUDPPort(short port) { + return (BasicElementConfig) setOrClear(RADIUS_SERVER_PORT, (long) port); + } + +} diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java index 60959ada..84f69241 100644 --- a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java +++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachine.java @@ -18,13 +18,16 @@ package org.onosproject.aaa; +import java.util.BitSet; +import java.util.Map; + import org.onlab.packet.MacAddress; import org.onosproject.net.ConnectPoint; import org.onosproject.xosintegration.VoltTenant; import org.onosproject.xosintegration.VoltTenantService; import org.slf4j.Logger; -import java.util.BitSet; +import com.google.common.collect.Maps; import static org.slf4j.LoggerFactory.getLogger; @@ -58,9 +61,9 @@ class StateMachine { private byte[] requestAuthenticator; // Supplicant connectivity info - protected ConnectPoint supplicantConnectpoint; - protected MacAddress supplicantAddress; - protected short vlanId; + private ConnectPoint supplicantConnectpoint; + private MacAddress supplicantAddress; + private short vlanId; private String sessionId = null; @@ -109,8 +112,28 @@ class StateMachine { private int currentState = STATE_IDLE; + // Maps of state machines. Each state machine is represented by an + // unique identifier on the switch: dpid + port number + private static Map<String, StateMachine> sessionIdMap; + private static Map<Integer, StateMachine> identifierMap; - /** + public static void initializeMaps() { + sessionIdMap = Maps.newConcurrentMap(); + identifierMap = Maps.newConcurrentMap(); + } + + public static void destroyMaps() { + sessionIdMap = null; + identifierMap = null; + } + + public static StateMachine lookupStateMachineById(byte identifier) { + return identifierMap.get((int) identifier); + } + + public static StateMachine lookupStateMachineBySessionId(String sessionId) { + return sessionIdMap.get(sessionId); + } /** * State Machine Constructor. * * @param sessionId session Id represented by the switch dpid + port number @@ -120,15 +143,69 @@ class StateMachine { log.info("Creating a new state machine for {}", sessionId); this.sessionId = sessionId; this.voltService = voltService; + sessionIdMap.put(sessionId, this); + } + + /** + * Gets the connect point for the supplicant side. + * + * @return supplicant connect point + */ + public ConnectPoint supplicantConnectpoint() { + return supplicantConnectpoint; + } + + /** + * Sets the supplicant side connect point. + * + * @param supplicantConnectpoint supplicant select point. + */ + public void setSupplicantConnectpoint(ConnectPoint supplicantConnectpoint) { + this.supplicantConnectpoint = supplicantConnectpoint; + } + + /** + * Gets the MAC address of the supplicant. + * + * @return supplicant MAC address + */ + public MacAddress supplicantAddress() { + return supplicantAddress; + } + + /** + * Sets the supplicant MAC address. + * + * @param supplicantAddress new supplicant MAC address + */ + public void setSupplicantAddress(MacAddress supplicantAddress) { + this.supplicantAddress = supplicantAddress; + } + + /** + * Gets the client's Vlan ID. + * + * @return client vlan ID + */ + public short vlanId() { + return vlanId; + } + /** + * Sets the client's vlan ID. + * + * @param vlanId new client vlan ID + */ + public void setVlanId(short vlanId) { + this.vlanId = vlanId; } /** - * Get the client id that is requesting for access. + * Gets the client id that is requesting for access. * * @return The client id. */ - public String getSessionId() { + public String sessionId() { return this.sessionId; } @@ -137,7 +214,7 @@ class StateMachine { */ private void createIdentifier() throws StateMachineException { log.debug("Creating Identifier."); - int index = -1; + int index; try { //find the first available spot for identifier assignment @@ -178,11 +255,11 @@ class StateMachine { } /** - * Get the challenge EAP identifier set by the RADIUS. + * Gets the challenge EAP identifier set by the RADIUS. * * @return The challenge EAP identifier. */ - protected byte getChallengeIdentifier() { + protected byte challengeIdentifier() { return this.challengeIdentifier; } @@ -198,11 +275,11 @@ class StateMachine { } /** - * Get the challenge state set by the RADIUS. + * Gets the challenge state set by the RADIUS. * * @return The challenge state. */ - protected byte[] getChallengeState() { + protected byte[] challengeState() { return this.challengeState; } @@ -217,16 +294,16 @@ class StateMachine { /** - * Get the username. + * Gets the username. * * @return The requestAuthenticator. */ - protected byte[] getReqeustAuthenticator() { + protected byte[] requestAuthenticator() { return this.requestAuthenticator; } /** - * Set the username. + * Sets the authenticator. * * @param authenticator The username sent to the RADIUS upon access request. */ @@ -236,11 +313,11 @@ class StateMachine { /** - * Get the username. + * Gets the username. * * @return The username. */ - protected byte[] getUsername() { + protected byte[] username() { return this.username; } @@ -249,7 +326,7 @@ class StateMachine { * * @return The state machine identifier. */ - public byte getIdentifier() { + public byte identifier() { return (byte) this.identifier; } @@ -267,7 +344,7 @@ class StateMachine { /** * Move to the next state. * - * @param msg + * @param msg message */ private void next(int msg) { currentState = transition[currentState][msg]; @@ -280,14 +357,11 @@ class StateMachine { * @throws StateMachineException if authentication protocol is violated */ public void start() throws StateMachineException { - try { - states[currentState].start(); - //move to the next state - next(TRANSITION_START); - createIdentifier(); - } catch (StateMachineInvalidTransitionException e) { - e.printStackTrace(); - } + states[currentState].start(); + //move to the next state + next(TRANSITION_START); + createIdentifier(); + identifierMap.put(identifier, this); } /** @@ -297,13 +371,9 @@ class StateMachine { * @throws StateMachineException if authentication protocol is violated */ public void requestAccess() throws StateMachineException { - try { - states[currentState].requestAccess(); - //move to the next state - next(TRANSITION_REQUEST_ACCESS); - } catch (StateMachineInvalidTransitionException e) { - e.printStackTrace(); - } + states[currentState].requestAccess(); + //move to the next state + next(TRANSITION_REQUEST_ACCESS); } /** @@ -313,27 +383,22 @@ class StateMachine { * @throws StateMachineException if authentication protocol is violated */ public void authorizeAccess() throws StateMachineException { - try { - states[currentState].radiusAccepted(); - //move to the next state - next(TRANSITION_AUTHORIZE_ACCESS); - - if (voltService != null) { - voltService.addTenant( - VoltTenant.builder() - .withHumanReadableName("VCPE-" + this.identifier) - .withId(this.identifier) - .withProviderService(1) - .withServiceSpecificId(String.valueOf(this.identifier)) - .withPort(this.supplicantConnectpoint) - .withVlanId(String.valueOf(this.vlanId)).build()); - } - - deleteIdentifier(); - } catch (StateMachineInvalidTransitionException e) { - e.printStackTrace(); + states[currentState].radiusAccepted(); + //move to the next state + next(TRANSITION_AUTHORIZE_ACCESS); + + if (voltService != null) { + voltService.addTenant( + VoltTenant.builder() + .withHumanReadableName("VCPE-" + this.identifier) + .withId(this.identifier) + .withProviderService(1) + .withServiceSpecificId(String.valueOf(this.identifier)) + .withPort(this.supplicantConnectpoint) + .withVlanId(String.valueOf(this.vlanId)).build()); } + deleteIdentifier(); } /** @@ -343,14 +408,10 @@ class StateMachine { * @throws StateMachineException if authentication protocol is violated */ public void denyAccess() throws StateMachineException { - try { - states[currentState].radiusDenied(); - //move to the next state - next(TRANSITION_DENY_ACCESS); - deleteIdentifier(); - } catch (StateMachineInvalidTransitionException e) { - e.printStackTrace(); - } + states[currentState].radiusDenied(); + //move to the next state + next(TRANSITION_DENY_ACCESS); + deleteIdentifier(); } /** @@ -360,141 +421,117 @@ class StateMachine { * @throws StateMachineException if authentication protocol is violated */ public void logoff() throws StateMachineException { - try { - states[currentState].logoff(); - //move to the next state - next(TRANSITION_LOGOFF); - } catch (StateMachineInvalidTransitionException e) { - e.printStackTrace(); - } + states[currentState].logoff(); + //move to the next state + next(TRANSITION_LOGOFF); } /** - * Get the current state. + * Gets the current state. * * @return The current state. Could be STATE_IDLE, STATE_STARTED, STATE_PENDING, STATE_AUTHORIZED, * STATE_UNAUTHORIZED. */ - public int getState() { + public int state() { return currentState; } - + @Override public String toString() { return ("sessionId: " + this.sessionId) + "\t" + ("identifier: " + this.identifier) + "\t" + ("state: " + this.currentState); } -} -// FIXME: A source file should contain no more than one top-level entity! + abstract class State { + private final Logger log = getLogger(getClass()); -abstract class State { - private final Logger log = getLogger(getClass()); - - private String name = "State"; + private String name = "State"; - public void start() throws StateMachineInvalidTransitionException { - log.warn("START transition from this state is not allowed."); - } + public void start() throws StateMachineInvalidTransitionException { + log.warn("START transition from this state is not allowed."); + } - public void requestAccess() throws StateMachineInvalidTransitionException { - log.warn("REQUEST ACCESS transition from this state is not allowed."); - } + public void requestAccess() throws StateMachineInvalidTransitionException { + log.warn("REQUEST ACCESS transition from this state is not allowed."); + } - public void radiusAccepted() throws StateMachineInvalidTransitionException { - log.warn("AUTHORIZE ACCESS transition from this state is not allowed."); - } + public void radiusAccepted() throws StateMachineInvalidTransitionException { + log.warn("AUTHORIZE ACCESS transition from this state is not allowed."); + } - public void radiusDenied() throws StateMachineInvalidTransitionException { - log.warn("DENY ACCESS transition from this state is not allowed."); - } + public void radiusDenied() throws StateMachineInvalidTransitionException { + log.warn("DENY ACCESS transition from this state is not allowed."); + } - public void logoff() throws StateMachineInvalidTransitionException { - log.warn("LOGOFF transition from this state is not allowed."); + public void logoff() throws StateMachineInvalidTransitionException { + log.warn("LOGOFF transition from this state is not allowed."); + } } -} -/** - * Idle state: supplicant is logged of from the network. - */ -class Idle extends State { - private final Logger log = getLogger(getClass()); - private String name = "IDLE_STATE"; + /** + * Idle state: supplicant is logged of from the network. + */ + class Idle extends State { + private final Logger log = getLogger(getClass()); + private String name = "IDLE_STATE"; - public void start() { - log.info("Moving from IDLE state to STARTED state."); + public void start() { + log.info("Moving from IDLE state to STARTED state."); + } } -} -/** - * Started state: supplicant has entered the network and informed the authenticator. - */ -class Started extends State { - private final Logger log = getLogger(getClass()); - private String name = "STARTED_STATE"; + /** + * Started state: supplicant has entered the network and informed the authenticator. + */ + class Started extends State { + private final Logger log = getLogger(getClass()); + private String name = "STARTED_STATE"; - public void requestAccess() { - log.info("Moving from STARTED state to PENDING state."); + public void requestAccess() { + log.info("Moving from STARTED state to PENDING state."); + } } -} -/** - * Pending state: supplicant has been identified by the authenticator but has not access yet. - */ -class Pending extends State { - private final Logger log = getLogger(getClass()); - private String name = "PENDING_STATE"; + /** + * Pending state: supplicant has been identified by the authenticator but has not access yet. + */ + class Pending extends State { + private final Logger log = getLogger(getClass()); + private String name = "PENDING_STATE"; - public void radiusAccepted() { - log.info("Moving from PENDING state to AUTHORIZED state."); - } + public void radiusAccepted() { + log.info("Moving from PENDING state to AUTHORIZED state."); + } - public void radiusDenied() { - log.info("Moving from PENDING state to UNAUTHORIZED state."); + public void radiusDenied() { + log.info("Moving from PENDING state to UNAUTHORIZED state."); + } } -} -/** - * Authorized state: supplicant port has been accepted, access is granted. - */ -class Authorized extends State { - private final Logger log = getLogger(getClass()); - private String name = "AUTHORIZED_STATE"; + /** + * Authorized state: supplicant port has been accepted, access is granted. + */ + class Authorized extends State { + private final Logger log = getLogger(getClass()); + private String name = "AUTHORIZED_STATE"; - public void logoff() { + public void logoff() { - log.info("Moving from AUTHORIZED state to IDLE state."); + log.info("Moving from AUTHORIZED state to IDLE state."); + } } -} -/** - * Unauthorized state: supplicant port has been rejected, access is denied. - */ -class Unauthorized extends State { - private final Logger log = getLogger(getClass()); - private String name = "UNAUTHORIZED_STATE"; + /** + * Unauthorized state: supplicant port has been rejected, access is denied. + */ + class Unauthorized extends State { + private final Logger log = getLogger(getClass()); + private String name = "UNAUTHORIZED_STATE"; - public void logoff() { - log.info("Moving from UNAUTHORIZED state to IDLE state."); + public void logoff() { + log.info("Moving from UNAUTHORIZED state to IDLE state."); + } } -} -/** - * Exception for the State Machine. - */ -class StateMachineException extends Exception { - public StateMachineException(String message) { - super(message); - - } -} - -/** - * Exception raised when the transition from one state to another is invalid. - */ -class StateMachineInvalidTransitionException extends StateMachineException { - public StateMachineInvalidTransitionException(String message) { - super(message); - } } diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineException.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineException.java new file mode 100644 index 00000000..d4a4da77 --- /dev/null +++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineException.java @@ -0,0 +1,28 @@ +/* + * + * Copyright 2015 AT&T Foundry + * + * 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.aaa; + +/** + * Exception for the State Machine. + */ +class StateMachineException extends Exception { + public StateMachineException(String message) { + super(message); + + } +} diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineInvalidTransitionException.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineInvalidTransitionException.java new file mode 100644 index 00000000..9f41a34f --- /dev/null +++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/StateMachineInvalidTransitionException.java @@ -0,0 +1,27 @@ +/* + * + * Copyright 2015 AT&T Foundry + * + * 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.aaa; + +/** + * Exception raised when the transition from one state to another is invalid. + */ +class StateMachineInvalidTransitionException extends StateMachineException { + public StateMachineInvalidTransitionException(String message) { + super(message); + } +} diff --git a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java index 1b581ab1..75a60336 100644 --- a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java +++ b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java @@ -15,29 +15,494 @@ */ package org.onosproject.aaa; +import java.nio.ByteBuffer; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.onlab.packet.Data; +import org.onlab.packet.DeserializationException; +import org.onlab.packet.EAP; +import org.onlab.packet.EAPOL; +import org.onlab.packet.EthType; +import org.onlab.packet.Ethernet; +import org.onlab.packet.IPv4; +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; +import org.onlab.packet.RADIUS; +import org.onlab.packet.RADIUSAttribute; +import org.onlab.packet.UDP; +import org.onlab.packet.VlanId; +import org.onosproject.core.CoreServiceAdapter; +import org.onosproject.net.Annotations; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.HostLocation; +import org.onosproject.net.config.Config; +import org.onosproject.net.config.NetworkConfigRegistryAdapter; +import org.onosproject.net.host.HostServiceAdapter; +import org.onosproject.net.packet.DefaultInboundPacket; +import org.onosproject.net.packet.DefaultPacketContext; +import org.onosproject.net.packet.InboundPacket; +import org.onosproject.net.packet.OutboundPacket; +import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketServiceAdapter; +import org.onosproject.net.provider.ProviderId; + +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableSet; + +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; +import static org.onosproject.net.NetTestTools.connectPoint; /** * Set of tests of the ONOS application component. */ public class AAATest { + MacAddress clientMac = MacAddress.valueOf("1a:1a:1a:1a:1a:1a"); + MacAddress serverMac = MacAddress.valueOf("2a:2a:2a:2a:2a:2a"); + + PacketProcessor packetProcessor; private AAA aaa; + List<Ethernet> savedPackets = new LinkedList<>(); + + /** + * Saves the given packet onto the saved packets list. + * + * @param eth packet to save + */ + private void savePacket(Ethernet eth) { + savedPackets.add(eth); + } + + /** + * Keeps a reference to the PacketProcessor and saves the OutboundPackets. + */ + private class MockPacketService extends PacketServiceAdapter { + + @Override + public void addProcessor(PacketProcessor processor, int priority) { + packetProcessor = processor; + } + + @Override + public void emit(OutboundPacket packet) { + try { + Ethernet eth = Ethernet.deserializer().deserialize(packet.data().array(), + 0, packet.data().array().length); + savePacket(eth); + } catch (Exception e) { + fail(e.getMessage()); + } + } + } + + /** + * Mocks the DefaultPacketContext. + */ + private final class TestPacketContext extends DefaultPacketContext { + + private TestPacketContext(long time, InboundPacket inPkt, + OutboundPacket outPkt, boolean block) { + super(time, inPkt, outPkt, block); + } + + @Override + public void send() { + // We don't send anything out. + } + } + + /** + * Mocks a host to allow locating the Radius server. + */ + private static final class MockHost implements Host { + @Override + public HostId id() { + return null; + } + + @Override + public MacAddress mac() { + return null; + } + + @Override + public VlanId vlan() { + return VlanId.vlanId(VlanId.UNTAGGED); + } + + @Override + public Set<IpAddress> ipAddresses() { + return null; + } + + @Override + public HostLocation location() { + return null; + } + + @Override + public Annotations annotations() { + return null; + } + + @Override + public ProviderId providerId() { + return null; + } + } + + /** + * Mocks the Host service. + */ + private static final class MockHostService extends HostServiceAdapter { + @Override + public Set<Host> getHostsByIp(IpAddress ip) { + return ImmutableSet.of(new MockHost()); + } + } + + /** + * Mocks the network config registry. + */ + @SuppressWarnings("unchecked") + private static final class TestNetworkConfigRegistry + extends NetworkConfigRegistryAdapter { + @Override + public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) { + return (C) new AAAConfig(); + } + } + + /** + * Sends an Ethernet packet to the process method of the Packet Processor. + * + * @param reply Ethernet packet + */ + private void sendPacket(Ethernet reply) { + final ByteBuffer byteBuffer = ByteBuffer.wrap(reply.serialize()); + InboundPacket inPacket = new DefaultInboundPacket(connectPoint("1", 1), + reply, + byteBuffer); + + PacketContext context = new TestPacketContext(127L, inPacket, null, false); + packetProcessor.process(context); + } + + /** + * Constructs an Ethernet packet containing a EAPOL_START Payload. + * + * @return Ethernet packet + */ + private Ethernet constructSupplicantStartPacket() { + Ethernet eth = new Ethernet(); + eth.setDestinationMACAddress(clientMac.toBytes()); + eth.setSourceMACAddress(serverMac.toBytes()); + eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort()); + eth.setVlanID((short) 2); + + EAP eap = new EAP(EAPOL.EAPOL_START, (byte) 1, EAPOL.EAPOL_START, null); + + //eapol header + EAPOL eapol = new EAPOL(); + eapol.setEapolType(EAPOL.EAPOL_START); + eapol.setPacketLength(eap.getLength()); + + //eap part + eapol.setPayload(eap); + + eth.setPayload(eapol); + eth.setPad(true); + return eth; + } + + /** + * Constructs an Ethernet packet containing identification payload. + * + * @return Ethernet packet + */ + private Ethernet constructSupplicantIdentifyPacket(byte type) { + Ethernet eth = new Ethernet(); + eth.setDestinationMACAddress(clientMac.toBytes()); + eth.setSourceMACAddress(serverMac.toBytes()); + eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort()); + eth.setVlanID((short) 2); + + String username = "user"; + EAP eap = new EAP(EAP.REQUEST, (byte) 1, type, + username.getBytes(Charsets.US_ASCII)); + eap.setIdentifier((byte) 1); + + // eapol header + EAPOL eapol = new EAPOL(); + eapol.setEapolType(EAPOL.EAPOL_PACKET); + eapol.setPacketLength(eap.getLength()); + + // eap part + eapol.setPayload(eap); + + eth.setPayload(eapol); + eth.setPad(true); + return eth; + } + + /** + * Constructs an Ethernet packet containing a RADIUS challenge + * packet. + * + * @param challengeCode code to use in challenge packet + * @param challengeType type to use in challenge packet + * @return Ethernet packet + */ + private Ethernet constructRADIUSCodeAccessChallengePacket(byte challengeCode, byte challengeType) { + Ethernet eth = new Ethernet(); + eth.setDestinationMACAddress(clientMac.toBytes()); + eth.setSourceMACAddress(serverMac.toBytes()); + eth.setEtherType(EthType.EtherType.IPV4.ethType().toShort()); + eth.setVlanID((short) 2); + + IPv4 ipv4 = new IPv4(); + ipv4.setProtocol(IPv4.PROTOCOL_UDP); + ipv4.setSourceAddress(aaa.radiusIpAddress.getHostAddress()); + + String challenge = "1234"; + + EAP eap = new EAP(challengeType, (byte) 1, challengeType, + challenge.getBytes(Charsets.US_ASCII)); + eap.setIdentifier((byte) 1); + + RADIUS radius = new RADIUS(); + radius.setCode(challengeCode); + + radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE, + challenge.getBytes(Charsets.US_ASCII)); + radius.setPayload(eap); + radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE, + eap.serialize()); + + UDP udp = new UDP(); + udp.setPayload(radius); + ipv4.setPayload(udp); + + eth.setPayload(ipv4); + eth.setPad(true); + return eth; + } + + /** + * Sets up the services required by the AAA application. + */ @Before public void setUp() { - + aaa = new AAA(); + aaa.netCfgService = new TestNetworkConfigRegistry(); + aaa.coreService = new CoreServiceAdapter(); + aaa.packetService = new MockPacketService(); + aaa.hostService = new MockHostService(); + aaa.activate(); } + /** + * Tears down the AAA application. + */ @After public void tearDown() { + aaa.deactivate(); + } + + /** + * Extracts the RADIUS packet from a packet sent by the supplicant. + * + * @param supplicantPacket packet sent by the supplicant + * @return RADIUS packet + * @throws DeserializationException if deserialization of the packet contents + * fails. + */ + private RADIUS checkAndFetchRADIUSPacketFromSupplicant(Ethernet supplicantPacket) + throws DeserializationException { + assertThat(supplicantPacket, notNullValue()); + assertThat(supplicantPacket.getVlanID(), is(VlanId.UNTAGGED)); + assertThat(supplicantPacket.getSourceMAC().toString(), is(aaa.nasMacAddress)); + assertThat(supplicantPacket.getDestinationMAC().toString(), is(aaa.radiusMacAddress)); + + assertThat(supplicantPacket.getPayload(), instanceOf(IPv4.class)); + IPv4 ipv4 = (IPv4) supplicantPacket.getPayload(); + assertThat(ipv4, notNullValue()); + assertThat(IpAddress.valueOf(ipv4.getSourceAddress()).toString(), + is(aaa.nasIpAddress.getHostAddress())); + assertThat(IpAddress.valueOf(ipv4.getDestinationAddress()).toString(), + is(aaa.radiusIpAddress.getHostAddress())); + + assertThat(ipv4.getPayload(), instanceOf(UDP.class)); + UDP udp = (UDP) ipv4.getPayload(); + assertThat(udp, notNullValue()); + + assertThat(udp.getPayload(), instanceOf(Data.class)); + Data data = (Data) udp.getPayload(); + RADIUS radius = RADIUS.deserializer() + .deserialize(data.getData(), 0, data.getData().length); + assertThat(radius, notNullValue()); + return radius; } + /** + * Checks the contents of a RADIUS packet being sent to the RADIUS server. + * + * @param radiusPacket packet to check + * @param code expected code + */ + private void checkRadiusPacket(Ethernet radiusPacket, byte code) { + assertThat(radiusPacket.getVlanID(), is((short) 2)); + + // TODO: These address values seem wrong, but are produced by the current AAA implementation + assertThat(radiusPacket.getSourceMAC(), is(MacAddress.valueOf(1L))); + assertThat(radiusPacket.getDestinationMAC(), is(serverMac)); + + assertThat(radiusPacket.getPayload(), instanceOf(EAPOL.class)); + EAPOL eapol = (EAPOL) radiusPacket.getPayload(); + assertThat(eapol, notNullValue()); + + assertThat(eapol.getEapolType(), is(EAPOL.EAPOL_PACKET)); + assertThat(eapol.getPayload(), instanceOf(EAP.class)); + EAP eap = (EAP) eapol.getPayload(); + assertThat(eap, notNullValue()); + assertThat(eap.getCode(), is(code)); + } + + /** + * Fetches the sent packet at the given index. The requested packet + * must be the last packet on the list. + * + * @param index index into sent packets array + * @return packet + */ + private Ethernet fetchPacket(int index) { + assertThat(savedPackets.size(), is(index + 1)); + Ethernet eth = savedPackets.get(index); + assertThat(eth, notNullValue()); + return eth; + } + + /** + * Tests the authentication path through the AAA application. + * + * @throws DeserializationException if packed deserialization fails. + */ @Test - public void basics() { + public void testAuthentication() throws DeserializationException { + + // Our session id will be the device ID ("of:1") with the port ("1") concatenated + String sessionId = "of:11"; + + // (1) Supplicant start up + + Ethernet startPacket = constructSupplicantStartPacket(); + sendPacket(startPacket); + + Ethernet responsePacket = fetchPacket(0); + checkRadiusPacket(responsePacket, EAP.ATTR_IDENTITY); + + // (2) Supplicant identify + + Ethernet identifyPacket = constructSupplicantIdentifyPacket(EAP.ATTR_IDENTITY); + sendPacket(identifyPacket); + Ethernet radiusIdentifyPacket = fetchPacket(1); + + RADIUS radiusAccessRequest = checkAndFetchRADIUSPacketFromSupplicant(radiusIdentifyPacket); + assertThat(radiusAccessRequest, notNullValue()); + assertThat(radiusAccessRequest.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST)); + assertThat(new String(radiusAccessRequest.getAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME).getValue()), + is("user")); + + IpAddress nasIp = + IpAddress.valueOf(IpAddress.Version.INET, + radiusAccessRequest.getAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP) + .getValue()); + assertThat(nasIp.toString(), is(aaa.nasIpAddress.getHostAddress())); + + // State machine should have been created by now + + StateMachine stateMachine = + StateMachine.lookupStateMachineBySessionId(sessionId); + assertThat(stateMachine, notNullValue()); + assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING)); + + // (3) RADIUS MD5 challenge + + Ethernet radiusCodeAccessChallengePacket = + constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_MD5); + sendPacket(radiusCodeAccessChallengePacket); + + Ethernet radiusChallengeMD5Packet = fetchPacket(2); + checkRadiusPacket(radiusChallengeMD5Packet, EAP.ATTR_MD5); + + // (4) Supplicant MD5 response + + Ethernet md5RadiusPacket = constructSupplicantIdentifyPacket(EAP.ATTR_MD5); + sendPacket(md5RadiusPacket); + Ethernet supplicantMD5ResponsePacket = fetchPacket(3); + RADIUS responseMd5RadiusPacket = checkAndFetchRADIUSPacketFromSupplicant(supplicantMD5ResponsePacket); + assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 1)); + assertThat(responseMd5RadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST)); + + // State machine should be in pending state + + assertThat(stateMachine, notNullValue()); + assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING)); + + // (5) RADIUS TLS Challenge + + Ethernet radiusCodeAccessChallengeTLSPacket = + constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_TLS); + sendPacket(radiusCodeAccessChallengeTLSPacket); + + Ethernet radiusChallengeTLSPacket = fetchPacket(4); + checkRadiusPacket(radiusChallengeTLSPacket, EAP.ATTR_TLS); + + // (6) Supplicant TLS response + + Ethernet tlsRadiusPacket = constructSupplicantIdentifyPacket(EAP.ATTR_TLS); + sendPacket(tlsRadiusPacket); + Ethernet supplicantTLSResponsePacket = fetchPacket(5); + RADIUS responseTLSRadiusPacket = checkAndFetchRADIUSPacketFromSupplicant(supplicantTLSResponsePacket); + assertThat(responseTLSRadiusPacket.getIdentifier(), is((byte) 0)); + assertThat(responseTLSRadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST)); + + // (7) RADIUS Success + + Ethernet successPacket = + constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_ACCEPT, EAP.SUCCESS); + sendPacket(successPacket); + Ethernet supplicantSuccessPacket = fetchPacket(6); + + checkRadiusPacket(supplicantSuccessPacket, EAP.SUCCESS); + + // State machine should be in authorized state + + assertThat(stateMachine, notNullValue()); + assertThat(stateMachine.state(), is(StateMachine.STATE_AUTHORIZED)); } + /** + * Tests the default configuration. + */ + @Test + public void testConfig() { + assertThat(aaa.nasIpAddress.getHostAddress(), is(AAAConfig.DEFAULT_NAS_IP)); + assertThat(aaa.nasMacAddress, is(AAAConfig.DEFAULT_NAS_MAC)); + assertThat(aaa.radiusIpAddress.getHostAddress(), is(AAAConfig.DEFAULT_RADIUS_IP)); + assertThat(aaa.radiusMacAddress, is(AAAConfig.DEFAULT_RADIUS_MAC)); + } } diff --git a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java index 2fe44ab9..04837e85 100644 --- a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java +++ b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java @@ -21,6 +21,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import static org.junit.Assert.*; public class StateMachineTest { @@ -30,6 +31,7 @@ public class StateMachineTest { public void setUp() { System.out.println("Set Up."); StateMachine.bitSet.clear(); + StateMachine.initializeMaps(); stateMachine = new StateMachine("session0", null); } @@ -37,6 +39,7 @@ public class StateMachineTest { public void tearDown() { System.out.println("Tear Down."); StateMachine.bitSet.clear(); + StateMachine.destroyMaps(); stateMachine = null; } @@ -46,19 +49,19 @@ public class StateMachineTest { */ public void basic() throws StateMachineException { System.out.println("======= BASIC =======."); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE); stateMachine.start(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED); stateMachine.requestAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING); stateMachine.authorizeAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED); stateMachine.logoff(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE); } @Test @@ -68,19 +71,19 @@ public class StateMachineTest { public void testIdleState() throws StateMachineException { System.out.println("======= IDLE STATE TEST =======."); stateMachine.requestAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE); stateMachine.authorizeAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE); stateMachine.denyAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE); stateMachine.logoff(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE); stateMachine.start(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED); } @Test @@ -92,19 +95,19 @@ public class StateMachineTest { stateMachine.start(); stateMachine.authorizeAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED); stateMachine.denyAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED); stateMachine.logoff(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED); stateMachine.start(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_STARTED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_STARTED); stateMachine.requestAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING); } @Test @@ -118,19 +121,19 @@ public class StateMachineTest { stateMachine.requestAccess(); stateMachine.logoff(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING); stateMachine.start(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING); stateMachine.requestAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING); stateMachine.authorizeAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED); stateMachine.denyAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED); } @Test @@ -144,19 +147,19 @@ public class StateMachineTest { stateMachine.requestAccess(); stateMachine.logoff(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING); stateMachine.start(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING); stateMachine.requestAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_PENDING); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_PENDING); stateMachine.denyAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED); stateMachine.authorizeAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED); } @Test @@ -170,19 +173,19 @@ public class StateMachineTest { stateMachine.authorizeAccess(); stateMachine.start(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED); stateMachine.requestAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED); stateMachine.authorizeAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED); stateMachine.denyAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_AUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_AUTHORIZED); stateMachine.logoff(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE); } @Test @@ -196,27 +199,27 @@ public class StateMachineTest { stateMachine.denyAccess(); stateMachine.start(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED); stateMachine.requestAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED); stateMachine.authorizeAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED); stateMachine.denyAccess(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_UNAUTHORIZED); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_UNAUTHORIZED); stateMachine.logoff(); - Assert.assertEquals(stateMachine.getState(), StateMachine.STATE_IDLE); + Assert.assertEquals(stateMachine.state(), StateMachine.STATE_IDLE); } @Test public void testIdentifierAvailability() throws StateMachineException { System.out.println("======= IDENTIFIER TEST =======."); - byte identifier = stateMachine.getIdentifier(); - System.out.println("State: " + stateMachine.getState()); + byte identifier = stateMachine.identifier(); + System.out.println("State: " + stateMachine.state()); System.out.println("Identifier: " + Byte.toUnsignedInt(identifier)); Assert.assertEquals(-1, identifier); stateMachine.start(); @@ -230,7 +233,7 @@ public class StateMachineTest { for (int i = 1; i <= 255; i++) { StateMachine sm = new StateMachine("session" + i, null); sm.start(); - byte id = sm.getIdentifier(); + byte id = sm.identifier(); Assert.assertEquals(i, Byte.toUnsignedInt(id)); if (i == 3) { sm3 = sm; @@ -244,27 +247,72 @@ public class StateMachineTest { //simulate the state machine for a specific session and logoff so we can free up a spot for an identifier //let's choose identifier 247 then we free up 3 + Assert.assertNotNull(sm247); sm247.requestAccess(); sm247.authorizeAccess(); sm247.logoff(); - sm247 = null; + Assert.assertNotNull(sm3); sm3.requestAccess(); sm3.authorizeAccess(); sm3.logoff(); - sm3 = null; StateMachine otherSM3 = new StateMachine("session3b", null); otherSM3.start(); otherSM3.requestAccess(); - byte id3 = otherSM3.getIdentifier(); + byte id3 = otherSM3.identifier(); Assert.assertEquals(3, Byte.toUnsignedInt(id3)); StateMachine otherSM247 = new StateMachine("session247b", null); otherSM247.start(); otherSM247.requestAccess(); - byte id247 = otherSM247.getIdentifier(); + byte id247 = otherSM247.identifier(); Assert.assertEquals(247, Byte.toUnsignedInt(id247)); + } + @Test + public void testSessionIdLookups() { + String sessionId1 = "session1"; + String sessionId2 = "session2"; + String sessionId3 = "session3"; + + StateMachine machine1ShouldBeNull = + StateMachine.lookupStateMachineBySessionId(sessionId1); + assertNull(machine1ShouldBeNull); + StateMachine machine2ShouldBeNull = + StateMachine.lookupStateMachineBySessionId(sessionId2); + assertNull(machine2ShouldBeNull); + + StateMachine stateMachine1 = new StateMachine(sessionId1, null); + StateMachine stateMachine2 = new StateMachine(sessionId2, null); + + assertEquals(stateMachine1, + StateMachine.lookupStateMachineBySessionId(sessionId1)); + assertEquals(stateMachine2, + StateMachine.lookupStateMachineBySessionId(sessionId2)); + assertNull(StateMachine.lookupStateMachineBySessionId(sessionId3)); + } + + @Test + public void testIdentifierLookups() throws StateMachineException { + String sessionId1 = "session1"; + String sessionId2 = "session2"; + + StateMachine machine1ShouldBeNull = + StateMachine.lookupStateMachineById((byte) 1); + assertNull(machine1ShouldBeNull); + StateMachine machine2ShouldBeNull = + StateMachine.lookupStateMachineById((byte) 2); + assertNull(machine2ShouldBeNull); + + StateMachine stateMachine1 = new StateMachine(sessionId1, null); + stateMachine1.start(); + StateMachine stateMachine2 = new StateMachine(sessionId2, null); + stateMachine2.start(); + + assertEquals(stateMachine1, + StateMachine.lookupStateMachineById(stateMachine1.identifier())); + assertEquals(stateMachine2, + StateMachine.lookupStateMachineById(stateMachine2.identifier())); } } diff --git a/framework/src/onos/apps/acl/pom.xml b/framework/src/onos/apps/acl/pom.xml index 54dee432..454ac7e6 100644 --- a/framework/src/onos/apps/acl/pom.xml +++ b/framework/src/onos/apps/acl/pom.xml @@ -18,7 +18,9 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> @@ -36,10 +38,10 @@ <url>http://onosproject.org</url> <properties> - <onos.version>1.4.0-SNAPSHOT</onos.version> <onos.app.name>org.onosproject.acl</onos.app.name> <onos.app.origin>DLUT</onos.app.origin> - <web.context>/onos/acl</web.context> + + <web.context>/onos/v1/acl</web.context> <api.version>1.0.0</api.version> <api.title>ONOS ACL Application REST API</api.title> <api.description> @@ -64,19 +66,34 @@ <dependency> <groupId>org.onosproject</groupId> <artifactId>onlab-junit</artifactId> - <version>${onos.version}</version> </dependency> <dependency> <groupId>org.onosproject</groupId> <artifactId>onos-rest</artifactId> - <version>${onos.version}</version> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-osgi</artifactId> + <version>${project.version}</version> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-rest</artifactId> + <version>${project.version}</version> + <classifier>tests</classifier> + <scope>test</scope> </dependency> <dependency> <groupId>org.onosproject</groupId> <artifactId>onos-core-serializers</artifactId> - <version>${onos.version}</version> + <version>${project.version}</version> </dependency> <dependency> @@ -100,7 +117,6 @@ <dependency> <groupId>org.onosproject</groupId> <artifactId>onlab-misc</artifactId> - <version>${onos.version}</version> </dependency> </dependencies> diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclRule.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclRule.java new file mode 100644 index 00000000..8c91da4c --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclRule.java @@ -0,0 +1,290 @@ +/* + * Copyright 2015 Open Networking Laboratory + * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China + * Advisers: Keqiu Li, Heng Qi and Haisheng Yu + * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) + * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute. + * + * 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.acl; + +import com.google.common.base.MoreObjects; +import org.onlab.packet.IPv4; +import org.onlab.packet.Ip4Prefix; +import org.onosproject.core.IdGenerator; + +import java.util.Objects; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +/** + * ACL rule class. + */ +public final class AclRule { + + private final RuleId id; + + private final Ip4Prefix srcIp; + private final Ip4Prefix dstIp; + private final byte ipProto; + private final short dstTpPort; + private final Action action; + + private static IdGenerator idGenerator; + + /** + * Enum type for ACL rule's action. + */ + public enum Action { + DENY, ALLOW + } + + /** + * Constructor for serializer. + */ + private AclRule() { + this.id = null; + this.srcIp = null; + this.dstIp = null; + this.ipProto = 0; + this.dstTpPort = 0; + this.action = null; + } + + /** + * Create a new ACL rule. + * + * @param srcIp source IP address + * @param dstIp destination IP address + * @param ipProto IP protocol + * @param dstTpPort destination transport layer port + * @param action ACL rule's action + */ + private AclRule(Ip4Prefix srcIp, Ip4Prefix dstIp, byte ipProto, + short dstTpPort, Action action) { + checkState(idGenerator != null, "Id generator is not bound."); + this.id = RuleId.valueOf(idGenerator.getNewId()); + this.srcIp = srcIp; + this.dstIp = dstIp; + this.ipProto = ipProto; + this.dstTpPort = dstTpPort; + this.action = action; + } + + /** + * Check if the first CIDR address is in (or the same as) the second CIDR address. + */ + private boolean checkCIDRinCIDR(Ip4Prefix cidrAddr1, Ip4Prefix cidrAddr2) { + if (cidrAddr2 == null) { + return true; + } else if (cidrAddr1 == null) { + return false; + } + if (cidrAddr1.prefixLength() < cidrAddr2.prefixLength()) { + return false; + } + int offset = 32 - cidrAddr2.prefixLength(); + + int cidr1Prefix = cidrAddr1.address().toInt(); + int cidr2Prefix = cidrAddr2.address().toInt(); + cidr1Prefix = cidr1Prefix >> offset; + cidr2Prefix = cidr2Prefix >> offset; + cidr1Prefix = cidr1Prefix << offset; + cidr2Prefix = cidr2Prefix << offset; + + return (cidr1Prefix == cidr2Prefix); + } + + /** + * Check if this ACL rule match the given ACL rule. + * + * @param r ACL rule to check against + * @return true if this ACL rule matches the given ACL ruleule. + */ + public boolean checkMatch(AclRule r) { + return (this.dstTpPort == r.dstTpPort || r.dstTpPort == 0) + && (this.ipProto == r.ipProto || r.ipProto == 0) + && (checkCIDRinCIDR(this.srcIp(), r.srcIp())) + && (checkCIDRinCIDR(this.dstIp(), r.dstIp())); + } + + /** + * Returns a new ACL rule builder. + * + * @return ACL rule builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder of an ACL rule. + */ + public static final class Builder { + + private Ip4Prefix srcIp = null; + private Ip4Prefix dstIp = null; + private byte ipProto = 0; + private short dstTpPort = 0; + private Action action = Action.DENY; + + private Builder() { + // Hide constructor + } + + /** + * Sets the source IP address for the ACL rule that will be built. + * + * @param srcIp source IP address to use for built ACL rule + * @return this builder + */ + public Builder srcIp(Ip4Prefix srcIp) { + this.srcIp = srcIp; + return this; + } + + /** + * Sets the destination IP address for the ACL rule that will be built. + * + * @param dstIp destination IP address to use for built ACL rule + * @return this builder + */ + public Builder dstIp(Ip4Prefix dstIp) { + this.dstIp = dstIp; + return this; + } + + /** + * Sets the IP protocol for the ACL rule that will be built. + * + * @param ipProto IP protocol to use for built ACL rule + * @return this builder + */ + public Builder ipProto(byte ipProto) { + this.ipProto = ipProto; + return this; + } + + /** + * Sets the destination transport layer port for the ACL rule that will be built. + * + * @param dstTpPort destination transport layer port to use for built ACL rule + * @return this builder + */ + public Builder dstTpPort(short dstTpPort) { + if ((ipProto == IPv4.PROTOCOL_TCP || ipProto == IPv4.PROTOCOL_UDP)) { + this.dstTpPort = dstTpPort; + } + return this; + } + + /** + * Sets the action for the ACL rule that will be built. + * + * @param action action to use for built ACL rule + * @return this builder + */ + public Builder action(Action action) { + this.action = action; + return this; + } + + /** + * Builds an ACL rule from the accumulated parameters. + * + * @return ACL rule instance + */ + public AclRule build() { + checkState(srcIp != null && dstIp != null, "Either srcIp or dstIp must be assigned."); + checkState(ipProto == 0 || ipProto == IPv4.PROTOCOL_ICMP + || ipProto == IPv4.PROTOCOL_TCP || ipProto == IPv4.PROTOCOL_UDP, + "ipProto must be assigned to TCP, UDP, or ICMP."); + return new AclRule(srcIp, dstIp, ipProto, dstTpPort, action); + } + + } + + /** + * Binds an id generator for unique ACL rule id generation. + * <p> + * Note: A generator cannot be bound if there is already a generator bound. + * + * @param newIdGenerator id generator + */ + public static void bindIdGenerator(IdGenerator newIdGenerator) { + checkState(idGenerator == null, "Id generator is already bound."); + idGenerator = checkNotNull(newIdGenerator); + } + + public RuleId id() { + return id; + } + + public Ip4Prefix srcIp() { + return srcIp; + } + + public Ip4Prefix dstIp() { + return this.dstIp; + } + + public byte ipProto() { + return ipProto; + } + + public short dstTpPort() { + return dstTpPort; + } + + public Action action() { + return action; + } + + @Override + public int hashCode() { + return Objects.hash(action, id.fingerprint(), ipProto, srcIp, dstIp, dstTpPort); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof AclRule) { + AclRule that = (AclRule) obj; + return Objects.equals(id, that.id) && + Objects.equals(srcIp, that.srcIp) && + Objects.equals(dstIp, that.dstIp) && + Objects.equals(ipProto, that.ipProto) && + Objects.equals(dstTpPort, that.dstTpPort) && + Objects.equals(action, that.action); + } + return false; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .omitNullValues() + .add("id", id) + .add("srcIp", srcIp) + .add("dstIp", dstIp) + .add("ipProto", ipProto) + .add("dstTpPort", dstTpPort) + .add("action", action) + .toString(); + } + +} diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclService.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclService.java new file mode 100644 index 00000000..487a6761 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclService.java @@ -0,0 +1,56 @@ +/* + * Copyright 2015 Open Networking Laboratory + * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China + * Advisers: Keqiu Li, Heng Qi and Haisheng Yu + * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) + * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute. + * + * 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.acl; + +import java.util.List; + +/** + * Service interface exported by ACL application. + */ +public interface AclService { + + /** + * Gets a list containing all ACL rules. + * + * @return a list containing all ACL rules + */ + List<AclRule> getAclRules(); + + /** + * Adds a new ACL rule. + * + * @param rule ACL rule + * @return true if successfully added, otherwise false + */ + boolean addAclRule(AclRule rule); + + /** + * Removes an exsiting ACL rule by rule id. + * + * @param ruleId ACL rule identifier + */ + void removeAclRule(RuleId ruleId); + + /** + * Clears ACL and resets all. + */ + void clearAcl(); + +}
\ No newline at end of file diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclStore.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclStore.java new file mode 100644 index 00000000..ff9e25f6 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclStore.java @@ -0,0 +1,146 @@ +/* + * Copyright 2015 Open Networking Laboratory + * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China + * Advisers: Keqiu Li, Heng Qi and Haisheng Yu + * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) + * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute. + * + * 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.acl; + +import org.onosproject.net.DeviceId; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.store.Store; + +import java.util.List; +import java.util.Set; + +/** + * Service interface exported by ACL distributed store. + */ +public interface AclStore extends Store { + + /** + * Gets a list containing all ACL rules. + * + * @return a list containing all ACL rules + */ + List<AclRule> getAclRules(); + + /** + * Adds a new ACL rule. + * + * @param rule new ACL rule + */ + void addAclRule(AclRule rule); + + /** + * Gets an existing ACL rule. + * + * @param ruleId ACL rule id + * @return ACL rule with the given id + */ + AclRule getAclRule(RuleId ruleId); + + /** + * Removes an existing ACL rule by rule id. + * + * @param ruleId ACL rule id + */ + void removeAclRule(RuleId ruleId); + + /** + * Clears ACL and reset all. + */ + void clearAcl(); + + /** + * Gets the current priority for new ACL flow rule by device id. + * + * @param deviceId device id + * @return new ACL flow rule's priority in the given device + */ + int getPriorityByDevice(DeviceId deviceId); + + /** + * Gets a set containing all ACL flow rules belonging to a given ACL rule. + * + * @param ruleId ACL rule id + * @return a set containing all ACL flow rules belonging to the given ACL rule + */ + Set<FlowRule> getFlowByRule(RuleId ruleId); + + /** + * Adds a new mapping from ACL rule to ACL flow rule. + * + * @param ruleId ACL rule id + * @param flowRule ACL flow rule + */ + void addRuleToFlowMapping(RuleId ruleId, FlowRule flowRule); + + /** + * Removes an existing mapping from ACL rule to ACL flow rule. + * + * @param ruleId ACL rule id + */ + void removeRuleToFlowMapping(RuleId ruleId); + + /** + * Gets a list containing all allowing ACL rules matching a given denying ACL rule. + * + * @param denyingRuleId denying ACL rule id + * @return a list containing all allowing ACL rules matching the given denying ACL rule + */ + List<RuleId> getAllowingRuleByDenyingRule(RuleId denyingRuleId); + + /** + * Adds a new mapping from denying ACL rule to allowing ACL rule. + * + * @param denyingRuleId denying ACL rule id + * @param allowingRuleId allowing ACL rule id + */ + void addDenyToAllowMapping(RuleId denyingRuleId, RuleId allowingRuleId); + + /** + * Removes an exsiting mapping from denying ACL rule to allowing ACL rule. + * + * @param denyingRuleId denying ACL rule id + */ + void removeDenyToAllowMapping(RuleId denyingRuleId); + + /** + * Checks if an existing ACL rule already works in a given device. + * + * @param ruleId ACL rule id + * @param deviceId devide id + * @return true if the given ACL rule works in the given device + */ + boolean checkIfRuleWorksInDevice(RuleId ruleId, DeviceId deviceId); + + /** + * Adds a new mapping from ACL rule to device. + * + * @param ruleId ACL rule id + * @param deviceId device id + */ + void addRuleToDeviceMapping(RuleId ruleId, DeviceId deviceId); + + /** + * Removes an existing mapping from ACL rule to device. + * + * @param ruleId ACL rule id + */ + void removeRuleToDeviceMapping(RuleId ruleId); + +} diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java new file mode 100644 index 00000000..e792efba --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/AclWebResource.java @@ -0,0 +1,191 @@ +/* + * Copyright 2015 Open Networking Laboratory + * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China + * Advisers: Keqiu Li, Heng Qi and Haisheng Yu + * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) + * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute. + * + * 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.acl; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.onlab.packet.IPv4; +import org.onlab.packet.Ip4Prefix; +import org.onosproject.rest.AbstractWebResource; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +/** + * Manage ACL rules. + */ +@Path("rules") +public class AclWebResource extends AbstractWebResource { + + /** + * Get all ACL rules. + * Returns array of all ACL rules. + * + * @return 200 OK + */ + @GET + public Response queryAclRule() { + List<AclRule> rules = get(AclService.class).getAclRules(); + ObjectMapper mapper = new ObjectMapper(); + ObjectNode root = mapper.createObjectNode(); + ArrayNode arrayNode = mapper.createArrayNode(); + for (AclRule rule : rules) { + ObjectNode node = mapper.createObjectNode(); + node.put("id", rule.id().toString()); + if (rule.srcIp() != null) { + node.put("srcIp", rule.srcIp().toString()); + } + if (rule.dstIp() != null) { + node.put("dstIp", rule.dstIp().toString()); + } + if (rule.ipProto() != 0) { + switch (rule.ipProto()) { + case IPv4.PROTOCOL_ICMP: + node.put("ipProto", "ICMP"); + break; + case IPv4.PROTOCOL_TCP: + node.put("ipProto", "TCP"); + break; + case IPv4.PROTOCOL_UDP: + node.put("ipProto", "UDP"); + break; + default: + break; + } + } + if (rule.dstTpPort() != 0) { + node.put("dstTpPort", rule.dstTpPort()); + } + node.put("action", rule.action().toString()); + arrayNode.add(node); + } + root.set("aclRules", arrayNode); + return Response.ok(root.toString(), MediaType.APPLICATION_JSON_TYPE).build(); + } + + /** + * Add a new ACL rule. + * + * @param stream JSON data describing the rule + * @return 200 OK + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + public Response addAclRule(InputStream stream) throws URISyntaxException { + AclRule newRule = jsonToRule(stream); + return get(AclService.class).addAclRule(newRule) ? + Response.created(new URI(newRule.id().toString())).build() : + Response.serverError().build(); + } + + /** + * Remove ACL rule. + * + * @param id ACL rule id (in hex string format) + * @return 200 OK + */ + @DELETE + @Path("{id}") + public Response removeAclRule(@PathParam("id") String id) { + RuleId ruleId = new RuleId(Long.parseLong(id.substring(2), 16)); + get(AclService.class).removeAclRule(ruleId); + return Response.ok().build(); + } + + /** + * Remove all ACL rules. + * + * @return 200 OK + */ + @DELETE + public Response clearACL() { + get(AclService.class).clearAcl(); + return Response.ok().build(); + } + + /** + * Turns a JSON string into an ACL rule instance. + */ + private AclRule jsonToRule(InputStream stream) { + JsonNode node; + try { + node = mapper().readTree(stream); + } catch (IOException e) { + throw new IllegalArgumentException("Unable to parse ACL request", e); + } + + AclRule.Builder rule = AclRule.builder(); + + String s = node.path("srcIp").asText(null); + if (s != null) { + rule.srcIp(Ip4Prefix.valueOf(s)); + } + + s = node.path("dstIp").asText(null); + if (s != null) { + rule.dstIp(Ip4Prefix.valueOf(s)); + } + + s = node.path("ipProto").asText(null); + if (s != null) { + if ("TCP".equalsIgnoreCase(s)) { + rule.ipProto(IPv4.PROTOCOL_TCP); + } else if ("UDP".equalsIgnoreCase(s)) { + rule.ipProto(IPv4.PROTOCOL_UDP); + } else if ("ICMP".equalsIgnoreCase(s)) { + rule.ipProto(IPv4.PROTOCOL_ICMP); + } else { + throw new IllegalArgumentException("ipProto must be assigned to TCP, UDP, or ICMP"); + } + } + + int port = node.path("dstTpPort").asInt(0); + if (port > 0) { + rule.dstTpPort((short) port); + } + + s = node.path("action").asText(null); + if (s != null) { + if ("allow".equalsIgnoreCase(s)) { + rule.action(AclRule.Action.ALLOW); + } else if ("deny".equalsIgnoreCase(s)) { + rule.action(AclRule.Action.DENY); + } else { + throw new IllegalArgumentException("action must be ALLOW or DENY"); + } + } + + return rule.build(); + } + +}
\ No newline at end of file diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/RuleId.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/RuleId.java new file mode 100644 index 00000000..468dab5c --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/RuleId.java @@ -0,0 +1,85 @@ +/* + * Copyright 2015 Open Networking Laboratory + * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China + * Advisers: Keqiu Li and Heng Qi + * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) + * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute. + * + * 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.acl; + +/** + * ACL rule identifier suitable as an external key. + * <p>This class is immutable.</p> + */ +public final class RuleId { + private final long value; + + /** + * Creates an ACL rule identifier from the specified long value. + * + * @param value long value + * @return ACL rule identifier + */ + public static RuleId valueOf(long value) { + return new RuleId(value); + } + + /** + * Constructor for serializer. + */ + RuleId() { + this.value = 0; + } + + /** + * Constructs the ID corresponding to a given long value. + * + * @param value the underlying value of this ID + */ + RuleId(long value) { + this.value = value; + } + + /** + * Returns the backing value. + * + * @return the value + */ + public long fingerprint() { + return value; + } + + @Override + public int hashCode() { + return Long.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof RuleId)) { + return false; + } + RuleId that = (RuleId) obj; + return this.value == that.value; + } + + @Override + public String toString() { + return "0x" + Long.toHexString(value); + } +} diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java new file mode 100644 index 00000000..f5c0c204 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/AclManager.java @@ -0,0 +1,338 @@ +/* + * Copyright 2015 Open Networking Laboratory + * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China + * Advisers: Keqiu Li, Heng Qi and Haisheng Yu + * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) + * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute. + * + * 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.acl.impl; + +import org.onlab.packet.Ethernet; +import org.onlab.packet.IPv4; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip4Prefix; +import org.onlab.packet.IpAddress; +import org.onlab.packet.TpPort; +import org.onosproject.acl.AclRule; +import org.onosproject.acl.AclService; +import org.onosproject.acl.AclStore; +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.Service; +import org.onosproject.acl.RuleId; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.core.IdGenerator; +import org.onosproject.mastership.MastershipService; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Host; +import org.onosproject.net.MastershipRole; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.DefaultFlowEntry; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.FlowEntry; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.FlowRuleService; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.instructions.Instructions; +import org.onosproject.net.host.HostEvent; +import org.onosproject.net.host.HostListener; +import org.onosproject.net.host.HostService; +import org.slf4j.Logger; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Implementation of the ACL service. + */ +@Component(immediate = true) +@Service +public class AclManager implements AclService { + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected FlowRuleService flowRuleService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected HostService hostService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected MastershipService mastershipService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected AclStore aclStore; + + private final Logger log = getLogger(getClass()); + private ApplicationId appId; + private final HostListener hostListener = new InternalHostListener(); + private IdGenerator idGenerator; + + /** + * Checks if the given IP address is in the given CIDR address. + */ + private boolean checkIpInCIDR(Ip4Address ip, Ip4Prefix cidr) { + int offset = 32 - cidr.prefixLength(); + int cidrPrefix = cidr.address().toInt(); + int ipIntValue = ip.toInt(); + cidrPrefix = cidrPrefix >> offset; + ipIntValue = ipIntValue >> offset; + cidrPrefix = cidrPrefix << offset; + ipIntValue = ipIntValue << offset; + + return (cidrPrefix == ipIntValue); + } + + private class InternalHostListener implements HostListener { + + /** + * Generate new ACL flow rules for new host following the given ACL rule. + */ + private void processHostAddedEvent(HostEvent event, AclRule rule) { + DeviceId deviceId = event.subject().location().deviceId(); + for (IpAddress address : event.subject().ipAddresses()) { + if ((rule.srcIp() != null) ? + (checkIpInCIDR(address.getIp4Address(), rule.srcIp())) : + (checkIpInCIDR(address.getIp4Address(), rule.dstIp()))) { + if (!aclStore.checkIfRuleWorksInDevice(rule.id(), deviceId)) { + List<RuleId> allowingRuleList = aclStore + .getAllowingRuleByDenyingRule(rule.id()); + if (allowingRuleList != null) { + for (RuleId allowingRuleId : allowingRuleList) { + generateACLFlow(aclStore.getAclRule(allowingRuleId), deviceId); + } + } + generateACLFlow(rule, deviceId); + } + } + } + } + + @Override + public void event(HostEvent event) { + // if a new host appears and an existing rule denies + // its traffic, a new ACL flow rule is generated. + if (event.type() == HostEvent.Type.HOST_ADDED) { + DeviceId deviceId = event.subject().location().deviceId(); + if (mastershipService.getLocalRole(deviceId) == MastershipRole.MASTER) { + for (AclRule rule : aclStore.getAclRules()) { + if (rule.action() != AclRule.Action.ALLOW) { + processHostAddedEvent(event, rule); + } + } + } + } + } + } + + @Activate + public void activate() { + appId = coreService.registerApplication("org.onos.acl"); + hostService.addListener(hostListener); + idGenerator = coreService.getIdGenerator("acl-ids"); + AclRule.bindIdGenerator(idGenerator); + log.info("Started"); + } + + @Deactivate + public void deactivate() { + hostService.removeListener(hostListener); + flowRuleService.removeFlowRulesById(appId); + aclStore.clearAcl(); + log.info("Stopped"); + } + + @Override + public List<AclRule> getAclRules() { + return aclStore.getAclRules(); + } + + /** + * Checks if the new ACL rule matches an existing rule. + * If existing allowing rules matches the new denying rule, store the mappings. + * + * @return true if the new ACL rule matches an existing rule, false otherwise + */ + private boolean matchCheck(AclRule newRule) { + for (AclRule existingRule : aclStore.getAclRules()) { + if (newRule.checkMatch(existingRule)) { + return true; + } + + if (existingRule.action() == AclRule.Action.ALLOW + && newRule.action() == AclRule.Action.DENY) { + if (existingRule.checkMatch(newRule)) { + aclStore.addDenyToAllowMapping(newRule.id(), existingRule.id()); + } + } + } + return false; + } + + @Override + public boolean addAclRule(AclRule rule) { + if (matchCheck(rule)) { + return false; + } + aclStore.addAclRule(rule); + log.info("ACL rule(id:{}) is added.", rule.id()); + if (rule.action() != AclRule.Action.ALLOW) { + enforceRuleAdding(rule); + } + return true; + } + + /** + * Gets a set containing all devices connecting with the hosts + * whose IP address is in the given CIDR IP address. + */ + private Set<DeviceId> getDeviceIdSet(Ip4Prefix cidrAddr) { + Set<DeviceId> deviceIdSet = new HashSet<>(); + final Iterable<Host> hosts = hostService.getHosts(); + + if (cidrAddr.prefixLength() != 32) { + for (Host h : hosts) { + for (IpAddress a : h.ipAddresses()) { + if (checkIpInCIDR(a.getIp4Address(), cidrAddr)) { + deviceIdSet.add(h.location().deviceId()); + } + } + } + } else { + for (Host h : hosts) { + for (IpAddress a : h.ipAddresses()) { + if (checkIpInCIDR(a.getIp4Address(), cidrAddr)) { + deviceIdSet.add(h.location().deviceId()); + return deviceIdSet; + } + } + } + } + return deviceIdSet; + } + + /** + * Enforces denying ACL rule by ACL flow rules. + */ + private void enforceRuleAdding(AclRule rule) { + Set<DeviceId> dpidSet; + if (rule.srcIp() != null) { + dpidSet = getDeviceIdSet(rule.srcIp()); + } else { + dpidSet = getDeviceIdSet(rule.dstIp()); + } + + for (DeviceId deviceId : dpidSet) { + List<RuleId> allowingRuleList = aclStore.getAllowingRuleByDenyingRule(rule.id()); + if (allowingRuleList != null) { + for (RuleId allowingRuleId : allowingRuleList) { + generateACLFlow(aclStore.getAclRule(allowingRuleId), deviceId); + } + } + generateACLFlow(rule, deviceId); + } + } + + /** + * Generates ACL flow rule according to ACL rule + * and install it into related device. + */ + private void generateACLFlow(AclRule rule, DeviceId deviceId) { + if (rule == null || aclStore.checkIfRuleWorksInDevice(rule.id(), deviceId)) { + return; + } + + TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder(); + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); + FlowEntry.Builder flowEntry = DefaultFlowEntry.builder(); + + selectorBuilder.matchEthType(Ethernet.TYPE_IPV4); + if (rule.srcIp() != null) { + selectorBuilder.matchIPSrc(rule.srcIp()); + if (rule.dstIp() != null) { + selectorBuilder.matchIPDst(rule.dstIp()); + } + } else { + selectorBuilder.matchIPDst(rule.dstIp()); + } + if (rule.ipProto() != 0) { + selectorBuilder.matchIPProtocol(Integer.valueOf(rule.ipProto()).byteValue()); + } + if (rule.dstTpPort() != 0) { + switch (rule.ipProto()) { + case IPv4.PROTOCOL_TCP: + selectorBuilder.matchTcpDst(TpPort.tpPort(rule.dstTpPort())); + break; + case IPv4.PROTOCOL_UDP: + selectorBuilder.matchUdpDst(TpPort.tpPort(rule.dstTpPort())); + break; + default: + break; + } + } + if (rule.action() == AclRule.Action.ALLOW) { + treatment.add(Instructions.createOutput(PortNumber.CONTROLLER)); + } + flowEntry.forDevice(deviceId); + flowEntry.withPriority(aclStore.getPriorityByDevice(deviceId)); + flowEntry.withSelector(selectorBuilder.build()); + flowEntry.withTreatment(treatment.build()); + flowEntry.fromApp(appId); + flowEntry.makePermanent(); + // install flow rule + flowRuleService.applyFlowRules(flowEntry.build()); + log.debug("ACL flow rule {} is installed in {}.", flowEntry.build(), deviceId); + aclStore.addRuleToFlowMapping(rule.id(), flowEntry.build()); + aclStore.addRuleToDeviceMapping(rule.id(), deviceId); + } + + @Override + public void removeAclRule(RuleId ruleId) { + aclStore.removeAclRule(ruleId); + log.info("ACL rule(id:{}) is removed.", ruleId); + enforceRuleRemoving(ruleId); + } + + /** + * Enforces removing an existing ACL rule. + */ + private void enforceRuleRemoving(RuleId ruleId) { + Set<FlowRule> flowSet = aclStore.getFlowByRule(ruleId); + if (flowSet != null) { + for (FlowRule flowRule : flowSet) { + flowRuleService.removeFlowRules(flowRule); + log.debug("ACL flow rule {} is removed from {}.", flowRule.toString(), flowRule.deviceId().toString()); + } + } + aclStore.removeRuleToFlowMapping(ruleId); + aclStore.removeRuleToDeviceMapping(ruleId); + aclStore.removeDenyToAllowMapping(ruleId); + } + + @Override + public void clearAcl() { + aclStore.clearAcl(); + flowRuleService.removeFlowRulesById(appId); + log.info("ACL is cleared."); + } + +} diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java new file mode 100644 index 00000000..a5fcfcc7 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/DistributedAclStore.java @@ -0,0 +1,251 @@ +/* + * Copyright 2015 Open Networking Laboratory + * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China + * Advisers: Keqiu Li, Heng Qi and Haisheng Yu + * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) + * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute. + * + * 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.acl.impl; + +import com.google.common.collect.Collections2; +import org.onosproject.acl.AclRule; +import org.onosproject.acl.AclStore; +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.Service; +import org.onlab.util.KryoNamespace; +import org.onosproject.acl.RuleId; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.DeviceId; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.store.AbstractStore; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.ConsistentMap; +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.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Implementation of the ACL store service. + */ +@Component(immediate = true) +@Service +public class DistributedAclStore extends AbstractStore implements AclStore { + + private final Logger log = getLogger(getClass()); + private final int defaultFlowMaxPriority = 30000; + + private ConsistentMap<RuleId, AclRule> ruleSet; + private ConsistentMap<DeviceId, Integer> deviceToPriority; + private ConsistentMap<RuleId, Set<DeviceId>> ruleToDevice; + private ConsistentMap<RuleId, Set<FlowRule>> ruleToFlow; + private ConsistentMap<RuleId, List<RuleId>> denyRuleToAllowRule; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected StorageService storageService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + @Activate + public void activate() { + ApplicationId appId = coreService.getAppId("org.onosproject.acl"); + + KryoNamespace.Builder serializer = KryoNamespace.newBuilder() + .register(KryoNamespaces.API) + .register(AclRule.class) + .register(AclRule.Action.class) + .register(RuleId.class); + + ruleSet = storageService.<RuleId, AclRule>consistentMapBuilder() + .withSerializer(Serializer.using(serializer.build())) + .withName("acl-rule-set") + .withApplicationId(appId) + .withPurgeOnUninstall() + .build(); + + deviceToPriority = storageService.<DeviceId, Integer>consistentMapBuilder() + .withSerializer(Serializer.using(serializer.build())) + .withName("device-to-priority") + .withApplicationId(appId) + .withPurgeOnUninstall() + .build(); + + ruleToFlow = storageService.<RuleId, Set<FlowRule>>consistentMapBuilder() + .withSerializer(Serializer.using(serializer.build())) + .withName("rule-to-flow") + .withApplicationId(appId) + .withPurgeOnUninstall() + .build(); + + denyRuleToAllowRule = storageService.<RuleId, List<RuleId>>consistentMapBuilder() + .withSerializer(Serializer.using(serializer.build())) + .withName("deny-to-allow") + .withApplicationId(appId) + .withPurgeOnUninstall() + .build(); + + ruleToDevice = storageService.<RuleId, Set<DeviceId>>consistentMapBuilder() + .withSerializer(Serializer.using(serializer.build())) + .withName("rule-to-device") + .withApplicationId(appId) + .withPurgeOnUninstall() + .build(); + + log.info("Started"); + } + + @Deactivate + public void deactive() { + log.info("Stopped"); + } + + @Override + public List<AclRule> getAclRules() { + List<AclRule> aclRules = new ArrayList<>(); + aclRules.addAll(Collections2.transform(ruleSet.values(), Versioned::value)); + return aclRules; + } + + @Override + public void addAclRule(AclRule rule) { + ruleSet.putIfAbsent(rule.id(), rule); + } + + @Override + public AclRule getAclRule(RuleId ruleId) { + Versioned<AclRule> rule = ruleSet.get(ruleId); + if (rule != null) { + return rule.value(); + } else { + return null; + } + } + + @Override + public void removeAclRule(RuleId ruleId) { + ruleSet.remove(ruleId); + } + + @Override + public void clearAcl() { + ruleSet.clear(); + deviceToPriority.clear(); + ruleToFlow.clear(); + denyRuleToAllowRule.clear(); + ruleToDevice.clear(); + } + + @Override + public int getPriorityByDevice(DeviceId deviceId) { + return deviceToPriority.compute(deviceId, + (id, priority) -> (priority == null) ? defaultFlowMaxPriority : (priority - 1)) + .value(); + } + + @Override + public Set<FlowRule> getFlowByRule(RuleId ruleId) { + Versioned<Set<FlowRule>> flowRuleSet = ruleToFlow.get(ruleId); + if (flowRuleSet != null) { + return flowRuleSet.value(); + } else { + return null; + } + } + + @Override + public void addRuleToFlowMapping(RuleId ruleId, FlowRule flowRule) { + ruleToFlow.computeIf(ruleId, + flowRuleSet -> (flowRuleSet == null || !flowRuleSet.contains(flowRule)), + (id, flowRuleSet) -> { + Set<FlowRule> newSet = new HashSet<>(); + if (flowRuleSet != null) { + newSet.addAll(flowRuleSet); + } + newSet.add(flowRule); + return newSet; + }); + } + + @Override + public void removeRuleToFlowMapping(RuleId ruleId) { + ruleToFlow.remove(ruleId); + } + + @Override + public List<RuleId> getAllowingRuleByDenyingRule(RuleId denyingRuleId) { + Versioned<List<RuleId>> allowRuleIdSet = denyRuleToAllowRule.get(denyingRuleId); + if (allowRuleIdSet != null) { + return allowRuleIdSet.value(); + } else { + return null; + } + } + + @Override + public void addDenyToAllowMapping(RuleId denyingRuleId, RuleId allowingRuleId) { + denyRuleToAllowRule.computeIf(denyingRuleId, + ruleIdList -> (ruleIdList == null || !ruleIdList.contains(allowingRuleId)), + (id, ruleIdList) -> { + ArrayList<RuleId> newList = new ArrayList<>(); + if (ruleIdList != null) { + newList.addAll(ruleIdList); + } + newList.add(allowingRuleId); + return newList; + }); + } + + @Override + public void removeDenyToAllowMapping(RuleId denyingRuleId) { + denyRuleToAllowRule.remove(denyingRuleId); + } + + @Override + public boolean checkIfRuleWorksInDevice(RuleId ruleId, DeviceId deviceId) { + return ruleToDevice.containsKey(ruleId) && ruleToDevice.get(ruleId).value().contains(deviceId); + } + + @Override + public void addRuleToDeviceMapping(RuleId ruleId, DeviceId deviceId) { + ruleToDevice.computeIf(ruleId, + deviceIdSet -> (deviceIdSet == null || !deviceIdSet.contains(deviceId)), + (id, deviceIdSet) -> { + Set<DeviceId> newSet = new HashSet<>(); + if (deviceIdSet != null) { + newSet.addAll(deviceIdSet); + } + newSet.add(deviceId); + return newSet; + }); + } + + @Override + public void removeRuleToDeviceMapping(RuleId ruleId) { + ruleToDevice.remove(ruleId); + } + +} diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java new file mode 100644 index 00000000..9da9b3b7 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ACL application implementation. + */ +package org.onosproject.acl.impl; diff --git a/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/package-info.java b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/package-info.java new file mode 100644 index 00000000..67f755c6 --- /dev/null +++ b/framework/src/onos/apps/acl/src/main/java/org/onosproject/acl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * ACL application. + */ +package org.onosproject.acl; diff --git a/framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml b/framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml index 2c2d5cf3..fc188b7f 100644 --- a/framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml +++ b/framework/src/onos/apps/acl/src/main/webapp/WEB-INF/web.xml @@ -33,7 +33,7 @@ </init-param> <init-param> <param-name>com.sun.jersey.config.property.classnames</param-name> - <param-value>org.onos.acl.AclWebResource</param-value> + <param-value>org.onosproject.acl.AclWebResource</param-value> </init-param> <load-on-startup>10</load-on-startup> </servlet> diff --git a/framework/src/onos/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java b/framework/src/onos/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java new file mode 100644 index 00000000..c554db6e --- /dev/null +++ b/framework/src/onos/apps/acl/src/test/java/org/onosproject/acl/AclWebResourceTest.java @@ -0,0 +1,142 @@ +/* + * Copyright 2015 Open Networking Laboratory + * Originally created by Pengfei Lu, Network and Cloud Computing Laboratory, Dalian University of Technology, China + * Advisers: Keqiu Li and Heng Qi + * This work is supported by the State Key Program of National Natural Science of China(Grant No. 61432002) + * and Prospective Research Project on Future Networks in Jiangsu Future Networks Innovation Institute. + * + * 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.acl; + +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.test.framework.AppDescriptor; +import com.sun.jersey.test.framework.WebAppDescriptor; +import org.junit.After; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.onlab.osgi.ServiceDirectory; +import org.onlab.osgi.TestServiceDirectory; +import org.onlab.rest.BaseResource; +import org.onosproject.core.IdGenerator; +import org.onosproject.rest.ResourceTest; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +import static org.easymock.EasyMock.*; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertThat; + +/** + * Test class for ACL application REST resource. + */ +public class AclWebResourceTest extends ResourceTest { + + final AclService mockAclService = createMock(AclService.class); + final AclStore mockAclStore = createMock(AclStore.class); + final List<AclRule> rules = new ArrayList<>(); + + @Before + public void setUp() { + expect(mockAclService.getAclRules()).andReturn(rules).anyTimes(); + ServiceDirectory testDirectory = new TestServiceDirectory().add(AclService.class, mockAclService) + .add(AclStore.class, mockAclStore); + BaseResource.setServiceDirectory(testDirectory); + + IdGenerator idGenerator = new MockIdGenerator(); + AclRule.bindIdGenerator(idGenerator); + } + + @After + public void tearDown() { + verify(mockAclService); + } + + /** + * Mock id generator for testing. + */ + private class MockIdGenerator implements IdGenerator { + private AtomicLong nextId = new AtomicLong(0); + + @Override + public long getNewId() { + return nextId.getAndIncrement(); + } + } + + @Override + public AppDescriptor configure() { + return new WebAppDescriptor.Builder("org.onosproject.acl").build(); + } + + @Test + @Ignore("FIXME: This needs to get reworked") + public void addRule() throws IOException { + WebResource.Builder rs = resource().path("rules").header("Content-type", "application/json"); + String response; + String json; + + replay(mockAclService); + + // input a invalid JSON string that contains neither nw_src and nw_dst + json = "{\"ipProto\":\"TCP\",\"dstTpPort\":\"80\"}"; + response = rs.post(String.class, json); + assertThat(response, containsString("Failed! Either srcIp or dstIp must be assigned.")); + + // input a invalid JSON string that doesn't contain CIDR mask bits + json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}"; + response = rs.post(String.class, json); + assertThat(response, containsString("Malformed IPv4 prefix string: 10.0.0.1. " + + "Address must take form \"x.x.x.x/y\"")); + + // input a invalid JSON string that contains a invalid IP address + json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.256/32\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}"; + response = rs.post(String.class, json); + assertThat(response, containsString("Invalid IP address string: 10.0.0.256")); + + // input a invalid JSON string that contains a invalid IP address + json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.01/32\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}"; + response = rs.post(String.class, json); + assertThat(response, containsString("Invalid IP address string: 10.0.01")); + + // input a invalid JSON string that contains a invalid CIDR mask bits + json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1/a\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}"; + response = rs.post(String.class, json); + assertThat(response, containsString("Failed! For input string: \"a\"")); + + // input a invalid JSON string that contains a invalid CIDR mask bits + json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1/33\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}"; + response = rs.post(String.class, json); + assertThat(response, containsString("Invalid prefix length 33. The value must be in the interval [0, 32]")); + + // input a invalid JSON string that contains a invalid ipProto value + json = "{\"ipProto\":\"ARP\",\"srcIp\":\"10.0.0.1/32\",\"dstTpPort\":\"80\",\"action\":\"DENY\"}"; + response = rs.post(String.class, json); + assertThat(response, containsString("ipProto must be assigned to TCP, UDP, or ICMP.")); + + // input a invalid JSON string that contains a invalid dstTpPort value + json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1/32\",\"dstTpPort\":\"a\",\"action\":\"DENY\"}"; + response = rs.post(String.class, json); + assertThat(response, containsString("dstTpPort must be assigned to a numerical value.")); + + // input a invalid JSON string that contains a invalid action value + json = "{\"ipProto\":\"TCP\",\"srcIp\":\"10.0.0.1/32\",\"dstTpPort\":\"80\",\"action\":\"PERMIT\"}"; + response = rs.post(String.class, json); + assertThat(response, containsString("action must be assigned to ALLOW or DENY.")); + } +} diff --git a/framework/src/onos/apps/bgprouter/pom.xml b/framework/src/onos/apps/bgprouter/pom.xml index decdf5c2..6503ee79 100644 --- a/framework/src/onos/apps/bgprouter/pom.xml +++ b/framework/src/onos/apps/bgprouter/pom.xml @@ -24,7 +24,6 @@ <relativePath>../pom.xml</relativePath> </parent> <modelVersion>4.0.0</modelVersion> - <artifactId>onos-app-bgprouter</artifactId> <packaging>bundle</packaging> diff --git a/framework/src/onos/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java b/framework/src/onos/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java index 88265350..6130a2e2 100644 --- a/framework/src/onos/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java +++ b/framework/src/onos/apps/bgprouter/src/main/java/org/onosproject/bgprouter/IcmpHandler.java @@ -101,6 +101,7 @@ public class IcmpHandler { icmpReplyIpv4.setChecksum((short) 0); ICMP icmpReply = new ICMP(); + icmpReply.setPayload(((ICMP) icmpRequestIpv4.getPayload()).getPayload()); icmpReply.setIcmpType(ICMP.TYPE_ECHO_REPLY); icmpReply.setIcmpCode(ICMP.SUBTYPE_ECHO_REPLY); icmpReply.setChecksum((short) 0); diff --git a/framework/src/onos/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java b/framework/src/onos/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java index 1523e9c2..fa916865 100644 --- a/framework/src/onos/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java +++ b/framework/src/onos/apps/cordfabric/src/main/java/org/onosproject/cordfabric/CordFabricManager.java @@ -86,7 +86,7 @@ public class CordFabricManager implements FabricService { private short radiusPort = 1812; - private short ofPort = 6633; + private short ofPort = 6653; private DeviceId fabricDeviceId = DeviceId.deviceId("of:5e3e486e73000187"); diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java index 072254de..cb8acab2 100644 --- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java @@ -21,24 +21,11 @@ import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.Service; -import org.onlab.packet.IpAddress; -import org.onlab.packet.TpPort; import org.onlab.util.KryoNamespace; -import org.onosproject.cluster.ClusterService; -import org.onosproject.cluster.LeadershipEvent; -import org.onosproject.cluster.LeadershipEventListener; -import org.onosproject.cluster.LeadershipService; -import org.onosproject.cluster.NodeId; -import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; -import org.onosproject.mastership.MastershipService; import org.onosproject.net.Device; import org.onosproject.net.DeviceId; import org.onosproject.net.Host; -import org.onosproject.net.config.ConfigFactory; -import org.onosproject.net.config.NetworkConfigRegistry; -import org.onosproject.net.config.NetworkConfigService; -import org.onosproject.net.config.basics.SubjectFactories; import org.onosproject.net.device.DeviceEvent; import org.onosproject.net.device.DeviceListener; import org.onosproject.net.device.DeviceService; @@ -57,11 +44,15 @@ import java.util.concurrent.Executors; import java.util.stream.Collectors; import static org.onlab.util.Tools.groupedThreads; +import static org.onosproject.cordvtn.OvsdbNode.State; import static org.onosproject.cordvtn.OvsdbNode.State.INIT; +import static org.onosproject.cordvtn.OvsdbNode.State.DISCONNECT; +import static org.onosproject.net.Device.Type.SWITCH; import static org.slf4j.LoggerFactory.getLogger; /** - * CORD VTN Application that provisions overlay virtual tenant networks. + * Provides initial setup or cleanup for provisioning virtual tenant networks + * on ovsdb, integration bridge and vm when they are added or deleted. */ @Component(immediate = true) @Service @@ -69,6 +60,11 @@ public class CordVtn implements CordVtnService { protected final Logger log = getLogger(getClass()); + private static final int NUM_THREADS = 1; + private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder() + .register(KryoNamespaces.API) + .register(OvsdbNode.class); + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected CoreService coreService; @@ -79,112 +75,81 @@ public class CordVtn implements CordVtnService { protected LogicalClockService clockService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected ClusterService clusterService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected LeadershipService leadershipService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected NetworkConfigService configService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected NetworkConfigRegistry configRegistry; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected DeviceService deviceService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected HostService hostService; - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected MastershipService mastershipService; - - private static final int DEFAULT_NUM_THREADS = 1; - private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder() - .register(KryoNamespaces.API) - .register(OvsdbNode.class); + private final ExecutorService eventExecutor = Executors + .newFixedThreadPool(NUM_THREADS, groupedThreads("onos/cordvtn", "event-handler")); - private final ExecutorService eventExecutor = Executors.newFixedThreadPool( - DEFAULT_NUM_THREADS, groupedThreads("onos/cordvtn", "event-handler")); - - private final LeadershipEventListener leadershipListener = new InternalLeadershipListener(); private final DeviceListener deviceListener = new InternalDeviceListener(); private final HostListener hostListener = new InternalHostListener(); - private final NodeHandler nodeHandler = new NodeHandler(); + + private final OvsdbHandler ovsdbHandler = new OvsdbHandler(); private final BridgeHandler bridgeHandler = new BridgeHandler(); - private final VirtualMachineHandler vmHandler = new VirtualMachineHandler(); - - private final ConfigFactory configFactory = - new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, CordVtnConfig.class, "cordvtn") { - @Override - public CordVtnConfig createConfig() { - return new CordVtnConfig(); - } - }; - - private ApplicationId appId; - private NodeId local; + private final VmHandler vmHandler = new VmHandler(); + private EventuallyConsistentMap<DeviceId, OvsdbNode> nodeStore; - private NodeConnectionManager nodeConnectionManager; @Activate protected void activate() { - appId = coreService.registerApplication("org.onosproject.cordvtn"); - - local = clusterService.getLocalNode().id(); + coreService.registerApplication("org.onosproject.cordvtn"); nodeStore = storageService.<DeviceId, OvsdbNode>eventuallyConsistentMapBuilder() .withName("cordvtn-nodestore") .withSerializer(NODE_SERIALIZER) .withTimestampProvider((k, v) -> clockService.getTimestamp()) .build(); - configRegistry.registerConfigFactory(configFactory); deviceService.addListener(deviceListener); hostService.addListener(hostListener); - leadershipService.addListener(leadershipListener); - leadershipService.runForLeadership(appId.name()); - nodeConnectionManager = new NodeConnectionManager(appId, local, nodeStore, - mastershipService, leadershipService); - nodeConnectionManager.start(); + log.info("Started"); } @Deactivate protected void deactivate() { - nodeConnectionManager.stop(); - leadershipService.removeListener(leadershipListener); - leadershipService.withdraw(appId.name()); deviceService.removeListener(deviceListener); hostService.removeListener(hostListener); + eventExecutor.shutdown(); nodeStore.destroy(); - configRegistry.unregisterConfigFactory(configFactory); + log.info("Stopped"); } @Override - public void addNode(String hostname, IpAddress ip, TpPort port) { - DefaultOvsdbNode node = new DefaultOvsdbNode(hostname, ip, port, DeviceId.NONE, INIT); - - if (nodeStore.containsKey(node.deviceId())) { - log.warn("Node {} with ovsdb-server {}:{} already exists", hostname, ip, port); + public void addNode(OvsdbNode ovsdbNode) { + if (nodeStore.containsKey(ovsdbNode.deviceId())) { + log.warn("Node {} already exists", ovsdbNode.host()); return; } - nodeStore.put(node.deviceId(), node); - log.info("New node {} with ovsdb-server {}:{} has been added", hostname, ip, port); + nodeStore.put(ovsdbNode.deviceId(), ovsdbNode); + if (ovsdbNode.state() != INIT) { + updateNode(ovsdbNode, INIT); + } } @Override - public void deleteNode(IpAddress ip, TpPort port) { - DeviceId deviceId = DeviceId.deviceId("ovsdb:" + ip + ":" + port); - OvsdbNode node = nodeStore.get(deviceId); + public void deleteNode(OvsdbNode ovsdbNode) { + if (!nodeStore.containsKey(ovsdbNode.deviceId())) { + log.warn("Node {} does not exist", ovsdbNode.host()); + return; + } + updateNode(ovsdbNode, DISCONNECT); + } - if (node == null) { - log.warn("Node with ovsdb-server on {}:{} does not exist", ip, port); + @Override + public void updateNode(OvsdbNode ovsdbNode, State state) { + if (!nodeStore.containsKey(ovsdbNode.deviceId())) { + log.warn("Node {} does not exist", ovsdbNode.host()); return; } - nodeConnectionManager.disconnectNode(node); - nodeStore.remove(node.deviceId()); + DefaultOvsdbNode updatedNode = new DefaultOvsdbNode(ovsdbNode.host(), + ovsdbNode.ip(), + ovsdbNode.port(), + state); + nodeStore.put(ovsdbNode.deviceId(), updatedNode); } @Override @@ -193,58 +158,33 @@ public class CordVtn implements CordVtnService { } @Override + public OvsdbNode getNode(DeviceId deviceId) { + return nodeStore.get(deviceId); + } + + @Override public List<OvsdbNode> getNodes() { return nodeStore.values() .stream() .collect(Collectors.toList()); } - private void initialSetup() { - // Read ovsdb nodes from network config - CordVtnConfig config = configService.getConfig(appId, CordVtnConfig.class); - if (config == null) { - log.warn("No configuration found"); - return; - } - config.ovsdbNodes().forEach( - node -> addNode(node.hostname(), node.ip(), node.port())); - } - - private synchronized void processLeadershipChange(NodeId leader) { - // Only the leader performs the initial setup - if (leader == null || !leader.equals(local)) { - return; - } - initialSetup(); - } - - private class InternalLeadershipListener implements LeadershipEventListener { - - @Override - public void event(LeadershipEvent event) { - if (event.subject().topic().equals(appId.name())) { - processLeadershipChange(event.subject().leader()); - } - } - } - private class InternalDeviceListener implements DeviceListener { @Override public void event(DeviceEvent event) { Device device = event.subject(); - ConnectionHandler handler = - (device.type() == Device.Type.CONTROLLER ? nodeHandler : bridgeHandler); + ConnectionHandler handler = (device.type() == SWITCH ? bridgeHandler : ovsdbHandler); switch (event.type()) { - case DEVICE_ADDED: - eventExecutor.submit(() -> handler.connected(device)); - break; - case DEVICE_AVAILABILITY_CHANGED: - eventExecutor.submit(() -> handler.disconnected(device)); - break; - default: - break; + case DEVICE_ADDED: + eventExecutor.submit(() -> handler.connected(device)); + break; + case DEVICE_AVAILABILITY_CHANGED: + eventExecutor.submit(() -> handler.disconnected(device)); + break; + default: + break; } } } @@ -268,7 +208,7 @@ public class CordVtn implements CordVtnService { } } - private class NodeHandler implements ConnectionHandler<Device> { + private class OvsdbHandler implements ConnectionHandler<Device> { @Override public void connected(Device device) { @@ -296,7 +236,7 @@ public class CordVtn implements CordVtnService { } } - private class VirtualMachineHandler implements ConnectionHandler<Host> { + private class VmHandler implements ConnectionHandler<Host> { @Override public void connected(Host host) { diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java index c2c37aba..fdaf752a 100644 --- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfig.java @@ -27,12 +27,12 @@ import java.util.Set; import static com.google.common.base.Preconditions.checkNotNull; /** - * Configuration object for CORD VTN service. + * Configuration object for CordVtn service. */ public class CordVtnConfig extends Config<ApplicationId> { public static final String OVSDB_NODES = "ovsdbNodes"; - public static final String HOSTNAME = "hostname"; + public static final String HOST = "host"; public static final String IP = "ip"; public static final String PORT = "port"; @@ -49,7 +49,7 @@ public class CordVtnConfig extends Config<ApplicationId> { return null; } nodes.forEach(jsonNode -> ovsdbNodes.add(new OvsdbNodeConfig( - jsonNode.path(HOSTNAME).asText(), + jsonNode.path(HOST).asText(), IpAddress.valueOf(jsonNode.path(IP).asText()), TpPort.tpPort(jsonNode.path(PORT).asInt())))); @@ -57,27 +57,27 @@ public class CordVtnConfig extends Config<ApplicationId> { } /** - * Configuration for an OVSDB node. + * Configuration for an ovsdb node. */ public static class OvsdbNodeConfig { - private final String hostname; + private final String host; private final IpAddress ip; private final TpPort port; - public OvsdbNodeConfig(String hostname, IpAddress ip, TpPort port) { - this.hostname = checkNotNull(hostname); + public OvsdbNodeConfig(String host, IpAddress ip, TpPort port) { + this.host = checkNotNull(host); this.ip = checkNotNull(ip); this.port = checkNotNull(port); } /** - * Returns hostname of the node. + * Returns host information of the node. * - * @return hostname + * @return host */ - public String hostname() { - return this.hostname; + public String host() { + return this.host; } /** diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java new file mode 100644 index 00000000..043b3760 --- /dev/null +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java @@ -0,0 +1,144 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.cordvtn; + +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.cluster.ClusterService; +import org.onosproject.cluster.LeadershipEvent; +import org.onosproject.cluster.LeadershipEventListener; +import org.onosproject.cluster.LeadershipService; +import org.onosproject.cluster.NodeId; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.config.ConfigFactory; +import org.onosproject.net.config.NetworkConfigEvent; +import org.onosproject.net.config.NetworkConfigListener; +import org.onosproject.net.config.NetworkConfigRegistry; +import org.onosproject.net.config.NetworkConfigService; +import org.onosproject.net.config.basics.SubjectFactories; +import org.slf4j.Logger; + +import static org.onosproject.cordvtn.OvsdbNode.State.INIT; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Reads node information from the network config file and handles the config + * update events. + * Only a leader controller performs the node addition or deletion. + */ +@Component(immediate = true) +public class CordVtnConfigManager { + + protected final Logger log = getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected NetworkConfigRegistry configRegistry; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected NetworkConfigService configService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected LeadershipService leadershipService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ClusterService clusterService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CordVtnService cordVtnService; + + private final ConfigFactory configFactory = + new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, CordVtnConfig.class, "cordvtn") { + @Override + public CordVtnConfig createConfig() { + return new CordVtnConfig(); + } + }; + + private final LeadershipEventListener leadershipListener = new InternalLeadershipListener(); + private final NetworkConfigListener configListener = new InternalConfigListener(); + + private NodeId local; + private ApplicationId appId; + + @Activate + protected void active() { + local = clusterService.getLocalNode().id(); + appId = coreService.getAppId(CordVtnService.CORDVTN_APP_ID); + + configService.addListener(configListener); + configRegistry.registerConfigFactory(configFactory); + + leadershipService.addListener(leadershipListener); + leadershipService.runForLeadership(CordVtnService.CORDVTN_APP_ID); + } + + @Deactivate + protected void deactivate() { + leadershipService.removeListener(leadershipListener); + leadershipService.withdraw(appId.name()); + + configRegistry.unregisterConfigFactory(configFactory); + configService.removeListener(configListener); + } + + private void readConfiguration() { + CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class); + + if (config == null) { + log.warn("No configuration found"); + return; + } + + config.ovsdbNodes().forEach(node -> { + DefaultOvsdbNode ovsdbNode = + new DefaultOvsdbNode(node.host(), node.ip(), node.port(), INIT); + cordVtnService.addNode(ovsdbNode); + log.info("Add new node {}", node.host()); + }); + } + + private synchronized void processLeadershipChange(NodeId leader) { + if (leader == null || !leader.equals(local)) { + return; + } + readConfiguration(); + } + + private class InternalLeadershipListener implements LeadershipEventListener { + + @Override + public void event(LeadershipEvent event) { + if (event.subject().topic().equals(appId.name())) { + processLeadershipChange(event.subject().leader()); + } + } + } + + private class InternalConfigListener implements NetworkConfigListener { + + @Override + public void event(NetworkConfigEvent event) { + // TODO handle update event + } + } +} diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java index d26a10aa..1f75dceb 100644 --- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnService.java @@ -15,8 +15,8 @@ */ package org.onosproject.cordvtn; -import org.onlab.packet.IpAddress; -import org.onlab.packet.TpPort; +import org.onosproject.cordvtn.OvsdbNode.State; +import org.onosproject.net.DeviceId; import java.util.List; @@ -24,22 +24,30 @@ import java.util.List; * Service for provisioning overlay virtual networks on compute nodes. */ public interface CordVtnService { + + String CORDVTN_APP_ID = "org.onosproject.cordvtn"; /** * Adds a new node to the service. * - * @param hostname hostname of the node - * @param ip ip address to access the ovsdb server running on the node - * @param port port number to access the ovsdb server running on the node + * @param ovsdbNode ovsdb node + */ + void addNode(OvsdbNode ovsdbNode); + + /** + * Deletes a node from the service. + * + * @param ovsdbNode ovsdb node */ - void addNode(String hostname, IpAddress ip, TpPort port); + void deleteNode(OvsdbNode ovsdbNode); /** - * Deletes the node from the service. + * Updates ovsdb node. + * It only used for updating node's connection state. * - * @param ip ip address to access the ovsdb server running on the node - * @param port port number to access the ovsdb server running on the node + * @param ovsdbNode ovsdb node + * @param state ovsdb connection state */ - void deleteNode(IpAddress ip, TpPort port); + void updateNode(OvsdbNode ovsdbNode, State state); /** * Returns the number of the nodes known to the service. @@ -49,6 +57,14 @@ public interface CordVtnService { int getNodeCount(); /** + * Returns OvsdbNode with given device id. + * + * @param deviceId device id + * @return ovsdb node + */ + OvsdbNode getNode(DeviceId deviceId); + + /** * Returns all nodes known to the service. * * @return list of nodes diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DefaultOvsdbNode.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DefaultOvsdbNode.java index b8cdbe94..ce8b0f1d 100644 --- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DefaultOvsdbNode.java +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/DefaultOvsdbNode.java @@ -15,6 +15,7 @@ */ package org.onosproject.cordvtn; +import com.google.common.base.MoreObjects; import org.onlab.packet.IpAddress; import org.onlab.packet.TpPort; import org.onosproject.net.DeviceId; @@ -26,21 +27,15 @@ import java.util.Objects; */ public class DefaultOvsdbNode implements OvsdbNode { - private final String hostname; + private final String host; private final IpAddress ip; private final TpPort port; - private final DeviceId deviceId; - private final DeviceId bridgeId; private final State state; - public DefaultOvsdbNode(String hostname, IpAddress ip, TpPort port, - DeviceId bridgeId, State state) { - this.hostname = hostname; + public DefaultOvsdbNode(String host, IpAddress ip, TpPort port, State state) { + this.host = host; this.ip = ip; this.port = port; - this.deviceId = DeviceId.deviceId( - "ovsdb:" + ip.toString() + ":" + port.toString()); - this.bridgeId = bridgeId; this.state = state; } @@ -55,8 +50,8 @@ public class DefaultOvsdbNode implements OvsdbNode { } @Override - public String hostname() { - return this.hostname; + public String host() { + return this.host; } @Override @@ -66,12 +61,12 @@ public class DefaultOvsdbNode implements OvsdbNode { @Override public DeviceId deviceId() { - return this.deviceId; + return DeviceId.deviceId("ovsdb:" + this.ip.toString() + ":" + this.port.toString()); } @Override - public DeviceId bridgeId() { - return this.bridgeId; + public DeviceId intBrId() { + return DeviceId.deviceId("of:" + this.host); } @Override @@ -82,8 +77,9 @@ public class DefaultOvsdbNode implements OvsdbNode { if (o instanceof DefaultOvsdbNode) { DefaultOvsdbNode that = (DefaultOvsdbNode) o; - // We compare the ip and port only. - if (this.ip.equals(that.ip) && this.port.equals(that.port)) { + if (this.host.equals(that.host) && + this.ip.equals(that.ip) && + this.port.equals(that.port)) { return true; } } @@ -92,6 +88,16 @@ public class DefaultOvsdbNode implements OvsdbNode { @Override public int hashCode() { - return Objects.hash(ip, port); + return Objects.hash(host, ip, port); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("host", host) + .add("ip", ip) + .add("port", port) + .add("state", state) + .toString(); } } diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/NodeConnectionManager.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/NodeConnectionManager.java index 0b7029ef..ebba4cd5 100644 --- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/NodeConnectionManager.java +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/NodeConnectionManager.java @@ -15,12 +15,19 @@ */ package org.onosproject.cordvtn; +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.cluster.ClusterService; import org.onosproject.cluster.LeadershipService; import org.onosproject.cluster.NodeId; -import org.onosproject.core.ApplicationId; import org.onosproject.mastership.MastershipService; -import org.onosproject.net.DeviceId; -import org.onosproject.store.service.EventuallyConsistentMap; +import org.onosproject.net.Device; +import org.onosproject.net.device.DeviceEvent; +import org.onosproject.net.device.DeviceListener; +import org.onosproject.net.device.DeviceService; import org.slf4j.Logger; import java.util.concurrent.Executors; @@ -28,118 +35,131 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import static org.onlab.util.Tools.groupedThreads; +import static org.onosproject.cordvtn.OvsdbNode.State.CONNECTED; +import static org.onosproject.cordvtn.OvsdbNode.State.DISCONNECTED; +import static org.onosproject.cordvtn.OvsdbNode.State.READY; import static org.slf4j.LoggerFactory.getLogger; /** - * Node connection manager. + * Provides the connection state management of all nodes registered to the service + * so that the nodes keep connected unless it is requested to be deleted. */ +@Component(immediate = true) public class NodeConnectionManager { protected final Logger log = getLogger(getClass()); - private final ApplicationId appId; - private final NodeId localId; - private final EventuallyConsistentMap<DeviceId, OvsdbNode> nodeStore; - private final MastershipService mastershipService; - private final LeadershipService leadershipService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + MastershipService mastershipService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + LeadershipService leadershipService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + ClusterService clusterService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + DeviceService deviceService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + CordVtnService cordVtnService; private static final int DELAY_SEC = 5; - private ScheduledExecutorService connectionExecutor; - - /** - * Creates a new NodeConnectionManager. - * - * @param localId local id - * @param nodeStore node store - * @param mastershipService mastership service - */ - public NodeConnectionManager(ApplicationId appId, NodeId localId, - EventuallyConsistentMap<DeviceId, OvsdbNode> nodeStore, - MastershipService mastershipService, - LeadershipService leadershipService) { - this.appId = appId; - this.localId = localId; - this.nodeStore = nodeStore; - this.mastershipService = mastershipService; - this.leadershipService = leadershipService; - } - /** - * Starts the node connection manager. - */ - public void start() { - connectionExecutor = Executors.newSingleThreadScheduledExecutor( - groupedThreads("onos/cordvtn", "connection-executor")); - connectionExecutor.scheduleWithFixedDelay(() -> nodeStore.values() + private final DeviceListener deviceListener = new InternalDeviceListener(); + private final ScheduledExecutorService connectionExecutor = Executors + .newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn", "connection-manager")); + + private NodeId localId; + + @Activate + protected void activate() { + localId = clusterService.getLocalNode().id(); + deviceService.addListener(deviceListener); + + connectionExecutor.scheduleWithFixedDelay(() -> cordVtnService.getNodes() .stream() .filter(node -> localId.equals(getMaster(node))) - .forEach(node -> connectNode(node)), 0, DELAY_SEC, TimeUnit.SECONDS); + .forEach(node -> { + connect(node); + disconnect(node); + }), 0, DELAY_SEC, TimeUnit.SECONDS); } - /** - * Stops the node connection manager. - */ + @Deactivate public void stop() { connectionExecutor.shutdown(); + deviceService.removeListener(deviceListener); } - /** - * Adds a new node to the system. - * - * @param ovsdbNode ovsdb node - */ - public void connectNode(OvsdbNode ovsdbNode) { + public void connect(OvsdbNode ovsdbNode) { switch (ovsdbNode.state()) { case INIT: case DISCONNECTED: - // TODO: set the node to passive mode + setPassiveMode(ovsdbNode); case READY: - // TODO: initiate connection - break; - case CONNECTED: + setupConnection(ovsdbNode); break; default: + break; } } - /** - * Deletes the ovsdb node. - * - * @param ovsdbNode ovsdb node - */ - public void disconnectNode(OvsdbNode ovsdbNode) { + public void disconnect(OvsdbNode ovsdbNode) { switch (ovsdbNode.state()) { - case CONNECTED: + case DISCONNECT: // TODO: disconnect break; - case INIT: - case READY: - case DISCONNECTED: - break; default: + break; + } + } + + private class InternalDeviceListener implements DeviceListener { + + @Override + public void event(DeviceEvent event) { + Device device = event.subject(); + if (device.type() != Device.Type.CONTROLLER) { + return; + } + + DefaultOvsdbNode node; + switch (event.type()) { + case DEVICE_ADDED: + node = (DefaultOvsdbNode) cordVtnService.getNode(device.id()); + if (node != null) { + cordVtnService.updateNode(node, CONNECTED); + } + break; + case DEVICE_AVAILABILITY_CHANGED: + node = (DefaultOvsdbNode) cordVtnService.getNode(device.id()); + if (node != null) { + cordVtnService.updateNode(node, DISCONNECTED); + } + break; + default: + break; + } } } private NodeId getMaster(OvsdbNode ovsdbNode) { - // Return the master of the bridge(switch) if it exist or - // return the current leader - if (ovsdbNode.bridgeId() == DeviceId.NONE) { - return leadershipService.getLeader(this.appId.name()); - } else { - return mastershipService.getMasterFor(ovsdbNode.bridgeId()); + NodeId master = mastershipService.getMasterFor(ovsdbNode.intBrId()); + + // master is null if there's no such device + if (master == null) { + master = leadershipService.getLeader(CordVtnService.CORDVTN_APP_ID); } + return master; } private void setPassiveMode(OvsdbNode ovsdbNode) { // TODO: need ovsdb client implementation first // TODO: set the remove ovsdb server passive mode - // TODO: set the node state READY if it succeed - } - - private void connect(OvsdbNode ovsdbNode) { - // TODO: need ovsdb client implementation first + cordVtnService.updateNode(ovsdbNode, READY); } - private void disconnect(OvsdbNode ovsdbNode) { - // TODO: need ovsdb client implementation first + private void setupConnection(OvsdbNode ovsdbNode) { + // TODO initiate connection } } diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java index bb2a0b7d..296bd439 100644 --- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java +++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java @@ -24,51 +24,52 @@ import org.onosproject.net.DeviceId; */ public interface OvsdbNode { /** - * State of the ovsdb node. + * Ovsdb connection state. */ enum State { - INIT, READY, CONNECTED, DISCONNECTED + INIT, READY, CONNECTED, DISCONNECT, DISCONNECTED } /** - * Returns the IP address of ovsdb server. + * Returns the IP address of the ovsdb server. * * @return ip address */ IpAddress ip(); /** - * Returns the port number of ovsdb server. + * Returns the port number of the ovsdb server. * * @return port number */ TpPort port(); /** - * Returns the hostname of the node. + * Returns the host information of the ovsdb server. + * It could be hostname or ip address. * - * @return hostname + * @return host */ - String hostname(); + String host(); /** - * Returns the state of the node. + * Returns the connection state of the ovsdb server. * - * @return state of the node + * @return connection state */ State state(); /** - * Returns the device ID of the node. + * Returns the device id of the ovsdb server. * * @return device id */ DeviceId deviceId(); /** - * Returns the device ID of the bridge associated with this node. + * Returns the device id of the integration bridge associated with the node. * * @return device id */ - DeviceId bridgeId(); + DeviceId intBrId(); } diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java index c9fade9e..5615af1a 100644 --- a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/DhcpStore.java @@ -65,7 +65,7 @@ public interface DhcpStore { * * @param hostId the host ID for which the mapping needs to be changed */ - void releaseIP(HostId hostId); + Ip4Address releaseIP(HostId hostId); /** * Returns a collection of all the MacAddress to IPAddress mapping assigned to the hosts. diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java index 345d5ad0..96d94a2b 100644 --- a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DhcpManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 Open Networking Laboratory + * Copyright 2015 Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -154,6 +154,8 @@ public class DhcpManager implements DhcpService { private static Ip4Address domainServer = Ip4Address.valueOf("10.0.0.2"); + private static final Ip4Address IP_BROADCAST = Ip4Address.valueOf("255.255.255.255"); + protected Timeout timeout; protected static int timerDelay = 2; @@ -290,12 +292,18 @@ public class DhcpManager implements DhcpService { DHCP dhcpPacket = (DHCP) udpPacket.getPayload(); DHCP dhcpReply = new DHCP(); dhcpReply.setOpCode(DHCP.OPCODE_REPLY); - - dhcpReply.setYourIPAddress(ipOffered.toInt()); - dhcpReply.setServerIPAddress(myIP.toInt()); - - dhcpReply.setTransactionId(dhcpPacket.getTransactionId()); + dhcpReply.setFlags(dhcpPacket.getFlags()); + dhcpReply.setGatewayIPAddress(dhcpPacket.getGatewayIPAddress()); dhcpReply.setClientHardwareAddress(dhcpPacket.getClientHardwareAddress()); + dhcpReply.setTransactionId(dhcpPacket.getTransactionId()); + + if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) { + dhcpReply.setYourIPAddress(ipOffered.toInt()); + dhcpReply.setServerIPAddress(myIP.toInt()); + if (dhcpPacket.getGatewayIPAddress() == 0) { + ipv4Reply.setDestinationAddress(IP_BROADCAST.toInt()); + } + } dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET); dhcpReply.setHardwareAddressLength((byte) 6); @@ -317,54 +325,57 @@ public class DhcpManager implements DhcpService { option.setData(myIP.toOctets()); optionList.add(option); - // IP Address Lease Time. - option = new DHCPOption(); - option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue()); - option.setLength((byte) 4); - option.setData(ByteBuffer.allocate(4).putInt(leaseTime).array()); - optionList.add(option); - - // IP Address Renewal Time. - option = new DHCPOption(); - option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue()); - option.setLength((byte) 4); - option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array()); - optionList.add(option); - - // IP Address Rebinding Time. - option = new DHCPOption(); - option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue()); - option.setLength((byte) 4); - option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array()); - optionList.add(option); - - // Subnet Mask. - option = new DHCPOption(); - option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue()); - option.setLength((byte) 4); - option.setData(subnetMask.toOctets()); - optionList.add(option); - - // Broadcast Address. - option = new DHCPOption(); - option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue()); - option.setLength((byte) 4); - option.setData(broadcastAddress.toOctets()); - optionList.add(option); - - // Router Address. - option = new DHCPOption(); - option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue()); - option.setLength((byte) 4); - option.setData(routerAddress.toOctets()); - optionList.add(option); - - // DNS Server Address. - option = new DHCPOption(); - option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue()); - option.setLength((byte) 4); - option.setData(domainServer.toOctets()); - optionList.add(option); + if (outgoingMessageType != DHCPPacketType.DHCPNAK.getValue()) { + + // IP Address Lease Time. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_LeaseTime.getValue()); + option.setLength((byte) 4); + option.setData(ByteBuffer.allocate(4).putInt(leaseTime).array()); + optionList.add(option); + + // IP Address Renewal Time. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_RenewalTime.getValue()); + option.setLength((byte) 4); + option.setData(ByteBuffer.allocate(4).putInt(renewalTime).array()); + optionList.add(option); + + // IP Address Rebinding Time. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OPtionCode_RebindingTime.getValue()); + option.setLength((byte) 4); + option.setData(ByteBuffer.allocate(4).putInt(rebindingTime).array()); + optionList.add(option); + + // Subnet Mask. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_SubnetMask.getValue()); + option.setLength((byte) 4); + option.setData(subnetMask.toOctets()); + optionList.add(option); + + // Broadcast Address. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_BroadcastAddress.getValue()); + option.setLength((byte) 4); + option.setData(broadcastAddress.toOctets()); + optionList.add(option); + + // Router Address. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_RouterAddress.getValue()); + option.setLength((byte) 4); + option.setData(routerAddress.toOctets()); + optionList.add(option); + + // DNS Server Address. + option = new DHCPOption(); + option.setCode(DHCP.DHCPOptionCode.OptionCode_DomainServer.getValue()); + option.setLength((byte) 4); + option.setData(domainServer.toOctets()); + optionList.add(option); + } // End Option. option = new DHCPOption(); @@ -447,41 +458,51 @@ public class DhcpManager implements DhcpService { } else if (incomingPacketType.getValue() == DHCPPacketType.DHCPREQUEST.getValue()) { - outgoingPacketType = DHCPPacketType.DHCPACK; - if (flagIfServerIP && flagIfRequestedIP) { // SELECTING state - if (myIP.equals(serverIP) && - dhcpStore.assignIP(hostId, requestedIP, leaseTime)) { + if (myIP.equals(serverIP)) { - Ethernet ethReply = buildReply(packet, requestedIP, - (byte) outgoingPacketType.getValue()); + if (dhcpStore.assignIP(hostId, requestedIP, leaseTime)) { + outgoingPacketType = DHCPPacketType.DHCPACK; + discoverHost(context, requestedIP); + } else { + outgoingPacketType = DHCPPacketType.DHCPNAK; + } + Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue()); sendReply(context, ethReply); - discoverHost(context, requestedIP); } } else if (flagIfRequestedIP) { // INIT-REBOOT state if (dhcpStore.assignIP(hostId, requestedIP, leaseTime)) { - Ethernet ethReply = buildReply(packet, requestedIP, - (byte) outgoingPacketType.getValue()); + outgoingPacketType = DHCPPacketType.DHCPACK; + Ethernet ethReply = buildReply(packet, requestedIP, (byte) outgoingPacketType.getValue()); sendReply(context, ethReply); discoverHost(context, requestedIP); } + } else { // RENEWING and REBINDING state int ciaadr = dhcpPayload.getClientIPAddress(); if (ciaadr != 0) { Ip4Address clientIaddr = Ip4Address.valueOf(ciaadr); if (dhcpStore.assignIP(hostId, clientIaddr, leaseTime)) { - Ethernet ethReply = buildReply(packet, clientIaddr, - (byte) outgoingPacketType.getValue()); - sendReply(context, ethReply); + outgoingPacketType = DHCPPacketType.DHCPACK; discoverHost(context, clientIaddr); + } else if (packet.getEtherType() == Ethernet.TYPE_IPV4 && + ((IPv4) packet.getPayload()).getDestinationAddress() == myIP.toInt()) { + outgoingPacketType = DHCPPacketType.DHCPNAK; + } else { + return; } + Ethernet ethReply = buildReply(packet, clientIaddr, (byte) outgoingPacketType.getValue()); + sendReply(context, ethReply); } } } else if (incomingPacketType.getValue() == DHCPPacketType.DHCPRELEASE.getValue()) { - dhcpStore.releaseIP(hostId); + Ip4Address ip4Address = dhcpStore.releaseIP(hostId); + if (ip4Address != null) { + hostProviderService.removeIpFromHost(hostId, ip4Address); + } } } } @@ -666,9 +687,10 @@ public class DhcpManager implements DhcpService { if ((ipAssignment.assignmentStatus() != IpAssignment.AssignmentStatus.Option_Expired) && (ipAssignment.leasePeriod() > 0) && (timeLapsed > (ipAssignment.leasePeriodMs()))) { - dhcpStore.releaseIP(entry.getKey()); - // TODO remove only the IP from the host entry when the API is in place. - hostProviderService.hostVanished(entry.getKey()); + Ip4Address ip4Address = dhcpStore.releaseIP(entry.getKey()); + if (ip4Address != null) { + hostProviderService.removeIpFromHost(entry.getKey(), ipAssignment.ipAddress()); + } } } timeout = Timer.getTimer().newTimeout(new PurgeListTask(), timerDelay, TimeUnit.MINUTES); diff --git a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java index dbdadb34..63f69d40 100644 --- a/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java +++ b/framework/src/onos/apps/dhcp/src/main/java/org/onosproject/dhcp/impl/DistributedDhcpStore.java @@ -212,7 +212,7 @@ public class DistributedDhcpStore implements DhcpStore { } @Override - public void releaseIP(HostId hostId) { + public Ip4Address releaseIP(HostId hostId) { if (allocationMap.containsKey(hostId)) { IpAssignment newAssignment = IpAssignment.builder(allocationMap.get(hostId).value()) .assignmentStatus(IpAssignment.AssignmentStatus.Option_Expired) @@ -222,7 +222,9 @@ public class DistributedDhcpStore implements DhcpStore { if (ipWithinRange(freeIP)) { freeIPPool.add(freeIP); } + return freeIP; } + return null; } @Override diff --git a/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java b/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java index 3ea3b1b8..fd4701c6 100644 --- a/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java +++ b/framework/src/onos/apps/dhcp/src/test/java/org/onosproject/dhcp/impl/DhcpManagerTest.java @@ -25,6 +25,7 @@ import org.onlab.packet.DHCPPacketType; import org.onlab.packet.Ethernet; import org.onlab.packet.IPv4; import org.onlab.packet.Ip4Address; +import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; import org.onlab.packet.UDP; import org.onosproject.core.CoreServiceAdapter; @@ -234,7 +235,8 @@ public class DhcpManagerTest { public void setDefaultTimeoutForPurge(int timeInSeconds) { } - public void releaseIP(HostId hostId) { + public Ip4Address releaseIP(HostId hostId) { + return null; } public Map<HostId, IpAssignment> listAssignedMapping() { @@ -331,12 +333,18 @@ public class DhcpManagerTest { @Override public void hostDetected(HostId hostId, HostDescription hostDescription, boolean replaceIps) { + } @Override public void hostVanished(HostId hostId) { } + @Override + public void removeIpFromHost(HostId hostId, IpAddress ipAddress) { + + } + } /** diff --git a/framework/src/onos/apps/flowanalyzer/pom.xml b/framework/src/onos/apps/flowanalyzer/pom.xml index f5dfcf2b..b0920412 100644 --- a/framework/src/onos/apps/flowanalyzer/pom.xml +++ b/framework/src/onos/apps/flowanalyzer/pom.xml @@ -40,6 +40,38 @@ <groupId>org.osgi</groupId> <artifactId>org.osgi.compendium</artifactId> </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-junit</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-cli</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.karaf.shell</groupId> + <artifactId>org.apache.karaf.shell.console</artifactId> + </dependency> + </dependencies> </project> diff --git a/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalysisCommand.java b/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalysisCommand.java new file mode 100644 index 00000000..2c61949b --- /dev/null +++ b/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalysisCommand.java @@ -0,0 +1,33 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.flowanalyzer; + +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; + +/** + * Analyzes flows for cycles and black holes. + */ +@Command(scope = "onos", name = "flow-analysis", + description = "Analyzes flows for cycles and black holes") +public class FlowAnalysisCommand extends AbstractShellCommand { + + @Override + protected void execute() { + FlowAnalyzer service = get(FlowAnalyzer.class); + print(service.analyze()); + } +} diff --git a/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java b/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java index 5d99d746..6aaaaee8 100644 --- a/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java +++ b/framework/src/onos/apps/flowanalyzer/src/main/java/org/onosproject/flowanalyzer/FlowAnalyzer.java @@ -21,12 +21,31 @@ import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.Service; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.FlowEntry; import org.onosproject.net.flow.FlowRuleService; -import org.onosproject.net.host.HostService; +import org.onosproject.net.DeviceId; +import org.onosproject.net.HostId; +import org.onosproject.net.flow.criteria.Criteria; +import org.onosproject.net.flow.criteria.Criterion; +import org.onosproject.net.flow.criteria.PortCriterion; +import org.onosproject.net.flow.instructions.Instruction; +import org.onosproject.net.flow.instructions.Instructions; +import org.onosproject.net.topology.TopologyService; +import org.onosproject.net.topology.TopologyGraph; import org.onosproject.net.link.LinkService; +import org.onosproject.net.Link; +import org.onosproject.net.topology.TopologyVertex; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; +import java.util.HashSet; +import java.util.List; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + import static org.slf4j.LoggerFactory.getLogger; /** @@ -42,11 +61,10 @@ public class FlowAnalyzer { protected FlowRuleService flowRuleService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected LinkService linkService; + protected TopologyService topologyService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected HostService hostService; - + protected LinkService linkService; @Activate public void activate(ComponentContext context) { @@ -58,12 +76,193 @@ public class FlowAnalyzer { log.info("Stopped"); } + TopologyGraph graph; + Map<FlowEntry, String> label = new HashMap<>(); + Set<FlowEntry> ignoredFlows = new HashSet<>(); /** - * ... + * Analyzes and prints out a report on the status of every flow entry inside + * the network. The possible states are: Cleared (implying that the entry leads to + * a host), Cycle (implying that it is part of cycle), and Black Hole (implying + * that the entry does not lead to a single host). */ - public void analyze() { - // TODO: implement this + public String analyze() { + graph = topologyService.getGraph(topologyService.currentTopology()); + for (TopologyVertex v: graph.getVertexes()) { + DeviceId srcDevice = v.deviceId(); + Iterable<FlowEntry> flowTable = flowRuleService.getFlowEntries(srcDevice); + for (FlowEntry flow: flowTable) { + dfs(flow); + } + } + + //analyze the cycles to look for "critical flows" that can be removed + //to break the cycle + Set<FlowEntry> critpts = new HashSet<>(); + for (FlowEntry flow: label.keySet()) { + if ("Cycle".equals(label.get(flow))) { + Map<FlowEntry, String> labelSaved = label; + label = new HashMap<FlowEntry, String>(); + ignoredFlows.add(flow); + for (TopologyVertex v: graph.getVertexes()) { + DeviceId srcDevice = v.deviceId(); + Iterable<FlowEntry> flowTable = flowRuleService.getFlowEntries(srcDevice); + for (FlowEntry flow1: flowTable) { + dfs(flow1); + } + } + + boolean replacable = true; + for (FlowEntry flow2: label.keySet()) { + if ("Cleared".equals(labelSaved.get(flow2)) && !("Cleared".equals(label.get(flow2)))) { + replacable = false; + } + } + if (replacable) { + critpts.add(flow); + } + label = labelSaved; + } + } + + for (FlowEntry flow: critpts) { + label.put(flow, "Cycle Critical Point"); + } + + String s = "\n"; + for (FlowEntry flow: label.keySet()) { + s += ("Flow Rule: " + flowEntryRepresentation(flow) + "\n"); + s += ("Analysis: " + label.get(flow) + "!\n\n"); + } + s += ("Analyzed " + label.keySet().size() + " flows."); + //log.info(s); + return s; + } + + public Map<FlowEntry, String> calcLabels() { + analyze(); + return label; + } + public String analysisOutput() { + analyze(); + String s = "\n"; + for (FlowEntry flow: label.keySet()) { + s += ("Flow Rule: " + flowEntryRepresentation(flow) + "\n"); + s += ("Analysis: " + label.get(flow) + "!\n\n"); + } + return s; + } + + private boolean dfs(FlowEntry flow) { + if (ignoredFlows.contains(flow)) { + return false; + } + if ("Cycle".equals(label.get(flow)) || + "Black Hole".equals(label.get(flow)) || + "Cleared".equals(label.get(flow)) || + "NA".equals(label.get(flow)) || + "Cycle Critical Point".equals(label.get(flow))) { + + // This flow has already been analyzed and there is no need to analyze it further + return !"Black Hole".equals(label.get(flow)); + } + + if ("Visiting".equals(label.get(flow))) { + //you've detected a cycle because you reached the same entry again during your dfs + //let it continue so you can label the whole cycle + label.put(flow, "Cycle"); + } else { + //otherwise, mark off the current flow entry as currently being visited + label.put(flow, "Visiting"); + } + + boolean pointsToLiveEntry = false; + + List<Instruction> instructions = flow.treatment().allInstructions(); + for (Instruction i: instructions) { + if (i instanceof Instructions.OutputInstruction) { + pointsToLiveEntry |= analyzeInstruction(i, flow); + } + if ("NA".equals(label.get(flow))) { + return pointsToLiveEntry; + } + } + + if (!pointsToLiveEntry) { + //this entry does not point to any "live" entries thus must be a black hole + label.put(flow, "Black Hole"); + } else if ("Visiting".equals(label.get(flow))) { + //the flow is not in a cycle or in a black hole + label.put(flow, "Cleared"); + } + return pointsToLiveEntry; } + private boolean analyzeInstruction(Instruction i, FlowEntry flow) { + boolean pointsToLiveEntry = false; + Instructions.OutputInstruction output = (Instructions.OutputInstruction) i; + PortNumber port = output.port(); + PortNumber outPort = null; + + DeviceId egress = null; + boolean hasHost = false; + + ConnectPoint portPt = new ConnectPoint(flow.deviceId(), port); + for (Link l: linkService.getEgressLinks(portPt)) { + if (l.dst().elementId() instanceof DeviceId) { + egress = l.dst().deviceId(); + outPort = l.dst().port(); + } else if (l.dst().elementId() instanceof HostId) { + //the port leads to a host: therefore it is not a dead link + pointsToLiveEntry = true; + hasHost = true; + } + } + if (!topologyService.isInfrastructure(topologyService.currentTopology(), portPt) && egress == null) { + pointsToLiveEntry = true; + hasHost = true; + } + if (hasHost) { + return pointsToLiveEntry; + } + if (egress == null) { + //the port that the flow instructions tells you to send the packet + //to doesn't exist or is a controller port + label.put(flow, "NA"); + return pointsToLiveEntry; + } + + Iterable<FlowEntry> dstFlowTable = flowRuleService.getFlowEntries(egress); + + Set<Criterion> flowCriteria = flow.selector().criteria(); + + //filter the criteria in order to remove port dependency + Set<Criterion> filteredCriteria = new HashSet<>(); + for (Criterion criterion : flowCriteria) { + if (!(criterion instanceof PortCriterion)) { + filteredCriteria.add(criterion); + } + } + + //ensure that the in port is equal to the port that it is coming in from + filteredCriteria.add(Criteria.matchInPort(outPort)); + + for (FlowEntry entry: dstFlowTable) { + if (ignoredFlows.contains(entry)) { + continue; + } + if (filteredCriteria.containsAll(entry.selector().criteria())) { + dfs(entry); + + if (!"Black Hole".equals(label.get(entry))) { + //this entry is "live" i.e not a black hole + pointsToLiveEntry = true; + } + } + } + return pointsToLiveEntry; + } + public String flowEntryRepresentation(FlowEntry flow) { + return "Device: " + flow.deviceId() + ", " + flow.selector().criteria() + ", " + flow.treatment().immediate(); + } } diff --git a/framework/src/onos/apps/flowanalyzer/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/flowanalyzer/src/main/resources/OSGI-INF/blueprint/shell-config.xml new file mode 100644 index 00000000..93cb27ee --- /dev/null +++ b/framework/src/onos/apps/flowanalyzer/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -0,0 +1,23 @@ +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0"> + <command> + <action class="org.onosproject.flowanalyzer.FlowAnalysisCommand"/> + </command> + + </command-bundle> +</blueprint> diff --git a/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/DefaultMutableTopologyGraph.java b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/DefaultMutableTopologyGraph.java new file mode 100644 index 00000000..4ea3aa48 --- /dev/null +++ b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/DefaultMutableTopologyGraph.java @@ -0,0 +1,28 @@ +package org.onosproject.flowanalyzer; + +import org.onlab.graph.MutableAdjacencyListsGraph; +import org.onosproject.net.topology.TopologyEdge; +import org.onosproject.net.topology.TopologyGraph; +import org.onosproject.net.topology.TopologyVertex; + +import java.util.Set; + +/** + * Default implementation of an immutable topology graph based on a generic + * implementation of adjacency lists graph. + */ +public class DefaultMutableTopologyGraph + extends MutableAdjacencyListsGraph<TopologyVertex, TopologyEdge> + implements TopologyGraph { + + /** + * Creates a topology graph comprising of the specified vertexes and edges. + * + * @param vertexes set of graph vertexes + * @param edges set of graph edges + */ + public DefaultMutableTopologyGraph(Set<TopologyVertex> vertexes, Set<TopologyEdge> edges) { + super(vertexes, edges); + } + +} diff --git a/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/FlowAnalyzerTest.java b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/FlowAnalyzerTest.java new file mode 100644 index 00000000..faa2f5f9 --- /dev/null +++ b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/FlowAnalyzerTest.java @@ -0,0 +1,120 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.flowanalyzer; + +import org.junit.Ignore; +import org.junit.Test; + +import org.onosproject.core.DefaultApplicationId; +import org.onosproject.net.DeviceId; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.DefaultFlowRule; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.FlowRuleExtPayLoad; +import org.onosproject.net.flow.FlowRuleService; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.instructions.Instructions; +import org.onosproject.net.topology.TopologyService; + +import java.util.Arrays; +import java.util.TreeSet; + +import static org.junit.Assert.assertEquals; + + +/** + * Created by nikcheerla on 7/20/15. + */ +public class FlowAnalyzerTest { + + FlowRuleService flowRuleService = new MockFlowRuleService(); + TopologyService topologyService; + MockLinkService linkService = new MockLinkService(); + + @Test + @Ignore("This needs to be reworked to be more robust") + public void basic() { + flowRuleService = new MockFlowRuleService(); + flowRuleService.applyFlowRules(genFlow("ATL-001", 110, 90)); + flowRuleService.applyFlowRules(genFlow("ATL-001", 110, 100)); + flowRuleService.applyFlowRules(genFlow("ATL-001", 110, 150)); + flowRuleService.applyFlowRules(genFlow("ATL-002", 80, 70)); + flowRuleService.applyFlowRules(genFlow("ATL-003", 120, 130)); + flowRuleService.applyFlowRules(genFlow("ATL-004", 50)); + flowRuleService.applyFlowRules(genFlow("ATL-005", 140, 10)); + + linkService.addLink("H00:00:00:00:00:0660", 160, "ATL-005", 140); + linkService.addLink("ATL-005", 10, "ATL-004", 40); + linkService.addLink("ATL-004", 50, "ATL-002", 80); + linkService.addLink("ATL-002", 70, "ATL-001", 110); + linkService.addLink("ATL-001", 150, "H00:00:00:00:00:0770", 170); + linkService.addLink("ATL-001", 90, "ATL-004", 30); + linkService.addLink("ATL-001", 100, "ATL-003", 120); + linkService.addLink("ATL-003", 130, "ATL-005", 20); + + topologyService = new MockTopologyService(linkService.createdGraph); + + FlowAnalyzer flowAnalyzer = new FlowAnalyzer(); + flowAnalyzer.flowRuleService = flowRuleService; + flowAnalyzer.linkService = linkService; + flowAnalyzer.topologyService = topologyService; + + String labels = flowAnalyzer.analysisOutput(); + String correctOutput = "Flow Rule: Device: atl-005, [IN_PORT{port=140}], [OUTPUT{port=10}]\n" + + "Analysis: Cleared!\n" + + "\n" + + "Flow Rule: Device: atl-003, [IN_PORT{port=120}], [OUTPUT{port=130}]\n" + + "Analysis: Black Hole!\n" + + "\n" + + "Flow Rule: Device: atl-001, [IN_PORT{port=110}], [OUTPUT{port=90}]\n" + + "Analysis: Cycle Critical Point!\n" + + "\n" + + "Flow Rule: Device: atl-004, [], [OUTPUT{port=50}]\n" + + "Analysis: Cycle!\n" + + "\n" + + "Flow Rule: Device: atl-001, [IN_PORT{port=110}], [OUTPUT{port=150}]\n" + + "Analysis: Cleared!\n" + + "\n" + + "Flow Rule: Device: atl-001, [IN_PORT{port=110}], [OUTPUT{port=100}]\n" + + "Analysis: Black Hole!\n" + + "\n" + + "Flow Rule: Device: atl-002, [IN_PORT{port=80}], [OUTPUT{port=70}]\n" + + "Analysis: Cycle!\n"; + assertEquals("Wrong labels", new TreeSet(Arrays.asList(labels.replaceAll("\\s+", "").split("!"))), + new TreeSet(Arrays.asList(correctOutput.replaceAll("\\s+", "").split("!")))); + } + + public FlowRule genFlow(String d, long inPort, long outPort) { + DeviceId device = DeviceId.deviceId(d); + TrafficSelector ts = DefaultTrafficSelector.builder().matchInPort(PortNumber.portNumber(inPort)).build(); + TrafficTreatment tt = DefaultTrafficTreatment.builder() + .add(Instructions.createOutput(PortNumber.portNumber(outPort))).build(); + return new DefaultFlowRule(device, ts, tt, 1, new DefaultApplicationId(5000, "of"), + 50000, true, FlowRuleExtPayLoad.flowRuleExtPayLoad(new byte[5])); + } + public FlowRule genFlow(String d, long outPort) { + DeviceId device = DeviceId.deviceId(d); + TrafficSelector ts = DefaultTrafficSelector.builder().build(); + TrafficTreatment tt = DefaultTrafficTreatment.builder() + .add(Instructions.createOutput(PortNumber.portNumber(outPort))).build(); + return new DefaultFlowRule(device, ts, tt, 1, new DefaultApplicationId(5000, "of"), + 50000, true, FlowRuleExtPayLoad.flowRuleExtPayLoad(new byte[5])); + } + +} diff --git a/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockFlowRuleService.java b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockFlowRuleService.java new file mode 100644 index 00000000..40bb0043 --- /dev/null +++ b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockFlowRuleService.java @@ -0,0 +1,103 @@ +package org.onosproject.flowanalyzer; + +import com.google.common.collect.Sets; +import org.onosproject.core.ApplicationId; +import org.onosproject.net.DeviceId; +import org.onosproject.net.flow.DefaultFlowEntry; +import org.onosproject.net.flow.FlowEntry; +import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.FlowRuleOperations; +import org.onosproject.net.flow.FlowRuleServiceAdapter; + +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + +/** + * Created by nikcheerla on 7/20/15. + */ + +public class MockFlowRuleService extends FlowRuleServiceAdapter { + + final Set<FlowRule> flows = Sets.newHashSet(); + boolean success; + + int errorFlow = -1; + public void setErrorFlow(int errorFlow) { + this.errorFlow = errorFlow; + } + + public void setFuture(boolean success) { + this.success = success; + } + + @Override + public void apply(FlowRuleOperations ops) { + AtomicBoolean thisSuccess = new AtomicBoolean(success); + ops.stages().forEach(stage -> stage.forEach(flow -> { + if (errorFlow == flow.rule().id().value()) { + thisSuccess.set(false); + } else { + switch (flow.type()) { + case ADD: + case MODIFY: //TODO is this the right behavior for modify? + flows.add(flow.rule()); + break; + case REMOVE: + flows.remove(flow.rule()); + break; + default: + break; + } + } + })); + if (thisSuccess.get()) { + ops.callback().onSuccess(ops); + } else { + ops.callback().onError(ops); + } + } + + @Override + public int getFlowRuleCount() { + return flows.size(); + } + + @Override + public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) { + return flows.stream() + .filter(flow -> flow.deviceId().equals(deviceId)) + .map(DefaultFlowEntry::new) + .collect(Collectors.toList()); + } + + @Override + public void applyFlowRules(FlowRule... flowRules) { + for (FlowRule flow : flowRules) { + flows.add(flow); + } + } + + @Override + public void removeFlowRules(FlowRule... flowRules) { + for (FlowRule flow : flowRules) { + flows.remove(flow); + } + } + + @Override + public Iterable<FlowRule> getFlowRulesById(ApplicationId id) { + return flows.stream() + .filter(flow -> flow.appId() == id.id()) + .collect(Collectors.toList()); + } + + @Override + public Iterable<FlowRule> getFlowRulesByGroupId(ApplicationId appId, short groupId) { + return flows.stream() + .filter(flow -> flow.appId() == appId.id() && flow.groupId().id() == groupId) + .collect(Collectors.toList()); + } +} + + diff --git a/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockLinkService.java b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockLinkService.java new file mode 100644 index 00000000..2171c6f8 --- /dev/null +++ b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockLinkService.java @@ -0,0 +1,183 @@ +package org.onosproject.flowanalyzer; + +import org.onosproject.net.PortNumber; +import org.onosproject.net.link.LinkServiceAdapter; +import org.onosproject.net.topology.TopologyEdge; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DeviceId; +import org.onosproject.net.ElementId; +import org.onosproject.net.HostId; +import org.onosproject.net.Link; +import org.onosproject.net.Annotations; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.net.topology.TopologyVertex; + +import java.util.Set; +import java.util.ArrayList; +import java.util.List; +import java.util.HashSet; + +import static org.onosproject.net.Link.State.ACTIVE; + + +/** + * Created by nikcheerla on 7/21/15. + */ +public class MockLinkService extends LinkServiceAdapter { + DefaultMutableTopologyGraph createdGraph = new DefaultMutableTopologyGraph(new HashSet<>(), new HashSet<>()); + List<Link> links = new ArrayList<>(); + + @Override + public int getLinkCount() { + return links.size(); + } + + @Override + public Iterable<Link> getLinks() { + return links; + } + + @Override + public Set<Link> getDeviceLinks(DeviceId deviceId) { + Set<Link> egress = getDeviceEgressLinks(deviceId); + egress.addAll(getDeviceIngressLinks(deviceId)); + return egress; + } + + @Override + public Set<Link> getDeviceEgressLinks(DeviceId deviceId) { + Set<Link> setL = new HashSet<>(); + for (Link l: links) { + if (l.src().elementId() instanceof DeviceId && l.src().deviceId().equals(deviceId)) { + setL.add(l); + } + } + return setL; + } + + @Override + public Set<Link> getDeviceIngressLinks(DeviceId deviceId) { + Set<Link> setL = new HashSet<>(); + for (Link l: links) { + if (l.dst().elementId() instanceof DeviceId && l.dst().deviceId().equals(deviceId)) { + setL.add(l); + } + } + return setL; + } + + + @Override + public Set<Link> getEgressLinks(ConnectPoint pt) { + Set<Link> setL = new HashSet<>(); + for (Link l: links) { + if (l.src().equals(pt)) { + setL.add(l); + } + } + return setL; + } + + @Override + public Set<Link> getIngressLinks(ConnectPoint pt) { + Set<Link> setL = new HashSet<>(); + for (Link l: links) { + if (l.dst().equals(pt)) { + setL.add(l); + } + } + return setL; + } + + @Override + public Set<Link> getLinks(ConnectPoint pt) { + Set<Link> setL = new HashSet<>(); + for (Link l: links) { + if (l.src().equals(pt) || l.dst().equals(pt)) { + setL.add(l); + } + } + return setL; + } + + public void addLink(String device, long port, String device2, long port2) { + ElementId d1; + if (device.charAt(0) == 'H') { + device = device.substring(1, device.length()); + d1 = HostId.hostId(device); + } else { + d1 = DeviceId.deviceId(device); + } + + ElementId d2; + if (device2.charAt(0) == 'H') { + d2 = HostId.hostId(device2.substring(1, device2.length())); + } else { + d2 = DeviceId.deviceId(device2); + } + + ConnectPoint src = new ConnectPoint(d1, PortNumber.portNumber(port)); + ConnectPoint dst = new ConnectPoint(d2, PortNumber.portNumber(port2)); + Link curLink; + curLink = new Link() { + @Override + public ConnectPoint src() { + return src; + } + + @Override + public ConnectPoint dst() { + return dst; + } + + @Override + public boolean isDurable() { + return true; + } + + @Override + public Annotations annotations() { + return null; + } + + @Override + public Type type() { + return null; + } + + @Override + public ProviderId providerId() { + return null; + } + + @Override + public State state() { + return ACTIVE; + } + }; + links.add(curLink); + if (d1 instanceof DeviceId && d2 instanceof DeviceId) { + TopologyVertex v1 = () -> (DeviceId) d1, v2 = () -> (DeviceId) d2; + createdGraph.addVertex(v1); + createdGraph.addVertex(v2); + createdGraph.addEdge(new TopologyEdge() { + @Override + public Link link() { + return curLink; + } + + @Override + public TopologyVertex src() { + return v1; + } + + @Override + public TopologyVertex dst() { + return v2; + } + }); + } + } + + +} diff --git a/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockTopologyService.java b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockTopologyService.java new file mode 100644 index 00000000..0d25c977 --- /dev/null +++ b/framework/src/onos/apps/flowanalyzer/src/test/java/org/onosproject/flowanalyzer/MockTopologyService.java @@ -0,0 +1,21 @@ +package org.onosproject.flowanalyzer; +import org.onosproject.net.topology.Topology; +import org.onosproject.net.topology.TopologyGraph; +import org.onosproject.net.topology.TopologyServiceAdapter; + + +/** + * Created by nikcheerla on 7/20/15. + */ +public class MockTopologyService extends TopologyServiceAdapter { + TopologyGraph cur; + + public MockTopologyService(TopologyGraph g) { + cur = g; + } + + @Override + public TopologyGraph getGraph(Topology topology) { + return cur; + } +} diff --git a/framework/src/onos/apps/igmp/pom.xml b/framework/src/onos/apps/igmp/pom.xml new file mode 100644 index 00000000..7980d2c0 --- /dev/null +++ b/framework/src/onos/apps/igmp/pom.xml @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2014 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-apps</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>onos-app-igmp</artifactId> + <packaging>bundle</packaging> + + <description>Internet Group Message Protocol</description> + + <properties> + <onos.app.name>org.onosproject.igmp</onos.app.name> + </properties> + + <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-cli</artifactId> + <version>${project.version}</version> + </dependency> + + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-osgi</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-junit</artifactId> + <scope>test</scope> + </dependency> + + <!-- This is needed by ComponentContext, used for tunable configuration --> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + <version>1.9.8</version> + <scope>provided</scope> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-SymbolicName> + ${project.groupId}.${project.artifactId} + </Bundle-SymbolicName> + <Import-Package> + org.slf4j, + org.osgi.framework, + org.apache.commons.lang.math.*, + com.google.common.*, + org.onlab.packet.*, + org.onlab.rest.*, + org.onosproject.*, + org.onosproject.mfwd.impl.*; + org.onlab.util.*, + org.jboss.netty.util.* + </Import-Package> + </instructions> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>2.5.1</version> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPComponent.java b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPComponent.java new file mode 100644 index 00000000..ae539c62 --- /dev/null +++ b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPComponent.java @@ -0,0 +1,155 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.igmp.impl; + +import static org.slf4j.LoggerFactory.getLogger; + +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.onlab.packet.Ethernet; +import org.onlab.packet.IPv4; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.IGMP; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.packet.InboundPacket; +import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketPriority; +import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketService; +import org.slf4j.Logger; + +/** + * Internet Group Management Protocol. + */ +@Component(immediate = true) +public class IGMPComponent { + private final Logger log = getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected PacketService packetService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + private IGMPPacketProcessor processor = new IGMPPacketProcessor(); + private static ApplicationId appId; + + @Activate + public void activate() { + appId = coreService.registerApplication("org.onosproject.igmp"); + + packetService.addProcessor(processor, PacketProcessor.director(1)); + + // Build a traffic selector for all multicast traffic + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + selector.matchEthType(Ethernet.TYPE_IPV4); + selector.matchIPProtocol(IPv4.PROTOCOL_IGMP); + packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId); + + log.info("Started"); + } + + @Deactivate + public void deactivate() { + packetService.removeProcessor(processor); + processor = null; + log.info("Stopped"); + } + + /** + * Packet processor responsible for handling IGMP packets. + */ + private class IGMPPacketProcessor implements PacketProcessor { + + @Override + public void process(PacketContext context) { + // Stop processing if the packet has been handled, since we + // can't do any more to it. + if (context.isHandled()) { + return; + } + + InboundPacket pkt = context.inPacket(); + Ethernet ethPkt = pkt.parsed(); + if (ethPkt == null) { + return; + } + + /* + * IPv6 MLD packets are handled by ICMP6. We'll only deal + * with IPv4. + */ + if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) { + return; + } + + IPv4 ip = (IPv4) ethPkt.getPayload(); + IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress()); + IpAddress saddr = Ip4Address.valueOf(ip.getSourceAddress()); + log.debug("Packet (" + saddr.toString() + ", " + gaddr.toString() + + "\tingress port: " + context.inPacket().receivedFrom().toString()); + + if (ip.getProtocol() != IPv4.PROTOCOL_IGMP) { + log.error("IGMP Picked up a non IGMP packet."); + return; + } + + IpPrefix mcast = IpPrefix.valueOf("224.0.0.0/4"); + if (!mcast.contains(gaddr)) { + log.error("IGMP Picked up a non multicast packet."); + return; + } + + if (mcast.contains(saddr)) { + log.error("IGMP Picked up a packet with a multicast source address."); + return; + } + IpPrefix spfx = IpPrefix.valueOf(saddr, 32); + IpPrefix gpfx = IpPrefix.valueOf(gaddr, 32); + + IGMP igmp = (IGMP) ip.getPayload(); + switch (igmp.getIgmpType()) { + + case IGMP.TYPE_IGMPV3_MEMBERSHIP_REPORT: + IGMPProcessMembership.processMembership(igmp, pkt.receivedFrom()); + break; + + case IGMP.TYPE_IGMPV3_MEMBERSHIP_QUERY: + IGMPProcessQuery.processQuery(igmp, pkt.receivedFrom()); + break; + + case IGMP.TYPE_IGMPV1_MEMBERSHIP_REPORT: + case IGMP.TYPE_IGMPV2_MEMBERSHIP_REPORT: + case IGMP.TYPE_IGMPV2_LEAVE_GROUP: + log.debug("IGMP version 1 & 2 message types are not currently supported. Message type: " + + igmp.getIgmpType()); + break; + + default: + log.debug("Unkown IGMP message type: " + igmp.getIgmpType()); + break; + } + } + } +} diff --git a/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessMembership.java b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessMembership.java new file mode 100644 index 00000000..3d7d6033 --- /dev/null +++ b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessMembership.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.igmp.impl; + +import org.onlab.packet.IGMP; +import org.onosproject.net.ConnectPoint; + +/** + * Process an IGMP Membership Report. + */ +public final class IGMPProcessMembership { + + // Hide the default constructor. + private IGMPProcessMembership() { + } + + /** + * Process the IGMP Membership report. + * + * @param igmp the deserialized IGMP message. + * @param receivedFrom the ConnectPoint this message came from. + */ + public static void processMembership(IGMP igmp, ConnectPoint receivedFrom) { + } + +} diff --git a/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessQuery.java b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessQuery.java new file mode 100644 index 00000000..eb256796 --- /dev/null +++ b/framework/src/onos/apps/igmp/src/main/java/org/onosproject/igmp/impl/IGMPProcessQuery.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.igmp.impl; + +import org.onlab.packet.IGMP; +import org.onosproject.net.ConnectPoint; + +/** + * Process IGMP Query messages. + */ +public final class IGMPProcessQuery { + + // Hide the default constructor. + private IGMPProcessQuery() { + } + + /** + * Process the IGMP Membership Query message. + * + * @param igmp The deserialzed IGMP message + * @param receivedFrom the ConnectPoint this message came from. + */ + public static void processQuery(IGMP igmp, ConnectPoint receivedFrom) { + } + +} diff --git a/framework/src/onos/apps/mfwd/pom.xml b/framework/src/onos/apps/mfwd/pom.xml new file mode 100644 index 00000000..835de836 --- /dev/null +++ b/framework/src/onos/apps/mfwd/pom.xml @@ -0,0 +1,142 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2014 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-apps</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>onos-app-mfwd</artifactId> + <packaging>bundle</packaging> + + <description>Multicast forwarding application</description> + + <properties> + <onos.app.name>org.onosproject.mfwd</onos.app.name> + </properties> + + <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-cli</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>io.netty</groupId> + <artifactId>netty</artifactId> + <version>3.9.0.Final</version> + </dependency> + + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.karaf.shell</groupId> + <artifactId>org.apache.karaf.shell.console</artifactId> + </dependency> + + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-rest</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-rest</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>com.sun.jersey</groupId> + <artifactId>jersey-servlet</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-annotations</artifactId> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-junit</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymock</artifactId> + <scope>test</scope> + </dependency> + + </dependencies> + + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <_wab>src/main/webapp/</_wab> + <Bundle-SymbolicName> + ${project.groupId}.${project.artifactId} + </Bundle-SymbolicName> + <Import-Package> + org.slf4j, + org.osgi.framework, + javax.ws.rs,javax.ws.rs.core, + com.sun.jersey.api.core, + com.sun.jersey.spi.container.servlet, + com.sun.jersey.server.impl.container.servlet, + com.fasterxml.jackson.databind, + com.fasterxml.jackson.databind.node, + org.apache.commons.lang.math.*, + org.apache.karaf.shell.commands, + org.apache.karaf.shell.console, + com.google.common.*, + org.onlab.packet.*, + org.onlab.rest.*, + org.onosproject.*, + org.onlab.util.*, + org.jboss.netty.util.* + </Import-Package> + <Web-ContextPath>${web.context}</Web-ContextPath> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + +</project> + diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastDeleteCommand.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastDeleteCommand.java new file mode 100644 index 00000000..ae5d9e93 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastDeleteCommand.java @@ -0,0 +1,45 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.mfwd.cli; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.mfwd.impl.McastRouteTable; + +/** + * Deletes a multicast route. + */ +@Command(scope = "onos", name = "mcast-delete", + description = "Delete a multicast route flow") +public class McastDeleteCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "sAddr", + description = "IP Address of the multicast source. '*' can be used for any source (*, G) entry", + required = true, multiValued = false) + String sAddr = null; + + @Argument(index = 1, name = "gAddr", + description = "IP Address of the multicast group", + required = true, multiValued = false) + String gAddr = null; + + @Override + protected void execute() { + McastRouteTable mrib = McastRouteTable.getInstance(); + mrib.removeRoute(sAddr, gAddr); + } +}
\ No newline at end of file diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastJoinCommand.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastJoinCommand.java new file mode 100644 index 00000000..7260fde5 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastJoinCommand.java @@ -0,0 +1,72 @@ +/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+
+import org.onosproject.mfwd.impl.McastConnectPoint;
+import org.onosproject.mfwd.impl.McastRouteBase;
+import org.onosproject.mfwd.impl.McastRouteTable;
+
+/**
+ * Installs a source, multicast group flow.
+ */
+@Command(scope = "onos", name = "mcast-join",
+ description = "Installs a source, multicast group flow")
+public class McastJoinCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "sAddr",
+ description = "IP Address of the multicast source. '*' can be used for any source (*, G) entry",
+ required = true, multiValued = false)
+ String sAddr = null;
+
+ @Argument(index = 1, name = "gAddr",
+ description = "IP Address of the multicast group",
+ required = true, multiValued = false)
+ String gAddr = null;
+
+ @Argument(index = 2, name = "ingressPort",
+ description = "Ingress port and Egress ports",
+ required = false, multiValued = false)
+ String ingressPort = null;
+
+ @Argument(index = 3, name = "ports",
+ description = "Ingress port and Egress ports",
+ required = false, multiValued = true)
+ String[] ports = null;
+
+ @Override
+ protected void execute() {
+ McastRouteTable mrib = McastRouteTable.getInstance();
+ McastRouteBase mr = mrib.addRoute(sAddr, gAddr);
+
+ // Port format "of:0000000000000023/4"
+ if (ingressPort != null) {
+ String inCP = ingressPort;
+ log.debug("Ingress port provided: " + inCP);
+ mr.addIngressPoint(inCP);
+ }
+
+ for (int i = 0; i < ports.length; i++) {
+ String egCP = ports[i];
+ log.debug("Egress port provided: " + egCP);
+ mr.addEgressPoint(egCP, McastConnectPoint.JoinSource.STATIC);
+ }
+ print("Added the mcast route");
+ }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastShowCommand.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastShowCommand.java new file mode 100644 index 00000000..7fa3a13a --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/McastShowCommand.java @@ -0,0 +1,62 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.mfwd.cli; + +import org.apache.karaf.shell.commands.Command; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.JsonNode; + +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.mfwd.impl.McastRouteTable; +import org.onosproject.mfwd.impl.MRibCodec; + +import org.slf4j.Logger; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Displays the source, multicast group flows entries. + */ +@Command(scope = "onos", name = "mcast-show", description = "Displays the source, multicast group flows") +public class McastShowCommand extends AbstractShellCommand { + + private final Logger log = getLogger(getClass()); + private static final String MCAST_GROUP = "mcastgroup"; + + @Override + protected void execute() { + McastRouteTable mrt = McastRouteTable.getInstance(); + if (outputJson()) { + print("%s", json(mrt)); + } else { + printMrib4(mrt); + } + } + + public JsonNode json(McastRouteTable mrt) { + ObjectNode pushContent = new MRibCodec().encode(mrt , this); + return pushContent; + } + + /** + * Displays multicast route table entries. + * + * @param mrt Mutlicast Route Table + */ + protected void printMrib4(McastRouteTable mrt) { + print(mrt.printMcastRouteTable()); + } +} diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/package-info.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/package-info.java new file mode 100644 index 00000000..7b5ed39a --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/cli/package-info.java @@ -0,0 +1,5 @@ +/**
+ * Sample Multicast forwarding framework using intents.
+ */
+package org.onosproject.mfwd.cli;
+
diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/MRibCodec.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/MRibCodec.java new file mode 100644 index 00000000..c4f18527 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/MRibCodec.java @@ -0,0 +1,211 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.mfwd.impl; + +import org.onosproject.codec.CodecContext; +import org.onosproject.codec.JsonCodec; + +import org.onlab.packet.IpPrefix; + +import java.util.Set; +import java.util.Map; +import java.util.Collection; +import java.util.Iterator; +import java.util.Optional; + +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; + +import org.slf4j.Logger; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Encode and Decode the Multicast Route Table in JSON for CLI and REST commands. + */ +public class MRibCodec extends JsonCodec<McastRouteTable> { + + private final Logger log = getLogger(getClass()); + private static final String SOURCE_ADDRESS = "sourceAddress"; + private static final String GROUP_ADDRESS = "groupAddress"; + private static final String INGRESS_POINT = "ingressPoint"; + private static final String EGRESS_POINT = "egressPoint"; + private static final String MCASTCONNECTPOINT = "McastConnectPoint"; + private static final String ELEMENTID = "elementId"; + private static final String PORTNUMBER = "portNumber"; + private static final String MCAST_GROUP = "mcastGroup"; + + /** + * Encode the MRIB into json format. + * + * @param mcastRouteTable McastRouteTable + * @param context CodecContext + * @return result ObjectNode + */ + @Override + public ObjectNode encode(McastRouteTable mcastRouteTable, CodecContext context) { + + final JsonNodeFactory nodeFactory = JsonNodeFactory.instance; + final ObjectNode macastRouteTabNode = nodeFactory.objectNode(); + ArrayNode mcastGroupNode = context.mapper().createArrayNode(); + Optional<McastRouteTable> mcastRouteTabOpt = Optional.ofNullable(mcastRouteTable); + + //checking whether the McastRouteTable is present. + if (mcastRouteTabOpt.isPresent()) { + Map<IpPrefix, McastRouteGroup> mrib4 = mcastRouteTabOpt.get().getMrib4(); + Optional<Map<IpPrefix, McastRouteGroup>> mrib4Opt = Optional.ofNullable(mrib4); + + //checking whether the mrib4 is present. + if (mrib4Opt.isPresent()) { + + for (McastRouteGroup mg : mrib4Opt.get().values()) { + Collection<McastRouteSource> mcastRoute = mg.getSources().values(); + Optional<Collection<McastRouteSource>> mcastRouteOpt = Optional.ofNullable(mcastRoute); + + //checking whether the McastRouteSource List is present. + if (mcastRouteOpt.isPresent()) { + for (McastRouteSource mcastRouteSource : mcastRouteOpt.get()) { + mcastGroupNode.add(createMcastGroupNode(mcastRouteSource, context)); + } + macastRouteTabNode.put(MCAST_GROUP, mcastGroupNode); + } + } + } + } + return macastRouteTabNode; + } + /** + * Method for creating the McastGroup object node. + * + * @param mcastRouteSource McastRouteSource + */ + private ObjectNode createMcastGroupNode(McastRouteSource mcastRouteSource, CodecContext context) { + + final ObjectNode mcastGroupNode = context.mapper().createObjectNode(); + final ObjectNode ingressNode = context.mapper().createObjectNode(); + final ObjectNode egressNode = context.mapper().createObjectNode(); + final ArrayNode jsonLabelIds = context.mapper().createArrayNode(); + final String sAddr = mcastRouteSource.getSaddr().toString(); + final String gAddr = mcastRouteSource.getGaddr().toString(); + + Optional<String> saddrOpt = Optional.ofNullable(sAddr); + Optional<String> gaddrOpt = Optional.ofNullable(gAddr); + + //checking source address and group address are present. + if (saddrOpt.isPresent() && gaddrOpt.isPresent()) { + mcastGroupNode.put(SOURCE_ADDRESS, saddrOpt.get().toString()); + mcastGroupNode.put(GROUP_ADDRESS, gaddrOpt.get().toString()); + McastConnectPoint mcastIngCP = mcastRouteSource.getIngressPoint(); + Optional<McastConnectPoint> mcastIngCPOpt = Optional.ofNullable(mcastIngCP); + + //checking whether the ingress connection point is present. + if (mcastIngCPOpt.isPresent()) { + ingressNode.put(MCASTCONNECTPOINT, mcastConnectPoint(mcastIngCPOpt.get(), context)); + } + + mcastGroupNode.put(INGRESS_POINT , ingressNode); + Set<McastConnectPoint> mcastEgCPSet = mcastRouteSource.getEgressPoints(); + Optional<Set<McastConnectPoint>> mcastEgCPOpt = Optional.ofNullable(mcastEgCPSet); + + //checking whether the egress connection points are present. + if (mcastEgCPOpt.isPresent()) { + for (final McastConnectPoint mcastConnectPoint : mcastEgCPOpt.get()) { + jsonLabelIds.add(mcastConnectPoint(mcastConnectPoint, context)); + } + } + + egressNode.put(MCASTCONNECTPOINT , jsonLabelIds); + mcastGroupNode.put(EGRESS_POINT , egressNode); + } + return mcastGroupNode; + } + + /** + * Method for creating the McastConnectPoint object node. + * + * @param mcastConnectPoint McastConnectPoint + * @param context CodecContext + * @return mcastCpNode ObjectNode + */ + private ObjectNode mcastConnectPoint(McastConnectPoint mcastConnectPoint, CodecContext context) { + final ObjectNode mcastCpNode = context.mapper().createObjectNode(); + mcastCpNode.put(ELEMENTID , mcastConnectPoint.getConnectPoint().elementId().toString()); + mcastCpNode.put(PORTNUMBER , mcastConnectPoint.getConnectPoint().port().toLong()); + return mcastCpNode; + } + + /** + * Decode json format and insert into the flow table. + * + * @param json ObjectNode + * @param context CodecContext + * @return mr McastRouteBase + */ + @Override + public McastRouteTable decode(ObjectNode json, CodecContext context) { + + String macAddr = null; + String portNo = null; + String sAddr = json.path(SOURCE_ADDRESS).asText(); + String gAddr = json.path(GROUP_ADDRESS).asText(); + JsonNode inPntObjNode = (JsonNode) json.path(INGRESS_POINT); + JsonNode egPntArrNode = (JsonNode) json.path(EGRESS_POINT); + + log.debug("sAddr :" + sAddr + " gAddr :" + gAddr + " inPntObjNode :" + inPntObjNode); + log.debug("egPntArrNode :" + egPntArrNode.toString()); + + McastRouteTable mrib = McastRouteTable.getInstance(); + McastRouteBase mr = mrib.addRoute(sAddr, gAddr); + Optional<JsonNode> inPntOpt = Optional.ofNullable(inPntObjNode); + + if (inPntOpt.isPresent()) { + + JsonNode inMcastCP = inPntOpt.get().path(MCASTCONNECTPOINT); + Optional<JsonNode> inCpOpt = Optional.ofNullable(inMcastCP); + + if (inCpOpt.isPresent()) { + macAddr = inCpOpt.get().path(ELEMENTID).asText(); + portNo = inCpOpt.get().path(PORTNUMBER).asText(); + mr.addIngressPoint(macAddr + "/" + Long.parseLong(portNo)); + } + } + + Optional<JsonNode> egPntOpt = Optional.ofNullable(egPntArrNode); + + if (egPntOpt.isPresent()) { + JsonNode egMcastCP = egPntOpt.get().path(MCASTCONNECTPOINT); + Optional<JsonNode> egMcCpOpt = Optional.ofNullable(egMcastCP); + + if (egMcCpOpt.isPresent()) { + Iterator<JsonNode> egCpIt = egMcCpOpt.get().elements(); + + while (egCpIt.hasNext()) { + + JsonNode egMcastCPObj = egCpIt.next(); + Optional<JsonNode> egMcCpObOpt = Optional.ofNullable(egMcastCPObj); + if (egMcCpObOpt.isPresent()) { + macAddr = egMcCpObOpt.get().path(ELEMENTID).asText(); + portNo = egMcCpObOpt.get().path(PORTNUMBER).asText(); + log.debug("macAddr egPort : " + macAddr + " portNo egPort :" + portNo); + mr.addEgressPoint(macAddr + "/" + Long.parseLong(portNo), McastConnectPoint.JoinSource.STATIC); + } + } + } + } + return mrib; + } +} diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastConnectPoint.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastConnectPoint.java new file mode 100644 index 00000000..e2a6ff0d --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastConnectPoint.java @@ -0,0 +1,68 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.mfwd.impl; + +import org.onosproject.net.ConnectPoint; +import java.util.EnumSet; +import java.util.Set; + +/** + * Mulitcast ConnectPoint adds a variable to track the usage + * of these multicast endpoints. + */ +public class McastConnectPoint { + + private ConnectPoint connectPoint; + + public enum JoinSource { + STATIC, IGMP, PIM; + } + + public EnumSet<JoinSource> interest = EnumSet.noneOf(JoinSource.class); + + public McastConnectPoint(ConnectPoint cp) { + this.connectPoint = cp; + } + + public McastConnectPoint(ConnectPoint cp, JoinSource src) { + this.connectPoint = cp; + interest.add(src); + } + + public McastConnectPoint(String connectPoint, JoinSource src) { + ConnectPoint cp = ConnectPoint.deviceConnectPoint(connectPoint); + this.connectPoint = cp; + this.interest.add(src); + } + + /** + * Get the connect point. + * + * @return connectPoint + */ + public ConnectPoint getConnectPoint() { + return connectPoint; + } + + /** + * Get the sources of interest for this egressPoint. + * + * @return interest flags + */ + public Set<JoinSource> getInterest() { + return interest; + } +} diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastForwarding.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastForwarding.java new file mode 100644 index 00000000..f5bd1e01 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastForwarding.java @@ -0,0 +1,237 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.mfwd.impl; + +import static org.slf4j.LoggerFactory.getLogger; + +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.onlab.packet.Ethernet; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.IpAddress; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.IPv4; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.packet.DefaultOutboundPacket; +import org.onosproject.net.packet.InboundPacket; +import org.onosproject.net.packet.OutboundPacket; +import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketPriority; +import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketService; +import org.slf4j.Logger; + +/** + * WORK-IN-PROGRESS: The multicast forwarding application using intent framework. + */ +@Component(immediate = true) +public class McastForwarding { + + private final Logger log = getLogger(getClass()); + private final IpPrefix mcast = IpPrefix.valueOf("224.0.0.0/4"); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected PacketService packetService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + private ReactivePacketProcessor processor = new ReactivePacketProcessor(); + private McastRouteTable mrib; + private static ApplicationId appId; + + /** + * Active MulticastForwardingIntent. + */ + @Activate + public void activate() { + appId = coreService.registerApplication("org.onosproject.mfwd"); + + packetService.addProcessor(processor, PacketProcessor.director(2)); + + // Build a traffic selector for all multicast traffic + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + selector.matchEthType(Ethernet.TYPE_IPV4); + selector.matchIPDst(mcast); + packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId); + + mrib = McastRouteTable.getInstance(); + log.info("Started"); + } + + /** + * Deactivate Multicast Forwarding Intent. + */ + @Deactivate + public void deactivate() { + packetService.removeProcessor(processor); + processor = null; + log.info("Stopped"); + } + + /** + * Get the application ID, used by the McastIntentManager. + * + * @return the application ID + */ + public static ApplicationId getAppId() { + return appId; + } + + /** + * Packet processor responsible for forwarding packets along their paths. + */ + private class ReactivePacketProcessor implements PacketProcessor { + + /** + * Process incoming packets. + * + * @param context packet processing context + */ + @Override + public void process(PacketContext context) { + // Stop processing if the packet has been handled, since we + // can't do any more to it. + if (context.isHandled()) { + return; + } + + InboundPacket pkt = context.inPacket(); + Ethernet ethPkt = pkt.parsed(); + + if (ethPkt == null) { + return; + } + + if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4 && + ethPkt.getEtherType() != Ethernet.TYPE_IPV6) { + return; + } + + if (ethPkt.getEtherType() == Ethernet.TYPE_IPV6) { + // Ignore ipv6 at the moment. + return; + } + + IPv4 ip = (IPv4) ethPkt.getPayload(); + IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress()); + IpAddress saddr = Ip4Address.valueOf(ip.getSourceAddress()); + + log.debug("Packet ({}, {}) has been punted\n" + + "\tingress port: {}\n", + saddr.toString(), + gaddr.toString(), + context.inPacket().receivedFrom().toString()); + + if (!mcast.contains(gaddr)) { + // Yikes, this is a bad group address + return; + } + + if (mcast.contains(saddr)) { + // Yikes, the source address is multicast + return; + } + + IpPrefix spfx = IpPrefix.valueOf(saddr, 32); + IpPrefix gpfx = IpPrefix.valueOf(gaddr, 32); + + /* + * Do a best match lookup on the (s, g) of the packet. If an entry does + * not exist create one and store it's incoming connect point. + * + * The connect point is deviceId / portId that the packet entered + * the SDN network. This differs from traditional mcast where the + * ingress port would be a specific device. + */ + McastRoute entry = mrib.findBestMatch(spfx, gpfx); + if (entry == null || entry.getSaddr().equals(IPv4.fromIPv4Address(0))) { + + /* + * Create an entry that we can fast drop. + */ + entry = mrib.addRoute(spfx, gpfx); + entry.addIngressPoint(context.inPacket().receivedFrom()); + } + + /* + * TODO: If we do not have an ingress or any egress connect points we + * should set up a fast drop entry. + */ + if (entry.getIngressPoint() == null) { + return; + } + + if (entry.getEgressPoints().isEmpty()) { + return; + } + + /* + * This is odd, we should not have received a punted packet if an + * intent was installed unless the intent was not installed + * correctly. However, we are seeing packets get punted after + * the intent has been installed. + * + * Therefore we are going to forward the packets even if they + * should have already been forwarded by the intent fabric. + */ + if (entry.getIntentKey() != null) { + return; + } + + entry.setIntent(); + McastIntentManager im = McastIntentManager.getInstance(); + im.setIntent(entry); + + entry.incrementPuntCount(); + + // Send the pack out each of the egress devices & port + forwardPacketToDst(context, entry); + } + } + + /** + * Forward the packet to it's multicast destinations. + * + * @param context The packet context + * @param entry The multicast route entry matching this packet + */ + private void forwardPacketToDst(PacketContext context, McastRoute entry) { + + // Send the pack out each of the respective egress ports + for (ConnectPoint egress : entry.getEgressConnectPoints()) { + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(egress.port()).build(); + + OutboundPacket packet = new DefaultOutboundPacket( + egress.deviceId(), + treatment, + context.inPacket().unparsed()); + + packetService.emit(packet); + } + } +} diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastIntentManager.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastIntentManager.java new file mode 100644 index 00000000..90f65c94 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastIntentManager.java @@ -0,0 +1,133 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.mfwd.impl; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.onlab.packet.Ethernet; +import org.onosproject.net.intent.Intent; +import org.onosproject.net.intent.SinglePointToMultiPointIntent; +import org.onosproject.net.intent.IntentService; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; + +@Component(immediate = true) +@Service(value = org.onosproject.mfwd.impl.McastIntentManager.class) +public class McastIntentManager { + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected IntentService intentService; + + private static McastIntentManager instance; + + public McastIntentManager() { + instance = this; + } + + /** + * Active this component. + */ + @Activate + public void activate() { } + + /** + * Deactivate this component. + */ + @Deactivate + public void deactivate() { + withdrawAllIntents(); + } + + /** + * Get instance of this intentManager. + * + * @return the instance of this intent manager. + */ + public static McastIntentManager getInstance() { + if (instance == null) { + instance = new McastIntentManager(); + } + return instance; + } + + /** + * Install the PointToMultipoint forwarding intent. + * + * @param mroute multicast route entry + * @return the intent that has been set or null otherwise + */ + public SinglePointToMultiPointIntent setIntent(McastRoute mroute) { + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment(); + + if (mroute.getIngressPoint() == null || + mroute.getEgressPoints().isEmpty()) { + return null; + } + + /* + * Match the group AND source addresses. We will also check ether type to + * determine if we are doing ipv4 or ipv6. + * + * If we really wanted to be pendantic we could put in a + * condition to make sure the ethernet MAC address was also + * mcast. + */ + selector.matchEthType(Ethernet.TYPE_IPV4) + .matchIPDst(mroute.getGaddr()) + .matchIPSrc(mroute.getSaddr()); + + SinglePointToMultiPointIntent intent = + SinglePointToMultiPointIntent.builder() + .appId(McastForwarding.getAppId()) + .selector(selector.build()) + .treatment(treatment) + .ingressPoint(mroute.getIngressPoint().getConnectPoint()) + .egressPoints(mroute.getEgressConnectPoints()). + build(); + + intentService.submit(intent); + return intent; + } + + /** + * Withdraw the intent represented by this route. + * + * @param mroute the mcast route whose intent we want to remove + */ + public void withdrawIntent(McastRouteBase mroute) { + Intent intent = intentService.getIntent(mroute.getIntentKey()); + intentService.withdraw(intent); + } + + /** + * Withdraw all intents. + * + * This will be called from the deactivate method so we don't leave + * a mess behind us after we leave. + */ + public void withdrawAllIntents() { + for (Intent intent : intentService.getIntents()) { + intentService.withdraw(intent); + } + } +} diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRoute.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRoute.java new file mode 100644 index 00000000..12b7e6d4 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRoute.java @@ -0,0 +1,190 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.mfwd.impl; + +import org.onlab.packet.IpPrefix; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.intent.Key; +import org.onosproject.net.intent.SinglePointToMultiPointIntent; + +import java.util.Set; + +/** + * This McastRouteBase interface is implemented by the McastRouteBase class which + * in turn acts as the base class for both the McastRouteGroup and McastRouteSource. + */ +interface McastRoute { + + /** + * Gets the group addresses. + * + * @return group address + */ + public IpPrefix getGaddr(); + + /** + * Gets the source address. + * + * @return the source address + */ + public IpPrefix getSaddr(); + + /** + * Determines if this is an IPv4 multicast route. + * + * @return true if it is an IPv4 route + */ + public boolean isIp4(); + + /** + * Determines if this is an IPv6 multicast route. + * + * @return true if it is an IPv6 route + */ + public boolean isIp6(); + + /** + * Add the ingress ConnectPoint. + * + * @param cpstr string representing a ConnectPoint + * @return whether ingress has been added, only add if ingressPoint is null + */ + public boolean addIngressPoint(String cpstr); + + /** + * Add the ingress ConnectPoint. + * + * @param cp the ConnectPoint of incoming traffic. + * @return whether ingress has been added, only add if ingressPoint is null + */ + public boolean addIngressPoint(ConnectPoint cp); + + /** + * Get the ingress connect point. + * + * @return the ingress connect point + */ + public McastConnectPoint getIngressPoint(); + + /** + * Add an egress connect point. + * + * @param cp the egress McastConnectPoint to be added + * @return return the McastConnectPoint + */ + public McastConnectPoint addEgressPoint(ConnectPoint cp); + + /** + * Add an egress connect point. + * + * @param connectPoint deviceId/portNum + * @return return the McastConnectPoint + */ + public McastConnectPoint addEgressPoint(String connectPoint); + + /** + * Add an egress connect point. + * + * @param cp the egress McastConnectPoint to be added + * @param interest the protocol that has shown interest in this route + * @return return the McastConnectPoint + */ + public McastConnectPoint addEgressPoint(ConnectPoint cp, McastConnectPoint.JoinSource interest); + + /** + * Add an egress connect point. + * + * @param connectPoint deviceId/portNum + * @param interest the protocol that has shown interest in this route + * @return return the McastConnectPoint + */ + public McastConnectPoint addEgressPoint(String connectPoint, McastConnectPoint.JoinSource interest); + + /** + * Get the egress connect points. + * + * @return a set of egress connect points + */ + public Set<McastConnectPoint> getEgressPoints(); + + /** + * Get the egress connect points. + * + * @return a set of egress connect points + */ + public Set<ConnectPoint> getEgressConnectPoints(); + + /** + * Find the egress connect point if it exists. + * + * @param cp ConnectPoint to search for + * @return the connect point when found, null otherwise. + */ + public McastConnectPoint findEgressConnectPoint(ConnectPoint cp); + + /** + * remove Interest from a McastConnectPoint. + * + * @param mcp connect point. + * @param interest the protocol interested in this multicast stream + * @return whether or not interest was removed + */ + public boolean removeInterest(McastConnectPoint mcp, McastConnectPoint.JoinSource interest); + + /** + * Increment the punt count. + */ + public void incrementPuntCount(); + + /** + * Get the punt count. + * + * @return the punt count + */ + public int getPuntCount(); + + /** + * Have the McastIntentManager create an intent, attempt to + * install the intent and then save the key. + */ + public void setIntent(); + + /** + * Set the Intent key. + * + * @param intent intent + */ + public void setIntent(SinglePointToMultiPointIntent intent); + + /** + * Withdraw the intent if it has been installed. + */ + public void withdrawIntent(); + + /** + * Get the intent key. + * + * @return the intentKey + */ + public Key getIntentKey(); + + /** + * Pretty print the the route. + * + * @return a pretty string + */ + public String toString(); +}
\ No newline at end of file diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java new file mode 100644 index 00000000..730acfa7 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteBase.java @@ -0,0 +1,431 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.mfwd.impl; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.apache.commons.collections.set.ListOrderedSet; +import org.onlab.packet.IpPrefix; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.intent.SinglePointToMultiPointIntent; +import org.onosproject.net.intent.Key; + +import java.util.Set; +import java.util.HashSet; + +/** + * McastRouteBase base class for McastRouteGroup and McastRouteSource. + */ +public class McastRouteBase implements McastRoute { + protected final IpPrefix gaddr; + protected final IpPrefix saddr; + + protected McastConnectPoint ingressPoint; + protected Set<McastConnectPoint> egressPoints; + + protected boolean isGroup = false; + + protected boolean dirty = false; + + /** + * How may times has this packet been punted. + */ + private int puntCount = 0; + + /** + * If the intentKey is null that means no intent has + * been installed. + */ + protected Key intentKey = null; + + /** + * Create a multicast route. This is the parent class for both the Group + * and the source. + * + * @param saddr source address + * @param gaddr multicast group address + */ + public McastRouteBase(String saddr, String gaddr) { + this.gaddr = IpPrefix.valueOf(checkNotNull(gaddr)); + if (saddr == null || saddr.equals("*")) { + this.saddr = IpPrefix.valueOf(0, 0); + } else { + this.saddr = IpPrefix.valueOf(checkNotNull(gaddr)); + } + this.init(); + } + + /** + * Create a multicast group table entry. + * @param gaddr multicast group address + */ + public McastRouteBase(String gaddr) { + this("*", gaddr); + } + + /** + * Set the source and group address value of a (*, G) group. + * + * @param gpfx the group prefix address + */ + public McastRouteBase(IpPrefix gpfx) { + this(IpPrefix.valueOf(0, 0), gpfx); + } + + /** + * Create a multicast route constructor. + * + * @param saddr source address + * @param gaddr group address + */ + public McastRouteBase(IpPrefix saddr, IpPrefix gaddr) { + this.saddr = checkNotNull(saddr); + this.gaddr = checkNotNull(gaddr); + + this.init(); + } + + private void init() { + this.isGroup = (this.saddr.prefixLength() == 0); + this.ingressPoint = null; + this.egressPoints = new HashSet(); + } + + /** + * Get the multicast group address. + * + * @return the multicast group address + */ + @Override + public IpPrefix getGaddr() { + return gaddr; + } + + /** + * Get the multicast source address. + * + * @return the multicast source address + */ + @Override + public IpPrefix getSaddr() { + return saddr; + } + + /** + * Is this an IPv4 multicast route. + * + * @return true if it is an IPv4 route + */ + @Override + public boolean isIp4() { + return gaddr.isIp4(); + } + + /** + * Is this an IPv6 multicast route. + * + * @return true if it is an IPv6 route + */ + @Override + public boolean isIp6() { + return gaddr.isIp6(); + } + + /** + * Is this a multicast group route? + * + * @return true if it is a multicast group route. + */ + public boolean isGroup() { + return isGroup; + } + + /** + * @return true if this is (S, G) false if it (*, G). + */ + public boolean isSource() { + return (!isGroup); + } + + /** + * Get the dirty state. + * + * @return whether this route is dirty or not. + */ + public boolean getDirty() { + return this.dirty; + } + + /** + * Set the dirty state to indicate that something changed. + * This may require an update to the flow tables (intents). + * + * @param dirty set the dirty bit + */ + public void setDirty(boolean dirty) { + this.dirty = dirty; + } + + /** + * Add an ingress point to this route. + * + * @param ingress incoming connect point + * @return whether ingress has been added, only add if ingressPoint is null + */ + public boolean addIngressPoint(ConnectPoint ingress) { + + // Do NOT add the ingressPoint if it is not null. + if (this.ingressPoint != null) { + // TODO: Log an warning. + return false; + } + this.ingressPoint = new McastConnectPoint(checkNotNull(ingress)); + setDirty(true); + return true; + } + + /** + * Add or modify the ingress connect point. + * + * @param connectPoint string switch device Id + * @return whether ingress has been added, only add if ingressPoint is null + */ + public boolean addIngressPoint(String connectPoint) { + + if (this.ingressPoint != null) { + // TODO: log a warning. + return false; + } + ConnectPoint cp = ConnectPoint.deviceConnectPoint(checkNotNull(connectPoint)); + return this.addIngressPoint(cp); + } + + /** + * Get the ingress McastConnectPoint. + * + * @return the ingress McastConnectPoint + */ + public McastConnectPoint getIngressPoint() { + return this.ingressPoint; + } + + /** + * Add an egress McastConnectPoint. + * + * @param cp egress connect point + * @return return the McastConnectPoint + */ + public McastConnectPoint addEgressPoint(ConnectPoint cp) { + McastConnectPoint mcp = this.findEgressConnectPoint(cp); + if (mcp == null) { + mcp = new McastConnectPoint(checkNotNull(cp)); + egressPoints.add(mcp); + setDirty(true); + } + return mcp; + } + + /** + * Add an egress connect point from a string. + * + * @param connectPoint string representing a connect point + * @return the MulticastConnectPoint + */ + public McastConnectPoint addEgressPoint(String connectPoint) { + checkNotNull(connectPoint); + return this.addEgressPoint(ConnectPoint.deviceConnectPoint(connectPoint)); + } + + /** + * Add an egress McastConnectPoint. + * + * @param cp the egress connect point + * @param interest the source of interest for mcast traffic + */ + public McastConnectPoint addEgressPoint(ConnectPoint cp, McastConnectPoint.JoinSource interest) { + checkNotNull(cp); + checkNotNull(interest); + McastConnectPoint mcp = this.addEgressPoint(cp); + if (mcp != null) { + mcp.interest.add(interest); + setDirty(true); + } + return mcp; + } + + /** + * Add an egress McastConnectPoint. + * + * @param cpstr deviceId/port of the connect point + */ + public McastConnectPoint addEgressPoint(String cpstr, McastConnectPoint.JoinSource interest) { + checkNotNull(cpstr); + checkNotNull(interest); + return this.addEgressPoint(ConnectPoint.deviceConnectPoint(cpstr), interest); + } + + /** + * Get egress connect points for the route. + * + * @return Set of egress connect points + */ + public Set<McastConnectPoint> getEgressPoints() { + return egressPoints; + } + + /** + * Get egress McastConnectPoints points as ConnectPoints for intent system. + * + * @return Set of egress ConnectPoints + */ + public Set<ConnectPoint> getEgressConnectPoints() { + Set<ConnectPoint> cps = new ListOrderedSet(); + + for (McastConnectPoint mcp : egressPoints) { + cps.add(mcp.getConnectPoint()); + } + return cps; + } + + /** + * Find the Multicast Connect Point that contains the ConnectPoint. + * + * @param cp the regular ConnectPoint to match + * @return the McastConnectPoint that contains cp or null if not found. + */ + public McastConnectPoint findEgressConnectPoint(ConnectPoint cp) { + for (McastConnectPoint mcp : this.egressPoints) { + if (mcp.getConnectPoint().equals(cp)) { + return mcp; + } + } + return null; + } + + /** + * Remove specified interest from the given ConnectPoint. + * + * @param mcp connect point. + * @param interest the protocol interested in this multicast stream + * @return true if removed, false otherwise + */ + public boolean removeInterest(McastConnectPoint mcp, McastConnectPoint.JoinSource interest) { + checkNotNull(mcp); + if (mcp.interest.contains(interest)) { + mcp.interest.remove(interest); + setDirty(true); + return true; + } + return false; + } + + /** + * Get the number of times the packet has been punted. + * + * @return the punt count + */ + @Override + public int getPuntCount() { + return puntCount; + } + + /** + * Increment the punt count. + * + * TODO: we need to handle wrapping. + */ + @Override + public void incrementPuntCount() { + puntCount++; + } + + /** + * Have the McastIntentManager create and set the intent, then save the intent key. + * + * If we already have an intent, we will first withdraw the existing intent and + * replace it with a new one. This will support the case where the ingress connectPoint + * or group of egress connectPoints change. + */ + @Override + public void setIntent() { + if (this.intentKey != null) { + this.withdrawIntent(); + } + McastIntentManager im = McastIntentManager.getInstance(); + SinglePointToMultiPointIntent intent = im.setIntent(this); + this.intentKey = intent.key(); + } + + /** + * Set the Intent key. + * + * @param intent the multicast intent + */ + @Override + public void setIntent(SinglePointToMultiPointIntent intent) { + intentKey = intent.key(); + } + + /** + * Get the intent key represented by this route. + * + * @return intentKey + */ + @Override + public Key getIntentKey() { + return this.intentKey; + } + + + /** + * Withdraw the intent and set the key to null. + */ + @Override + public void withdrawIntent() { + if (intentKey == null) { + // nothing to withdraw + return; + } + McastIntentManager im = McastIntentManager.getInstance(); + im.withdrawIntent(this); + this.intentKey = null; + } + + /** + * Pretty Print this Multicast Route. Works for McastRouteSource and McastRouteGroup. + * + * @return pretty string of the multicast route + */ + @Override + public String toString() { + String out = String.format("(%s, %s)\n\t", + saddr.toString(), gaddr.toString()); + + out += "intent: "; + out += (intentKey == null) ? "not installed" : this.intentKey.toString(); + out += "\n\tingress: "; + out += (ingressPoint == null) ? "NULL" : ingressPoint.toString(); + out += "\n\tegress: {\n"; + if (egressPoints != null && !egressPoints.isEmpty()) { + for (McastConnectPoint eg : egressPoints) { + out += "\t\t" + eg.getConnectPoint().toString() + "\n"; + } + } + out += ("\t}\n"); + out += ("\tpunted: " + this.getPuntCount() + "\n"); + return out; + } +} diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteGroup.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteGroup.java new file mode 100644 index 00000000..4a58e1b1 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteGroup.java @@ -0,0 +1,110 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.mfwd.impl; + +import static com.google.common.base.Preconditions.checkNotNull; +import java.util.HashMap; +import org.onlab.packet.IpPrefix; + +/** + * The McastRouteGroup extends the McastRouteBase class and serves two purposes: + * first it represents a (*, G) multicast route entry. Second it serves + * as a container for all (S, G) multicast route entries that belong + * to the same group address. + */ +public class McastRouteGroup extends McastRouteBase { + private HashMap<IpPrefix, McastRouteSource> sources; + + /** + * Class constructor. + * + * @param gaddr - String representation of group address. + */ + public McastRouteGroup(String gaddr) { + super(checkNotNull(gaddr)); + this.init(); + } + + /** + * Create a multicast group. + * + * @param gpfx - Group address + */ + public McastRouteGroup(IpPrefix gpfx) { + super(checkNotNull(gpfx)); + this.init(); + } + + /** + * Common initialization used by constructors. + */ + private void init() { + this.sources = new HashMap(); + super.isGroup = true; + } + + /** + * Find a specific multicast source address for this group. + * + * @param saddr the source address + * @return the multicast source route or null if it does not exist + */ + public McastRouteSource findSource(IpPrefix saddr) { + return this.sources.get(checkNotNull(saddr)); + } + + /** + * Return the entire set of multicast sources for this group. + * + * @return the set of multicast sources + */ + public HashMap<IpPrefix, McastRouteSource> getSources() { + return this.sources; + } + + /** + * Add a new McastRouteSource to this group. + * + * @param src the multicast source + */ + public void addSource(McastRouteSource src) { + checkNotNull(src); + this.sources.put(src.getSaddr(), src); + } + + /** + * Remove the source with this specific IpPrefix from this group entry. + * + * @param spfx IP Prefix of the source to be removed + * @return the source route that was just removed + */ + public McastRouteSource removeSource(IpPrefix spfx) { + McastRouteSource src = this.sources.remove(spfx); + src.withdrawIntent(); + return src; + } + + /** + * Remove all sources from this. + */ + public void removeSources() { + for (McastRouteSource src : this.sources.values()) { + src.withdrawIntent(); + this.sources.remove(src.getSaddr()); + } + } + +} diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteSource.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteSource.java new file mode 100644 index 00000000..68edc2e0 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteSource.java @@ -0,0 +1,48 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.mfwd.impl; + +import static com.google.common.base.Preconditions.checkNotNull; +import org.onlab.packet.IpPrefix; + +/** + * This class represents and specific multicast senders source address. Objects from + * this class will belong to the sources collection of the multicast group. + */ +public class McastRouteSource extends McastRouteBase { + + // A reference to our parent group + private McastRouteGroup group; + + /** + * Create a multicast source with IpPrefixes. + * + * @param source the source address + * @param group the group address + */ + public McastRouteSource(IpPrefix source, IpPrefix group) { + super(checkNotNull(source), checkNotNull(group)); + } + + /** + * Set our parent multicast group. + * + * @param group the group this source belongs to + */ + public void setGroup(McastRouteGroup group) { + this.group = group; + } +}
\ No newline at end of file diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java new file mode 100644 index 00000000..5a07bec7 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/McastRouteTable.java @@ -0,0 +1,338 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.mfwd.impl; + +import org.apache.felix.scr.annotations.Service; +import org.onlab.packet.IpPrefix; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * The Mcast Route Table holds all multicast state for the controller. + * + * State for IPv4 and IPv6 are maintained. The tables are sets of McastRouteGroup + * structures that represent (*, G) state with a series of egress ConnectPoints. + * Each (*, G) may also have a set of (S, G) that may have there own set of + * ingress and egress ConnectPoints. + * + * TODO: perhaps should probably create two separate singleton for IPv4 and IPv6 respectively. + */ +@Service(value = org.onosproject.mfwd.impl.McastRouteTable.class) +public final class McastRouteTable { + + /* + * Create a map of the McastGroups indexed by the multicast group prefix. + * We may choose to change the map data structure in to some form a radix trie + * depending on the type of real world usage we see. + */ + private final Map<IpPrefix, McastRouteGroup> mrib4; + private final Map<IpPrefix, McastRouteGroup> mrib6; + private static McastRouteTable instance = null; + + private Boolean ipv6Enabled = false; + + /** + * Create the two v4 & v6 tables. + */ + private McastRouteTable() { + mrib4 = new ConcurrentHashMap<IpPrefix, McastRouteGroup>(); + if (ipv6Enabled) { + mrib6 = new ConcurrentHashMap<IpPrefix, McastRouteGroup>(); + } else { + mrib6 = null; + } + } + + /** + * Get the single instance of this multicast group address. + * + * @return the multicast route table + */ + public static McastRouteTable getInstance() { + if (instance == null) { + instance = new McastRouteTable(); + } + return instance; + } + + /** + * Get the IPv4 MRIB. + * + * @return the IPv4 MRIB + */ + public Map<IpPrefix, McastRouteGroup> getMrib4() { + return mrib4; + } + + /** + * Get the IPv6 MRIB. + * + * @return Return the set of prefix keyed McastGroups + */ + public Map<IpPrefix, McastRouteGroup> getMrib6() { + return mrib6; + } + + /** + * Save the McastRouteGroup in the address family appropriate mrib. + * + * @param group The McastRouteGroup to save + */ + private void storeGroup(McastRouteGroup group) { + if (group.isIp4()) { + mrib4.put(group.getGaddr(), group); + } else if (group.isIp6() && ipv6Enabled) { + mrib6.put(group.getGaddr(), group); + } + } + + /** + * Remove the group. + * + * @param group the group to be removed + */ + private void removeGroup(McastRouteGroup group) { + IpPrefix gpfx = group.getGaddr(); + if (gpfx.isIp4()) { + mrib4.remove(gpfx); + } else if (gpfx.isIp6() && ipv6Enabled) { + mrib6.remove(gpfx); + } + } + + /** + * Add a multicast route to the MRIB. This function will. + * + * @param saddr source address * or x.x.x.x or x.x.x.x/y + * @param gaddr group address x.x.x.x or x.x.x.x/y + * @return the multicast route + */ + public McastRouteBase addRoute(String saddr, String gaddr) { + IpPrefix gpfx = IpPrefix.valueOf(gaddr); + IpPrefix spfx = IpPrefix.valueOf(0, 0); + if (saddr != null && !saddr.equals("*")) { + spfx = IpPrefix.valueOf(saddr); + } + return addRoute(spfx, gpfx); + } + + /** + * Add a multicast route to the MRIB. This function will store either + * (S, G) or (*, G) in the mrib if an entry does not already exist. If + * an entry does exist it is returned to the caller. + * + * Every (S, G) is stored as part of it's parent group entry which also represents + * (*, G) routes. In the case of a (S, G) we will also create the (*, G) entry if needed + * then save the (S, G) to the (*, G). + * + * @param spfx the source prefix + * @param gpfx the group prefix + * @return the resulting McastRouteSource or McastRouteGroup accordingly. + */ + public McastRouteBase addRoute(IpPrefix spfx, IpPrefix gpfx) { + + /** + * If a group route (*, g) does not exist we will need to make so we + * can start attaching our sources to the group entry. + */ + McastRouteGroup group = findMcastGroup(gpfx); + if (group == null) { + group = new McastRouteGroup(gpfx); + + // Save it for later + if (gpfx.isIp4()) { + this.mrib4.put(gpfx, group); + } else if (gpfx.isIp6() && ipv6Enabled) { + this.mrib6.put(gpfx, group); + } + } + + /** + * If the source prefix length is 0 then we have our (*, g) entry, we can + * just return now. + */ + if (spfx.prefixLength() == 0) { + return group; + } + + // See if the source already exists. If so just return it. + McastRouteSource source = group.findSource(spfx); + if (source != null) { + return source; + } + + /** + * We have the group but no source. We need to create the source then add it + * to the group. + */ + source = new McastRouteSource(spfx, gpfx); + + // Have the source save it's parent + source.setGroup(group); + + // Save this source as part of this group + group.addSource(source); + + return source; + } + + /** + * Delete a multicast route from the MRIB. + * + * @param saddr source address * or x.x.x.x or x.x.x.x/y + * @param gaddr group address x.x.x.x or x.x.x.x/y + */ + public void removeRoute(String saddr, String gaddr) { + IpPrefix gpfx = IpPrefix.valueOf(gaddr); + IpPrefix spfx = IpPrefix.valueOf(0, 0); + if (saddr != null && !saddr.equals("*")) { + spfx = IpPrefix.valueOf(saddr); + } + removeRoute(spfx, gpfx); + } + + /** + * Remove a multicast route. + * + * @param spfx the source prefix + * @param gpfx the group prefix + */ + public void removeRoute(IpPrefix spfx, IpPrefix gpfx) { + + /** + * If a group route (*, g) does not exist we will need to make so we + * can start attaching our sources to the group entry. + */ + McastRouteGroup group = findMcastGroup(gpfx); + if (group == null) { + // The group does not exist, we can't remove it. + return; + } + + /** + * If the source prefix length is 0 then we have a (*, g) entry, which + * means we will remove this group and all of it's sources. We will + * also withdraw it's intent if need be. + */ + if (spfx.prefixLength() > 0) { + group.removeSource(spfx); + + /* + * Now a little house keeping. If this group has no more sources + * nor egress connectPoints git rid of it. + */ + if (group.getSources().size() == 0 && + group.getEgressPoints().size() == 0) { + removeGroup(group); + } + + } else { + // Group remove has been explicitly requested. + group.removeSources(); + group.withdrawIntent(); + removeGroup(group); + } + } + + /** + * Find the specific multicast group entry. + * + * @param group the group address + * @return McastRouteGroup the multicast (*, G) group route + */ + public McastRouteGroup findMcastGroup(IpPrefix group) { + McastRouteGroup g = null; + if (group.isIp4()) { + g = mrib4.get(group); + } else if (group.isIp6() && ipv6Enabled) { + g = mrib6.get(group); + } + return g; + } + + /** + * Find the multicast (S, G) entry if it exists. + * + * @param saddr the source address + * @param gaddr the group address + * @return The multicast source route entry if it exists, null if it does not. + */ + public McastRouteSource findMcastSource(IpPrefix saddr, IpPrefix gaddr) { + McastRouteGroup grp = findMcastGroup(checkNotNull(gaddr)); + if (grp == null) { + return null; + } + return grp.findSource(saddr); + } + + /** + * This will first look up a Group entry. If no group entry was found null will + * be returned. If the group entry has been found we will then look up the (s, g) entry. + * If the (s, g) entry has been found, that will be returned. If no (s, g) was found + * the (*, g) group entry will be returned. + * + * @param saddr the source address + * @param gaddr the group address + * @return return the best matching McastRouteSource or McastRouteGroup + */ + public McastRoute findBestMatch(IpPrefix saddr, IpPrefix gaddr) { + McastRouteGroup grp = this.findMcastGroup(checkNotNull(gaddr)); + if (grp == null) { + return null; + } + + // Found a group now look for a source + McastRouteSource src = grp.findSource(checkNotNull(saddr)); + if (src == null) { + return grp; + } + + return src; + } + + /** + * Print out the multicast route table in it's entirety. + * + * TODO: Eventually we will have to implement paging and how to handle large tables. + * @return String + */ + public String printMcastRouteTable() { + String out = this.toString() + "\n"; + + for (McastRouteGroup grp : mrib4.values()) { + out += grp.toString() + "\n"; + for (McastRouteSource src : grp.getSources().values()) { + out += src.toString() + "\n"; + } + } + return out; + } + + /** + * Print out a summary of groups in the MRIB. + * + * @return String + */ + public String toString() { + String out = "Mcast Route Table: "; + out += mrib4.size() + " IPv4 Multicast Groups\n"; + if (ipv6Enabled) { + out += mrib6.size() + " IPv6 Multicast Groups\n"; + } + return out; + } +} diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/package-info.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/package-info.java new file mode 100644 index 00000000..eaef5fcf --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/impl/package-info.java @@ -0,0 +1,4 @@ +/** + * Sample Multicast forwarding framework using intents. + */ +package org.onosproject.mfwd.impl; diff --git a/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/rest/McastResource.java b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/rest/McastResource.java new file mode 100644 index 00000000..608e0447 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/java/org/onosproject/mfwd/rest/McastResource.java @@ -0,0 +1,149 @@ +/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.mfwd.rest;
+
+import java.io.IOException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.onosproject.mfwd.impl.McastConnectPoint;
+import org.onosproject.mfwd.impl.McastRouteTable;
+import org.onosproject.mfwd.impl.McastRouteBase;
+import org.onosproject.mfwd.impl.MRibCodec;
+import org.onosproject.rest.AbstractWebResource;
+
+import org.slf4j.Logger;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Rest API for Multicast Forwarding.
+ */
+@Path("mcast")
+public class McastResource extends AbstractWebResource {
+
+ private final Logger log = getLogger(getClass());
+ private static final String SOURCE_ADDRESS = "sourceAddress";
+ private static final String GROUP_ADDRESS = "groupAddress";
+ private static final String INGRESS_POINT = "ingressPoint";
+ private static final String EGRESS_POINT = "egressPoint";
+ private static final String MCAST_GROUP = "mcastGroup";
+
+ /**
+ * Retrieve the multicast route table.
+ *
+ * @return the multicast route table.
+ * @throws IOException if an error occurs
+ */
+ @Path("show")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response showAll() throws IOException {
+ McastRouteTable mrt = McastRouteTable.getInstance();
+ ObjectNode pushContent = new MRibCodec().encode(mrt , this);
+ return ok(pushContent.toString()).build();
+ }
+
+ /**
+ * Static join a multicast flow.
+ *
+ * @param sAddr source address to join
+ * @param gAddr group address to join
+ * @param ports ingress and egress ConnectPoints to join
+ * @return the Result of the join
+ * @throws IOException if something failed with the join command
+ */
+ @Path("/join")
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response join(@QueryParam("src") String sAddr,
+ @QueryParam("grp") String gAddr,
+ @DefaultValue("") @QueryParam("ports") String ports)
+ throws IOException {
+
+ ObjectMapper mapper = new ObjectMapper();
+ log.debug("Source IP Address: " + sAddr);
+ log.debug("Destination IP Address: " + gAddr);
+ log.debug("Ingress and Egress ports: " + ports);
+
+ String output = "Insertion Faild";
+ if (sAddr != null && gAddr != null && ports != null) {
+
+ String[] portArr = ports.split(",");
+ log.debug("Port Array Length: " + portArr.length);
+ McastRouteTable mrt = McastRouteTable.getInstance();
+ McastRouteBase mr = mrt.addRoute(sAddr, gAddr);
+
+ // Port format "of:0000000000000023/4"
+ log.debug("checking inside outer if: " + portArr.length);
+
+ if (mr != null && portArr != null && portArr.length > 0) {
+
+ String inCP = portArr[0];
+ log.debug("Ingress port provided: " + inCP);
+ mr.addIngressPoint(inCP);
+
+ for (int i = 1; i < portArr.length; i++) {
+ String egCP = portArr[i];
+ log.debug("Egress port provided: " + egCP);
+ mr.addEgressPoint(egCP, McastConnectPoint.JoinSource.STATIC);
+ }
+ mrt.printMcastRouteTable();
+ output = "Successfully Inserted";
+ }
+ } else {
+ output = "Please Insert the rest uri correctly";
+ }
+ return Response.ok(output).build();
+ }
+
+ /**
+ * Delete multicast state.
+ *
+ * @param src address to be deleted
+ * @param grp address to be deleted
+ * @return status of delete if successful
+ */
+ @Path("/delete")
+ @DELETE
+ @Consumes(MediaType.TEXT_PLAIN)
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response removeMcastFlow(@QueryParam("src") String src,
+ @QueryParam("grp") String grp) {
+
+ String resp = "Failed to delete";
+ log.info("Source IP Address to delete: " + src);
+ log.info("Destination IP Address to delete: " + grp);
+ McastRouteTable mrt = McastRouteTable.getInstance();
+ if (src != null && grp != null) {
+ mrt.removeRoute(src, grp);
+ resp = "Deleted flow for src " + src + " and grp " + grp;
+ }
+
+ return Response.ok(resp).build();
+ }
+}
diff --git a/framework/src/onos/apps/mfwd/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/mfwd/src/main/resources/OSGI-INF/blueprint/shell-config.xml new file mode 100644 index 00000000..966cb4f2 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -0,0 +1,30 @@ +<!--
+ ~ Copyright 2014 Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.mfwd.cli.McastJoinCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.mfwd.cli.McastDeleteCommand"/>
+ </command>
+ <command>
+ <action class="org.onosproject.mfwd.cli.McastShowCommand"/>
+ </command>
+ </command-bundle>
+
+</blueprint>
diff --git a/framework/src/onos/apps/mfwd/src/main/webapp/WEB-INF/web.xml b/framework/src/onos/apps/mfwd/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..c4c4f459 --- /dev/null +++ b/framework/src/onos/apps/mfwd/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +~ Copyright 2014 Open Networking Laboratory +~ +~ Licensed under the Apache License, Version 2.0 (the "License"); +~ you may not use this file except in compliance with the License. +~ You may obtain a copy of the License at +~ +~ http://www.apache.org/licenses/LICENSE-2.0 +~ +~ Unless required by applicable law or agreed to in writing, software +~ distributed under the License is distributed on an "AS IS" BASIS, +~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +~ See the License for the specific language governing permissions and +~ limitations under the License. +--> +<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" + 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"> + <display-name>ONOS APP MFWD</display-name> + + <servlet> + <servlet-name>JAX-RS Service</servlet-name> + <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> + <init-param> + <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name> + <param-value>com.sun.jersey.api.core.ClassNamesResourceConfig</param-value> + </init-param> + <init-param> + <param-name>com.sun.jersey.config.property.classnames</param-name> + <param-value> + org.onosproject.mfwd.rest.McastResource + </param-value> + </init-param> + <load-on-startup>1</load-on-startup> + </servlet> + + <servlet-mapping> + <servlet-name>JAX-RS Service</servlet-name> + <url-pattern>/*</url-pattern> + </servlet-mapping> + +</web-app> diff --git a/framework/src/onos/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java b/framework/src/onos/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java index 85b5de27..8466b95e 100644 --- a/framework/src/onos/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java +++ b/framework/src/onos/apps/optical/src/main/java/org/onosproject/optical/OpticalPathProvisioner.java @@ -45,7 +45,8 @@ import org.onosproject.net.intent.IntentState; import org.onosproject.net.intent.OpticalCircuitIntent; import org.onosproject.net.intent.OpticalConnectivityIntent; import org.onosproject.net.intent.PointToPointIntent; -import org.onosproject.net.resource.device.DeviceResourceService; +import org.onosproject.net.newresource.ResourceService; +import org.onosproject.net.resource.device.IntentSetMultimap; import org.onosproject.net.resource.link.LinkResourceAllocations; import org.onosproject.net.resource.link.LinkResourceService; import org.onosproject.net.topology.LinkWeight; @@ -97,11 +98,14 @@ public class OpticalPathProvisioner { protected DeviceService deviceService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected DeviceResourceService deviceResourceService; + protected IntentSetMultimap intentSetMultimap; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected LinkResourceService linkResourceService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ResourceService resourceService; + private ApplicationId appId; private final InternalOpticalPathProvisioner pathProvisioner = new InternalOpticalPathProvisioner(); @@ -292,7 +296,6 @@ public class OpticalPathProvisioner { .bidirectional(true) .build(); intents.add(circuitIntent); - continue; } else if (srcPort instanceof OchPort && dstPort instanceof OchPort) { // Create lightpath // FIXME: hardcoded ODU signal type @@ -304,7 +307,6 @@ public class OpticalPathProvisioner { .bidirectional(true) .build(); intents.add(opticalIntent); - continue; } else { log.warn("Unsupported cross connect point types {} {}", srcPort.type(), dstPort.type()); return Collections.emptyList(); @@ -377,13 +379,13 @@ public class OpticalPathProvisioner { private void releaseResources(Intent intent) { LinkResourceAllocations lra = linkResourceService.getAllocations(intent.id()); if (intent instanceof OpticalConnectivityIntent) { - deviceResourceService.releasePorts(intent.id()); + resourceService.release(intent.id()); if (lra != null) { linkResourceService.releaseResources(lra); } } else if (intent instanceof OpticalCircuitIntent) { - deviceResourceService.releasePorts(intent.id()); - deviceResourceService.releaseMapping(intent.id()); + resourceService.release(intent.id()); + intentSetMultimap.releaseMapping(intent.id()); if (lra != null) { linkResourceService.releaseResources(lra); } diff --git a/framework/src/onos/apps/pim/pom.xml b/framework/src/onos/apps/pim/pom.xml new file mode 100644 index 00000000..83a366b6 --- /dev/null +++ b/framework/src/onos/apps/pim/pom.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-apps</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>onos-app-pim</artifactId> + <packaging>bundle</packaging> + + <description>Protocol Independent Multicast Emulation</description> + + <properties> + <onos.app.name>org.onosproject.pim</onos.app.name> + </properties> + + <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-cli</artifactId> + <version>${project.version}</version> + </dependency> + + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-osgi</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-junit</artifactId> + <scope>test</scope> + </dependency> + + <!-- This is needed by ComponentContext, used for tunable configuration --> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.karaf.shell</groupId> + <artifactId>org.apache.karaf.shell.console</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + <version>1.9.8</version> + <scope>provided</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-SymbolicName> + ${project.groupId}.${project.artifactId} + </Bundle-SymbolicName> + <Import-Package> + org.slf4j, + org.osgi.framework, + org.apache.commons.lang.math.*, + com.google.common.*, + org.apache.karaf.shell.commands, + org.apache.karaf.shell.console, + org.onlab.packet.*, + org.onlab.rest.*, + org.onosproject.*, + org.onosproject.mfwd.impl.*; + org.onlab.util.*, + org.jboss.netty.util.* + </Import-Package> + </instructions> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>2.5.1</version> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java new file mode 100644 index 00000000..0ef7e389 --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/PIMShowCommand.java @@ -0,0 +1,47 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.pim.cli; + +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.net.ConnectPoint; +import org.onosproject.pim.impl.PIMNeighbors; +import org.onosproject.pim.impl.PIMNeighborsCodec; + +import java.util.HashMap; + +@Command(scope = "onos", name = "pim-neighbors", description = "Displays the pim neighbors") +public class PIMShowCommand extends AbstractShellCommand { + + // prints either the json or cli version of the hash map connect point + // neighbors from the PIMNeighbors class. + @Override + protected void execute() { + // grab connect point neighbors hash map to send in to json encoder. + HashMap<ConnectPoint, PIMNeighbors> pimNbrs = PIMNeighbors.getConnectPointNeighbors(); + if (outputJson()) { + print("%s", json(pimNbrs)); + } else { + print(PIMNeighbors.printPimNeighbors()); + } + } + + private JsonNode json(HashMap<ConnectPoint, PIMNeighbors> pimNbrs) { + return new PIMNeighborsCodec().encode(pimNbrs, this); + } + +}
\ No newline at end of file diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java new file mode 100644 index 00000000..954dacbb --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/cli/package-info.java @@ -0,0 +1,4 @@ +/** + * PIM Multicast forwarding framework using intents. + */ +package org.onosproject.pim.cli;
\ No newline at end of file diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java new file mode 100644 index 00000000..bd5e1486 --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMComponent.java @@ -0,0 +1,153 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.pim.impl; + +import static org.slf4j.LoggerFactory.getLogger; + +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.onlab.packet.Ethernet; +import org.onlab.packet.IPv4; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.PIM; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.packet.InboundPacket; +import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketPriority; +import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketService; +import org.slf4j.Logger; + +/** + * Protocol Independent Multicast Emulation. + */ +@Component(immediate = true) +public class PIMComponent { + private final Logger log = getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected PacketService packetService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + private PIMPacketProcessor processor = new PIMPacketProcessor(); + private static ApplicationId appId; + + @Activate + public void activate() { + appId = coreService.registerApplication("org.onosproject.pim"); + + packetService.addProcessor(processor, PacketProcessor.director(1)); + + // Build a traffic selector for all multicast traffic + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + selector.matchEthType(Ethernet.TYPE_IPV4); + selector.matchIPProtocol(IPv4.PROTOCOL_PIM); + packetService.requestPackets(selector.build(), PacketPriority.REACTIVE, appId); + + log.info("Started"); + } + + @Deactivate + public void deactivate() { + packetService.removeProcessor(processor); + processor = null; + log.info("Stopped"); + } + + /** + * Packet processor responsible for handling IGMP packets. + */ + private class PIMPacketProcessor implements PacketProcessor { + + @Override + public void process(PacketContext context) { + // Stop processing if the packet has been handled, since we + // can't do any more to it. + if (context.isHandled()) { + return; + } + + InboundPacket pkt = context.inPacket(); + if (pkt == null) { + return; + } + + Ethernet ethPkt = pkt.parsed(); + if (ethPkt == null) { + return; + } + + /* + * IPv6 MLD packets are handled by ICMP6. We'll only deal + * with IPv4. + */ + if (ethPkt.getEtherType() != Ethernet.TYPE_IPV4) { + return; + } + + IPv4 ip = (IPv4) ethPkt.getPayload(); + IpAddress gaddr = IpAddress.valueOf(ip.getDestinationAddress()); + IpAddress saddr = Ip4Address.valueOf(ip.getSourceAddress()); + log.debug("Packet (" + saddr.toString() + ", " + gaddr.toString() + + "\tingress port: " + context.inPacket().receivedFrom().toString()); + + if (ip.getProtocol() != IPv4.PROTOCOL_PIM) { + log.debug("PIM Picked up a non PIM packet: IP protocol: " + ip.getProtocol()); + return; + } + + // TODO: check incoming to be PIM.PIM_ADDRESS or "Our" address. + IpPrefix spfx = IpPrefix.valueOf(saddr, 32); + IpPrefix gpfx = IpPrefix.valueOf(gaddr, 32); + + PIM pim = (PIM) ip.getPayload(); + switch (pim.getPimMsgType()) { + + case PIM.TYPE_HELLO: + PIMNeighbors.processHello(ethPkt, context.inPacket().receivedFrom()); + break; + + case PIM.TYPE_JOIN_PRUNE_REQUEST: + // Create the function + break; + + case PIM.TYPE_ASSERT: + case PIM.TYPE_BOOTSTRAP: + case PIM.TYPE_CANDIDATE_RP_ADV: + case PIM.TYPE_GRAFT: + case PIM.TYPE_GRAFT_ACK: + case PIM.TYPE_REGISTER: + case PIM.TYPE_REGISTER_STOP: + log.debug("Unsupported PIM message type: " + pim.getPimMsgType()); + break; + + default: + log.debug("Unkown PIM message type: " + pim.getPimMsgType()); + break; + } + } + } +} diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java new file mode 100644 index 00000000..1a96138f --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbor.java @@ -0,0 +1,340 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in reliance 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.pim.impl; + +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.TimerTask; +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; +import org.onlab.packet.pim.PIMHello; +import org.onlab.packet.pim.PIMHelloOption; +import org.onosproject.net.ConnectPoint; +import org.slf4j.Logger; + +import java.nio.ByteBuffer; +import java.util.concurrent.TimeUnit; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * PIMNeighbor represents all the PIM routers that have sent us + * hello messages, or that possibly have been statically configured. + */ +public class PIMNeighbor { + private final Logger log = getLogger(getClass()); + + // The primary address of this PIM neighbor + private IpAddress primaryAddr; + + // The MacAddress of this neighbor + private MacAddress macAddress; + + // The ConnectPoint this PIM neighbor is connected to. + private ConnectPoint connectPoint; + + // Is this neighbor us? + private boolean isThisUs = false; + + // The option values this neighbor has sent us. + private int priority = 0; + private int genId = 0; + private short holdtime = 0; + + // Is this pim neighbor the DR? + private boolean isDr = false; + + // Timeout for this neighbor + private volatile Timeout timeout; + + private boolean reelect = false; + + // A back pointer the neighbors list this neighbor belongs to. + private PIMNeighbors neighbors; + + /** + * Construct this neighbor from the address and connect point. + * + * @param ipaddr IP Address of neighbor + * @param macaddr MAC Address of the neighbor + * @param cp The ConnectPoint of this neighbor + */ + public PIMNeighbor(IpAddress ipaddr, MacAddress macaddr, ConnectPoint cp) { + this.macAddress = macaddr; + this.primaryAddr = ipaddr; + this.connectPoint = cp; + this.resetTimeout(); + } + + /** + * Get the primary address of this neighbor. + * + * @return the primary IP address. + */ + public IpAddress getPrimaryAddr() { + return primaryAddr; + } + + /** + * Set the primary address of this neighbor. + * + * @param primaryAddr the address we'll use when sending hello messages + */ + public void setPrimaryAddr(IpAddress primaryAddr) { + this.primaryAddr = primaryAddr; + } + + /** + * Get the priority this neighbor has advertised to us. + * + * @return the priority + */ + public int getPriority() { + return priority; + } + + /** + * Set the priority for this neighbor. + * + * @param priority This neighbors priority. + */ + public void setPriority(int priority) { + this.priority = priority; + } + + /** + * Get the generation ID. + * + * @return the generation ID. + */ + public int getGenId() { + return genId; + } + + /** + * Set the generation ID. + * + * @param genId the generation ID. + */ + public void setGenId(int genId) { + this.genId = genId; + } + + /** + * Get the holdtime for this neighbor. + * + * @return the holdtime + */ + public short getHoldtime() { + return holdtime; + } + + /** + * Set the holdtime for this neighbor. + * + * @param holdtime the holdtime. + */ + public void setholdtime(short holdtime) { + this.holdtime = holdtime; + } + + /** + * Is this neighbor the designated router on this connect point? + * + * @return true if so, false if not. + */ + public boolean isDr() { + return isDr; + } + + /** + * Set this router as the designated router on this connect point. + * + * @param isDr True is this neighbor is the DR false otherwise + */ + public void setIsDr(boolean isDr) { + this.isDr = isDr; + } + + /** + * The ConnectPoint this neighbor is connected to. + * + * @return the ConnectPoint + */ + public ConnectPoint getConnectPoint() { + return connectPoint; + } + + /** + * Set the ConnectPoint this router is connected to. + * + * @param connectPoint the ConnectPoint this router is connected to. + */ + public void setConnectPoint(ConnectPoint connectPoint) { + this.connectPoint = connectPoint; + } + + /** + * Set a back pointer to the neighbors list this neighbor is a member of. + * + * @param neighbors the neighbor list this neighbor belongs to + */ + public void setNeighbors(PIMNeighbors neighbors) { + this.neighbors = neighbors; + } + + /** + * We have received a fresh hello from a neighbor, now we need to process it. + * Depending on the values received in the the hello options may force a + * re-election process. + * + * We will also refresh the timeout for this neighbor. + * + * @param hello copy of the hello we'll be able to extract options from. + */ + public void refresh(PIMHello hello) { + checkNotNull(hello); + + for (PIMHelloOption opt : hello.getOptions().values()) { + + int len = opt.getOptLength(); + byte [] value = new byte[len]; + ByteBuffer bb = ByteBuffer.wrap(value); + + switch (opt.getOptType()) { + case PIMHelloOption.OPT_GENID: + int newid = bb.getInt(); + if (this.genId != newid) { + // TODO: we have a newly rebooted neighbor. Send them our joins. + this.genId = newid; + } + break; + + case PIMHelloOption.OPT_PRIORITY: + int newpri = bb.getInt(); + if (this.priority != newpri) { + + // The priorities have changed. We may need to re-elect a new DR? + if (this.isDr || this.neighbors.getDesignatedRouter().getPriority() < priority) { + reelect = true; + } + this.priority = newpri; + } + break; + + case PIMHelloOption.OPT_HOLDTIME: + short holdtime = bb.getShort(); + if (this.holdtime != holdtime) { + this.holdtime = holdtime; + if (holdtime == 0) { + // We have a neighbor going down. We can remove all joins + // we have learned from them. + // TODO: What else do we need to do when a neighbor goes down? + + log.debug("PIM Neighbor has timed out: {}", this.primaryAddr.toString()); + return; + } + } + break; + + case PIMHelloOption.OPT_PRUNEDELAY: + case PIMHelloOption.OPT_ADDRLIST: + // TODO: implement prune delay and addr list. Fall through for now. + + default: + log.debug("PIM Hello option type: {} not yet supported or unknown.", opt.getOptType()); + break; + } + } + + if (reelect) { + this.neighbors.electDR(this); + } + + // Reset the next timeout timer + this.resetTimeout(); + } + + /* --------------------------------------- Timer functions -------------------------- */ + + /** + * Restart the timeout task for this neighbor. + */ + private void resetTimeout() { + + if (this.holdtime == 0) { + + // Prepare to die. + log.debug("shutting down timer for nbr {}", this.primaryAddr.toString()); + if (this.timeout != null) { + this.timeout.cancel(); + this.timeout = null; + } + return; + } + + // Cancel the existing timeout and start a fresh new one. + if (this.timeout != null) { + this.timeout.cancel(); + } + + this.timeout = PIMTimer.getTimer().newTimeout(new NeighborTimeoutTask(this), holdtime, TimeUnit.SECONDS); + } + + /** + * The task to run when a neighbor timeout expires. + */ + private final class NeighborTimeoutTask implements TimerTask { + PIMNeighbor nbr; + + NeighborTimeoutTask(PIMNeighbor nbr) { + this.nbr = nbr; + } + + @Override + public void run(Timeout timeout) throws Exception { + + // TODO: log.debug; + PIMNeighbors neighbors = nbr.neighbors; + neighbors.removeNeighbor(nbr.getPrimaryAddr()); + } + } + + /** + * Stop the timeout timer. + * + * This happens when we remove the neighbor. + */ + private final void stopTimeout() { + this.timeout.cancel(); + this.timeout = null; + } + + @Override + public String toString() { + String out = ""; + if (this.isDr) { + out += "*NBR:"; + } else { + out += "NBR:"; + } + out += "\tIP: " + this.primaryAddr.toString(); + out += "\tPr: " + String.valueOf(this.priority); + out += "\tHoldTime: " + String.valueOf(this.holdtime); + out += "\tGenID: " + String.valueOf(this.genId) + "\n"; + return out; + } +}
\ No newline at end of file diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java new file mode 100644 index 00000000..cad90768 --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighbors.java @@ -0,0 +1,395 @@ + +package org.onosproject.pim.impl; + +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.TimerTask; +import org.onlab.packet.Ethernet; +import org.onlab.packet.IPv4; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; +import org.onlab.packet.PIM; +import org.onlab.packet.pim.PIMHello; +import org.onosproject.net.ConnectPoint; +import java.util.HashMap; +import java.util.concurrent.TimeUnit; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * PIMNeighbors is a collection of all neighbors we have received + * PIM hello messages from. The main structure is a HashMap indexed + * by ConnectPoint with another HashMap indexed on the PIM neighbors + * IPAddress, it contains all PIM neighbors attached on that ConnectPoint. + */ +public final class PIMNeighbors { + + private static Logger log = LoggerFactory.getLogger("PIMNeighbors"); + + /** + * This is the global container for all PIM neighbors indexed by ConnectPoints. + * + * NOTE: We'll have a problem if the same neighbor can show up on two interfaces + * but that should never happen. + */ + private static HashMap<ConnectPoint, PIMNeighbors> connectPointNeighbors = new HashMap<>(); + + // The connect point these neighbors are connected to. + private ConnectPoint connectPoint; + + // Pointer to the current designated router on this ConnectPoint. + private PIMNeighbor designatedRouter; + + // The list of neighbors we have learned on this ConnectPoint. + private HashMap<IpAddress, PIMNeighbor> neighbors = new HashMap<>(); + + /* + * TODO: turn ourIpAddress, ourPriority and OurHoldTime into config options. + */ + // The IP address we are using to source our PIM hello messages on this connect Point. + private IpAddress ourIpAddress; + + // The priority we use on this ConnectPoint. + private int ourPriority = 1; + + // The holdtime we are sending out. + private int ourHoldtime = 105; + + // Then generation ID we are sending out. 0 means we need to generate a new random ID + private int ourGenid = 0; + + // Hello Timer for sending hello messages per ConnectPoint with neighbors. + private volatile Timeout helloTimer; + + // The period of which we will be sending out PIM hello messages. + private final int defaultPimHelloInterval = 30; // seconds + + /** + * Create PIMNeighbors object per ConnectPoint. + * + * @param cp the ConnectPoint. + * @return PIMNeighbors structure + */ + public static PIMNeighbors getConnectPointNeighbors(ConnectPoint cp) { + return connectPointNeighbors.get(cp); + } + + /** + * Process incoming hello message, we will need the Macaddress and IP address of the sender. + * + * @param ethPkt the ethernet header + * @param receivedFrom the connect point we recieved this message from + */ + public static void processHello(Ethernet ethPkt, ConnectPoint receivedFrom) { + checkNotNull(ethPkt); + checkNotNull(ethPkt); + + MacAddress srcmac = ethPkt.getSourceMAC(); + IPv4 ip = (IPv4) ethPkt.getPayload(); + Ip4Address srcip = Ip4Address.valueOf(ip.getSourceAddress()); + + PIM pim = (PIM) ip.getPayload(); + checkNotNull(pim); + + PIMHello hello = (PIMHello) pim.getPayload(); + checkNotNull(hello); + + PIMNeighbor nbr = PIMNeighbors.findOrCreate(srcip, srcmac, receivedFrom); + if (nbr == null) { + log.error("Could not create a neighbor for: {1}", srcip.toString()); + return; + } + + nbr.setConnectPoint(receivedFrom); + nbr.refresh(hello); + } + + /** + * Create a PIM Neighbor. + * + * @param cp The ConnectPoint this neighbor was found on + */ + public PIMNeighbors(ConnectPoint cp) { + this.connectPoint = cp; + + // TODO: use network config to assign address. + this.ourIpAddress = IpAddress.valueOf("10.2.2.2"); + this.addIpAddress(this.ourIpAddress); + } + + /** + * Create a PIM neighbor. + * + * @param cp the ConnectPoint this neighbor was found on + * @param ourIp the IP address of this neighbor + */ + public PIMNeighbors(ConnectPoint cp, IpAddress ourIp) { + this.connectPoint = cp; + this.addIpAddress(ourIp); + } + + /** + * Start the hello timer when we have been given an IP address. + * + * @param ourIp our IP address. + */ + public void addIpAddress(IpAddress ourIp) { + this.startHelloTimer(); + + // Kick off the first pim hello packet + this.sendHelloPacket(); + } + + /** + * Getter for our IP address. + * + * @return our IP address. + */ + public IpAddress getOurIpAddress() { + return this.ourIpAddress; + } + + /** + * Get our priority. + * + * @return our priority. + */ + public int getOurPriority() { + return this.ourPriority; + } + + /** + * Get the neighbor list for this specific connectPoint. + * + * @return PIM neighbors on this ConnectPoint + */ + public HashMap<IpAddress, PIMNeighbor> getOurNeighborsList() { + return this.neighbors; + } + + /** + * Get the designated router on this connection. + * + * @return the PIMNeighbor representing the DR + */ + public PIMNeighbor getDesignatedRouter() { + return designatedRouter; + } + + /** + * Are we the DR on this CP? + * + * @return true if we are, false if not + */ + public boolean weAreTheDr() { + return (designatedRouter != null && + designatedRouter.getPrimaryAddr().equals(ourIpAddress)); + } + + /** + * Find the neighbor with the given IP address on this CP. + * + * @param ipaddr the IP address of the neighbor we are interested in + * @return the pim neighbor if it exists + */ + public PIMNeighbor findNeighbor(IpAddress ipaddr) { + PIMNeighbor nbr = neighbors.get(ipaddr); + return nbr; + } + + /** + * Add a new PIM neighbor to this list. + * + * @param nbr the neighbor to be added. + */ + public void addNeighbor(PIMNeighbor nbr) { + if (neighbors.containsKey(nbr.getPrimaryAddr())) { + + // TODO: Hmmm, how should this be handled? + log.debug("We are adding a neighbor that already exists: {}", nbr.toString()); + neighbors.remove(nbr.getPrimaryAddr(), nbr); + } + nbr.setNeighbors(this); + neighbors.put(nbr.getPrimaryAddr(), nbr); + } + + /** + * Remove the neighbor from our neighbor list. + * + * @param ipaddr the IP address of the neighbor to remove + */ + public void removeNeighbor(IpAddress ipaddr) { + + boolean reelect = (designatedRouter == null || designatedRouter.getPrimaryAddr().equals(ipaddr)); + if (neighbors.containsKey(ipaddr)) { + neighbors.remove(ipaddr); + } + this.electDR(); + } + + /** + * Remove the given neighbor from the neighbor list. + * + * @param nbr the nbr to be removed. + */ + public void removeNeighbor(PIMNeighbor nbr) { + + boolean reelect = (designatedRouter == null || nbr.isDr()); + neighbors.remove(nbr.getPrimaryAddr(), nbr); + this.electDR(); + } + + /** + * Elect a new DR on this ConnectPoint. + * + * @return the PIM Neighbor that wins + */ + public PIMNeighbor electDR() { + + for (PIMNeighbor nbr : this.neighbors.values()) { + if (this.designatedRouter == null) { + this.designatedRouter = nbr; + continue; + } + + if (nbr.getPriority() > this.designatedRouter.getPriority()) { + this.designatedRouter = nbr; + continue; + } + + // We could sort in ascending order + if (this.designatedRouter.getPrimaryAddr().compareTo(nbr.getPrimaryAddr()) > 0) { + this.designatedRouter = nbr; + continue; + } + } + + return this.designatedRouter; + } + + /** + * Elect a new DR given the new neighbor. + * + * @param nbr the new neighbor to use in DR election. + * @return the PIM Neighbor that wins DR election + */ + public PIMNeighbor electDR(PIMNeighbor nbr) { + + // Make sure I have + if (this.designatedRouter == null || + this.designatedRouter.getPriority() < nbr.getPriority() || + this.designatedRouter.getPrimaryAddr().compareTo(nbr.getPrimaryAddr()) > 0) { + this.designatedRouter = nbr; + } + return this.designatedRouter; + } + + /** + * Find or create a pim neighbor with a given ip address and connect point. + * + * @param ipaddr of the pim neighbor + * @param mac The mac address of our sending neighbor + * @param cp the connect point the neighbor was learned from + * @return an existing or new PIM neighbor + */ + public static PIMNeighbor findOrCreate(IpAddress ipaddr, MacAddress mac, ConnectPoint cp) { + PIMNeighbors neighbors = connectPointNeighbors.get(cp); + if (neighbors == null) { + neighbors = new PIMNeighbors(cp); + connectPointNeighbors.put(cp, neighbors); + } + + PIMNeighbor nbr = neighbors.findNeighbor(ipaddr); + if (nbr == null) { + nbr = new PIMNeighbor(ipaddr, mac, cp); + neighbors.addNeighbor(nbr); + neighbors.electDR(nbr); + } + return nbr; + } + + // Returns the connect point neighbors hash map + public static HashMap<ConnectPoint, PIMNeighbors> getConnectPointNeighbors() { + return connectPointNeighbors; + } + + /* ---------------------------------- PIM Hello Timer ----------------------------------- */ + + /** + * Start a new hello timer for this ConnectPoint. + */ + private void startHelloTimer() { + this.helloTimer = PIMTimer.getTimer().newTimeout( + new HelloTimer(this), + this.defaultPimHelloInterval, + TimeUnit.SECONDS); + + log.trace("Started Hello Timer: " + this.ourIpAddress.toString()); + } + + /** + * This inner class handles transmitting a PIM hello message on this ConnectPoint. + */ + private final class HelloTimer implements TimerTask { + PIMNeighbors neighbors; + + HelloTimer(PIMNeighbors neighbors) { + this.neighbors = neighbors; + } + + @Override + public void run(Timeout timeout) throws Exception { + + // Send off a hello packet + sendHelloPacket(); + + // restart the hello timer + neighbors.startHelloTimer(); + } + } + + private void sendHelloPacket() { + PIMHello hello = new PIMHello(); + + // TODO: we will need to implement the network config service to assign ip addresses & options + /* + hello.createDefaultOptions(); + + Ethernet eth = hello.createPIMHello(this.ourIpAddress); + hello.sendPacket(this.connectPoint); + */ + } + + /** + * prints the connectPointNeighbors list with each neighbor list. + * + * @return string of neighbors. + */ + public static String printPimNeighbors() { + String out = "PIM Neighbors Table: \n"; + + for (PIMNeighbors pn: connectPointNeighbors.values()) { + + out += "CP:\n " + pn.toString(); + for (PIMNeighbor nbr : pn.neighbors.values()) { + out += "\t" + nbr.toString(); + } + } + return out; + } + + @Override + public String toString() { + String out = "PIM Neighbors: "; + if (this.ourIpAddress != null) { + out += "IP: " + this.ourIpAddress.toString(); + } else { + out += "IP: *Null*"; + } + out += "\tPR: " + String.valueOf(this.ourPriority) + "\n"; + return out; + } +}
\ No newline at end of file diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java new file mode 100644 index 00000000..ee62eb79 --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMNeighborsCodec.java @@ -0,0 +1,97 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.pim.impl; + +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.onosproject.codec.CodecContext; +import org.onosproject.codec.JsonCodec; +import org.onosproject.net.ConnectPoint; + +import java.util.HashMap; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * PIM neighbors Codec. + */ +public class PIMNeighborsCodec extends JsonCodec<HashMap<ConnectPoint, PIMNeighbors>> { + // JSON field names + //Return Name + private static final String CPNBRLIST = "connect_point_list"; + + // PIM Neightbors Fields + private static final String IP = "ip"; + private static final String PRIORITY = "priority"; + private static final String NBRLIST = "neighbor_list"; + + // PIM neighbor Files + private static final String DR = "designated"; + private static final String NBR_IP = "ip"; + private static final String PR = "priority"; + private static final String HOLDTIME = "hold_time"; + + /** + * Encode the PIM Neighbors. + * + * @param cpn ConnectPoint neighbors + * @param context encoding context + * + * @return Encoded neighbors used by CLI and REST + */ + @Override + public ObjectNode encode(HashMap<ConnectPoint, PIMNeighbors> cpn, CodecContext context) { + checkNotNull(cpn, "Pim Neighbors cannot be null"); + + ObjectNode pimNbrJsonCodec = context.mapper().createObjectNode(); + ArrayNode cpnList = context.mapper().createArrayNode(); + + for (PIMNeighbors pn: cpn.values()) { + // get the PimNeighbors Obj, contains Neighbors list + // create the json object for a single Entry in the Neighbors list + ObjectNode cp = context.mapper().createObjectNode(); + cp.put(IP, pn.getOurIpAddress().toString()); + cp.put(PRIORITY, String.valueOf(pn.getOurPriority())); + + // create the array for the neighbors list + ArrayNode nbrsList = context.mapper().createArrayNode(); + for (PIMNeighbor nbr : pn.getOurNeighborsList().values()) { + nbrsList.add(neighbor(nbr, context)); + } + // adds pim neighbor to list + cp.set(NBRLIST, nbrsList); + // adds to arraynode which will represent the connect point neighbors hash map. + cpnList.add(cp); + } + pimNbrJsonCodec.set(CPNBRLIST, cpnList); + return pimNbrJsonCodec; + } + + /** + * Encode a single PIM Neighbor. + * + * @param nbr the neighbor to be encoded + * @param context encoding context + * @return the encoded neighbor + */ + private ObjectNode neighbor(PIMNeighbor nbr, CodecContext context) { + return context.mapper().createObjectNode() + .put(DR, Boolean.toString(nbr.isDr())) + .put(NBR_IP, nbr.getPrimaryAddr().toString()) + .put(PR, String.valueOf(nbr.getPriority())) + .put(HOLDTIME, String.valueOf(nbr.getHoldtime())); + } +} diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java new file mode 100644 index 00000000..c131a53b --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/PIMTimer.java @@ -0,0 +1,53 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.pim.impl; + +import org.jboss.netty.util.HashedWheelTimer; + +/** + * PIM Timer used for PIM Neighbors. + */ +public final class PIMTimer { + + private static volatile HashedWheelTimer timer; + + // Ban public construction + private PIMTimer() { + } + + /** + * Returns the singleton hashed-wheel timer. + * + * @return hashed-wheel timer + */ + public static HashedWheelTimer getTimer() { + if (PIMTimer.timer == null) { + initTimer(); + } + return PIMTimer.timer; + } + + // Start the PIM timer. + private static synchronized void initTimer() { + if (PIMTimer.timer == null) { + + // Create and start a new hashed wheel timer, if it does not exist. + HashedWheelTimer hwTimer = new HashedWheelTimer(); + hwTimer.start(); + PIMTimer.timer = hwTimer; + } + } +} diff --git a/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java new file mode 100644 index 00000000..29d1ce4e --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/java/org/onosproject/pim/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * PIM Emulation speak hello messages and listen to Join/Prunes. + */ +package org.onosproject.pim.impl; diff --git a/framework/src/onos/apps/pim/src/main/resources/OSGI-INF.blueprint/shell-config.xml b/framework/src/onos/apps/pim/src/main/resources/OSGI-INF.blueprint/shell-config.xml new file mode 100644 index 00000000..c30e3792 --- /dev/null +++ b/framework/src/onos/apps/pim/src/main/resources/OSGI-INF.blueprint/shell-config.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + + <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0"> + <command> + <action class="org.onosproject.pim.cli.PIMShowCommand"/> + </command> + </command-bundle> + +</blueprint> diff --git a/framework/src/onos/apps/pom.xml b/framework/src/onos/apps/pom.xml index 334c690b..b955130a 100644 --- a/framework/src/onos/apps/pom.xml +++ b/framework/src/onos/apps/pom.xml @@ -53,11 +53,12 @@ <module>olt</module> <module>cip</module> <module>flowanalyzer</module> - <module>vtnrsc</module> <module>vtn</module> - <module>vtnweb</module> <module>dhcp</module> <module>cordvtn</module> + <module>mfwd</module> + <module>igmp</module> + <module>pim</module> </modules> <properties> diff --git a/framework/src/onos/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java b/framework/src/onos/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java index 2eb96df2..2eb1d5ec 100644 --- a/framework/src/onos/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java +++ b/framework/src/onos/apps/proxyarp/src/main/java/org/onosproject/proxyarp/ProxyArp.java @@ -23,6 +23,8 @@ import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.onlab.packet.Ethernet; +import org.onlab.packet.ICMP6; +import org.onlab.packet.IPv6; import org.onosproject.cfg.ComponentConfigService; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; @@ -214,27 +216,35 @@ public class ProxyArp { if (context.isHandled()) { return; } - // If IPv6 NDP is disabled, don't handle IPv6 frames. + InboundPacket pkt = context.inPacket(); Ethernet ethPkt = pkt.parsed(); if (ethPkt == null) { return; } - if (!ipv6NeighborDiscovery && (ethPkt.getEtherType() == TYPE_IPV6)) { - return; + + if (ethPkt.getEtherType() == TYPE_ARP) { + //handle the arp packet. + proxyArpService.handlePacket(context); + } else if (ipv6NeighborDiscovery && ethPkt.getEtherType() == TYPE_IPV6) { + IPv6 ipv6Pkt = (IPv6) ethPkt.getPayload(); + if (ipv6Pkt.getNextHeader() == IPv6.PROTOCOL_ICMP6) { + ICMP6 icmp6Pkt = (ICMP6) ipv6Pkt.getPayload(); + if (icmp6Pkt.getIcmpType() == NEIGHBOR_SOLICITATION || + icmp6Pkt.getIcmpType() == NEIGHBOR_ADVERTISEMENT) { + // handle ICMPv6 solicitations and advertisements + proxyArpService.handlePacket(context); + } + } } + // FIXME why were we listening to IPv4 frames at all? // Do not ARP for multicast packets. Let mfwd handle them. if (ethPkt.getEtherType() == Ethernet.TYPE_IPV4) { if (ethPkt.getDestinationMAC().isMulticast()) { return; } } - - //handle the arp packet. - proxyArpService.handlePacket(context); } } } - - diff --git a/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/LocationType.java b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/LocationType.java new file mode 100644 index 00000000..01f4f700 --- /dev/null +++ b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/LocationType.java @@ -0,0 +1,35 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.reactive.routing; + +/** + * Specifies the type of an IP address or an IP prefix location. + */ +enum LocationType { + /** + * The location of an IP address or an IP prefix is in local SDN network. + */ + LOCAL, + /** + * The location of an IP address or an IP prefix is outside local SDN network. + */ + INTERNET, + /** + * There is no route for this IP address or IP prefix. + */ + NO_ROUTE +} diff --git a/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/ReactiveRoutingFib.java b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/ReactiveRoutingFib.java new file mode 100644 index 00000000..8e86056e --- /dev/null +++ b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/ReactiveRoutingFib.java @@ -0,0 +1,395 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.reactive.routing; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Maps; +import org.onlab.packet.Ethernet; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; +import org.onosproject.core.ApplicationId; +import org.onosproject.incubator.net.intf.Interface; +import org.onosproject.incubator.net.intf.InterfaceService; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.Host; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.host.HostService; +import org.onosproject.net.intent.Constraint; +import org.onosproject.net.intent.Key; +import org.onosproject.net.intent.MultiPointToSinglePointIntent; +import org.onosproject.net.intent.constraint.PartialFailureConstraint; +import org.onosproject.routing.IntentRequestListener; +import org.onosproject.routing.IntentSynchronizationService; +import org.onosproject.routing.config.RoutingConfigurationService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * FIB component for reactive routing intents. + */ +public class ReactiveRoutingFib implements IntentRequestListener { + + private static final int PRIORITY_OFFSET = 100; + private static final int PRIORITY_MULTIPLIER = 5; + protected static final ImmutableList<Constraint> CONSTRAINTS + = ImmutableList.of(new PartialFailureConstraint()); + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final ApplicationId appId; + private final HostService hostService; + private final RoutingConfigurationService configService; + private final InterfaceService interfaceService; + private final IntentSynchronizationService intentSynchronizer; + + private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents; + + /** + * Class constructor. + * + * @param appId application ID to use to generate intents + * @param hostService host service + * @param configService routing configuration service + * @param interfaceService interface service + * @param intentSynchronizer intent synchronization service + */ + public ReactiveRoutingFib(ApplicationId appId, HostService hostService, + RoutingConfigurationService configService, + InterfaceService interfaceService, + IntentSynchronizationService intentSynchronizer) { + this.appId = appId; + this.hostService = hostService; + this.configService = configService; + this.interfaceService = interfaceService; + this.intentSynchronizer = intentSynchronizer; + + routeIntents = Maps.newConcurrentMap(); + } + + @Override + public void setUpConnectivityInternetToHost(IpAddress hostIpAddress) { + checkNotNull(hostIpAddress); + Set<ConnectPoint> ingressPoints = + configService.getBgpPeerConnectPoints(); + + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + + if (hostIpAddress.isIp4()) { + selector.matchEthType(Ethernet.TYPE_IPV4); + } else { + selector.matchEthType(Ethernet.TYPE_IPV6); + } + + // Match the destination IP prefix at the first hop + IpPrefix ipPrefix = hostIpAddress.toIpPrefix(); + selector.matchIPDst(ipPrefix); + + // Rewrite the destination MAC address + MacAddress hostMac = null; + ConnectPoint egressPoint = null; + for (Host host : hostService.getHostsByIp(hostIpAddress)) { + if (host.mac() != null) { + hostMac = host.mac(); + egressPoint = host.location(); + break; + } + } + if (hostMac == null) { + hostService.startMonitoringIp(hostIpAddress); + return; + } + + TrafficTreatment.Builder treatment = + DefaultTrafficTreatment.builder().setEthDst(hostMac); + Key key = Key.of(ipPrefix.toString(), appId); + int priority = ipPrefix.prefixLength() * PRIORITY_MULTIPLIER + + PRIORITY_OFFSET; + MultiPointToSinglePointIntent intent = + MultiPointToSinglePointIntent.builder() + .appId(appId) + .key(key) + .selector(selector.build()) + .treatment(treatment.build()) + .ingressPoints(ingressPoints) + .egressPoint(egressPoint) + .priority(priority) + .constraints(CONSTRAINTS) + .build(); + + log.trace("Generates ConnectivityInternetToHost intent {}", intent); + submitReactiveIntent(ipPrefix, intent); + } + + @Override + public void setUpConnectivityHostToInternet(IpAddress hostIp, IpPrefix prefix, + IpAddress nextHopIpAddress) { + // Find the attachment point (egress interface) of the next hop + Interface egressInterface = interfaceService.getMatchingInterface(nextHopIpAddress); + if (egressInterface == null) { + log.warn("No outgoing interface found for {}", + nextHopIpAddress); + return; + } + + Set<Host> hosts = hostService.getHostsByIp(nextHopIpAddress); + if (hosts.isEmpty()) { + log.warn("No host found for next hop IP address"); + return; + } + MacAddress nextHopMacAddress = null; + for (Host host : hosts) { + nextHopMacAddress = host.mac(); + break; + } + + hosts = hostService.getHostsByIp(hostIp); + if (hosts.isEmpty()) { + log.warn("No host found for host IP address"); + return; + } + Host host = hosts.stream().findFirst().get(); + ConnectPoint ingressPoint = host.location(); + + // Generate the intent itself + ConnectPoint egressPort = egressInterface.connectPoint(); + log.debug("Generating intent for prefix {}, next hop mac {}", + prefix, nextHopMacAddress); + + // Match the destination IP prefix at the first hop + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + if (prefix.isIp4()) { + selector.matchEthType(Ethernet.TYPE_IPV4); + selector.matchIPDst(prefix); + } else { + selector.matchEthType(Ethernet.TYPE_IPV6); + selector.matchIPv6Dst(prefix); + } + + // Rewrite the destination MAC address + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder() + .setEthDst(nextHopMacAddress); + if (!egressInterface.vlan().equals(VlanId.NONE)) { + treatment.setVlanId(egressInterface.vlan()); + // If we set VLAN ID, we have to make sure a VLAN tag exists. + // TODO support no VLAN -> VLAN routing + selector.matchVlanId(VlanId.ANY); + } + + int priority = prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET; + Key key = Key.of(prefix.toString() + "-reactive", appId); + MultiPointToSinglePointIntent intent = MultiPointToSinglePointIntent.builder() + .appId(appId) + .key(key) + .selector(selector.build()) + .treatment(treatment.build()) + .ingressPoints(Collections.singleton(ingressPoint)) + .egressPoint(egressPort) + .priority(priority) + .constraints(CONSTRAINTS) + .build(); + + submitReactiveIntent(prefix, intent); + } + + @Override + public void setUpConnectivityHostToHost(IpAddress dstIpAddress, + IpAddress srcIpAddress, + MacAddress srcMacAddress, + ConnectPoint srcConnectPoint) { + checkNotNull(dstIpAddress); + checkNotNull(srcIpAddress); + checkNotNull(srcMacAddress); + checkNotNull(srcConnectPoint); + + IpPrefix srcIpPrefix = srcIpAddress.toIpPrefix(); + IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix(); + ConnectPoint dstConnectPoint = null; + MacAddress dstMacAddress = null; + + for (Host host : hostService.getHostsByIp(dstIpAddress)) { + if (host.mac() != null) { + dstMacAddress = host.mac(); + dstConnectPoint = host.location(); + break; + } + } + if (dstMacAddress == null) { + hostService.startMonitoringIp(dstIpAddress); + return; + } + + // + // Handle intent from source host to destination host + // + MultiPointToSinglePointIntent srcToDstIntent = + hostToHostIntentGenerator(dstIpAddress, dstConnectPoint, + dstMacAddress, srcConnectPoint); + submitReactiveIntent(dstIpPrefix, srcToDstIntent); + + // + // Handle intent from destination host to source host + // + + // Since we proactively handle the intent from destination host to + // source host, we should check whether there is an exiting intent + // first. + if (mp2pIntentExists(srcIpPrefix)) { + updateExistingMp2pIntent(srcIpPrefix, dstConnectPoint); + return; + } else { + // There is no existing intent, create a new one. + MultiPointToSinglePointIntent dstToSrcIntent = + hostToHostIntentGenerator(srcIpAddress, srcConnectPoint, + srcMacAddress, dstConnectPoint); + submitReactiveIntent(srcIpPrefix, dstToSrcIntent); + } + } + + /** + * Generates MultiPointToSinglePointIntent for both source host and + * destination host located in local SDN network. + * + * @param dstIpAddress the destination IP address + * @param dstConnectPoint the destination host connect point + * @param dstMacAddress the MAC address of destination host + * @param srcConnectPoint the connect point where packet-in from + * @return the generated MultiPointToSinglePointIntent + */ + private MultiPointToSinglePointIntent hostToHostIntentGenerator( + IpAddress dstIpAddress, + ConnectPoint dstConnectPoint, + MacAddress dstMacAddress, + ConnectPoint srcConnectPoint) { + checkNotNull(dstIpAddress); + checkNotNull(dstConnectPoint); + checkNotNull(dstMacAddress); + checkNotNull(srcConnectPoint); + + Set<ConnectPoint> ingressPoints = new HashSet<>(); + ingressPoints.add(srcConnectPoint); + IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix(); + + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + if (dstIpAddress.isIp4()) { + selector.matchEthType(Ethernet.TYPE_IPV4); + selector.matchIPDst(dstIpPrefix); + } else { + selector.matchEthType(Ethernet.TYPE_IPV6); + selector.matchIPv6Dst(dstIpPrefix); + } + + // Rewrite the destination MAC address + TrafficTreatment.Builder treatment = + DefaultTrafficTreatment.builder().setEthDst(dstMacAddress); + + Key key = Key.of(dstIpPrefix.toString(), appId); + int priority = dstIpPrefix.prefixLength() * PRIORITY_MULTIPLIER + + PRIORITY_OFFSET; + MultiPointToSinglePointIntent intent = + MultiPointToSinglePointIntent.builder() + .appId(appId) + .key(key) + .selector(selector.build()) + .treatment(treatment.build()) + .ingressPoints(ingressPoints) + .egressPoint(dstConnectPoint) + .priority(priority) + .constraints(CONSTRAINTS) + .build(); + + log.trace("Generates ConnectivityHostToHost = {} ", intent); + return intent; + } + + @Override + public void updateExistingMp2pIntent(IpPrefix ipPrefix, + ConnectPoint ingressConnectPoint) { + checkNotNull(ipPrefix); + checkNotNull(ingressConnectPoint); + + MultiPointToSinglePointIntent existingIntent = + getExistingMp2pIntent(ipPrefix); + if (existingIntent != null) { + Set<ConnectPoint> ingressPoints = existingIntent.ingressPoints(); + // Add host connect point into ingressPoints of the existing intent + if (ingressPoints.add(ingressConnectPoint)) { + MultiPointToSinglePointIntent updatedMp2pIntent = + MultiPointToSinglePointIntent.builder() + .appId(appId) + .key(existingIntent.key()) + .selector(existingIntent.selector()) + .treatment(existingIntent.treatment()) + .ingressPoints(ingressPoints) + .egressPoint(existingIntent.egressPoint()) + .priority(existingIntent.priority()) + .constraints(CONSTRAINTS) + .build(); + + log.trace("Update an existing MultiPointToSinglePointIntent " + + "to new intent = {} ", updatedMp2pIntent); + submitReactiveIntent(ipPrefix, updatedMp2pIntent); + } + // If adding ingressConnectPoint to ingressPoints failed, it + // because between the time interval from checking existing intent + // to generating new intent, onos updated this intent due to other + // packet-in and the new intent also includes the + // ingressConnectPoint. This will not affect reactive routing. + } + } + + /** + * Submits a reactive intent to the intent synchronizer. + * + * @param ipPrefix IP prefix of the intent + * @param intent intent to submit + */ + void submitReactiveIntent(IpPrefix ipPrefix, MultiPointToSinglePointIntent intent) { + routeIntents.put(ipPrefix, intent); + + intentSynchronizer.submit(intent); + } + + /** + * Gets the existing MultiPointToSinglePointIntent from memory for a given + * IP prefix. + * + * @param ipPrefix the IP prefix used to find MultiPointToSinglePointIntent + * @return the MultiPointToSinglePointIntent if found, otherwise null + */ + private MultiPointToSinglePointIntent getExistingMp2pIntent(IpPrefix ipPrefix) { + checkNotNull(ipPrefix); + return routeIntents.get(ipPrefix); + } + + @Override + public boolean mp2pIntentExists(IpPrefix ipPrefix) { + checkNotNull(ipPrefix); + return routeIntents.get(ipPrefix) != null; + } +} diff --git a/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java index ad78a1ce..96aa06ee 100644 --- a/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java +++ b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/SdnIpReactiveRouting.java @@ -25,27 +25,39 @@ import org.onlab.packet.EthType; import org.onlab.packet.Ethernet; import org.onlab.packet.IPv4; import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip6Address; import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; import org.onlab.packet.MacAddress; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; +import org.onosproject.incubator.net.intf.Interface; +import org.onosproject.incubator.net.intf.InterfaceService; import org.onosproject.net.ConnectPoint; +import org.onosproject.net.Host; import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.host.HostService; import org.onosproject.net.packet.DefaultOutboundPacket; import org.onosproject.net.packet.InboundPacket; import org.onosproject.net.packet.OutboundPacket; import org.onosproject.net.packet.PacketContext; import org.onosproject.net.packet.PacketProcessor; import org.onosproject.net.packet.PacketService; +import org.onosproject.routing.IntentRequestListener; +import org.onosproject.routing.RouteEntry; import org.onosproject.routing.RoutingService; +import org.onosproject.routing.SdnIpService; import org.onosproject.routing.config.RoutingConfigurationService; import org.slf4j.Logger; import java.nio.ByteBuffer; +import java.util.Optional; +import java.util.Set; +import static com.google.common.base.Preconditions.checkNotNull; import static org.onlab.packet.Ethernet.TYPE_ARP; import static org.onlab.packet.Ethernet.TYPE_IPV4; import static org.onosproject.net.packet.PacketPriority.REACTIVE; @@ -74,16 +86,32 @@ public class SdnIpReactiveRouting { protected RoutingService routingService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected SdnIpService sdnIpService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected RoutingConfigurationService config; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected InterfaceService interfaceService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected HostService hostService; + private ApplicationId appId; + private IntentRequestListener intentRequestListener; + private ReactiveRoutingProcessor processor = new ReactiveRoutingProcessor(); @Activate public void activate() { appId = coreService.registerApplication(APP_NAME); + + intentRequestListener = new ReactiveRoutingFib(appId, hostService, + config, interfaceService, + sdnIpService.getIntentSynchronizationService()); + packetService.addProcessor(processor, PacketProcessor.director(2)); requestIntercepts(); log.info("SDN-IP Reactive Routing Started"); @@ -168,12 +196,11 @@ public class SdnIpReactiveRouting { IpAddress srcIp = IpAddress.valueOf(ipv4Packet.getSourceAddress()); MacAddress srcMac = ethPkt.getSourceMAC(); - routingService.packetReactiveProcessor(dstIp, srcIp, - srcConnectPoint, srcMac); + packetReactiveProcessor(dstIp, srcIp, srcConnectPoint, srcMac); // TODO emit packet first or packetReactiveProcessor first ConnectPoint egressConnectPoint = null; - egressConnectPoint = routingService.getEgressConnectPoint(dstIp); + egressConnectPoint = getEgressConnectPoint(dstIp); if (egressConnectPoint != null) { forwardPacketToDst(context, egressConnectPoint); } @@ -185,6 +212,173 @@ public class SdnIpReactiveRouting { } /** + * Routes packet reactively. + * + * @param dstIpAddress the destination IP address of a packet + * @param srcIpAddress the source IP address of a packet + * @param srcConnectPoint the connect point where a packet comes from + * @param srcMacAddress the source MAC address of a packet + */ + private void packetReactiveProcessor(IpAddress dstIpAddress, + IpAddress srcIpAddress, + ConnectPoint srcConnectPoint, + MacAddress srcMacAddress) { + checkNotNull(dstIpAddress); + checkNotNull(srcIpAddress); + checkNotNull(srcConnectPoint); + checkNotNull(srcMacAddress); + + // + // Step1: Try to update the existing intent first if it exists. + // + IpPrefix ipPrefix = null; + RouteEntry routeEntry = null; + if (config.isIpAddressLocal(dstIpAddress)) { + if (dstIpAddress.isIp4()) { + ipPrefix = IpPrefix.valueOf(dstIpAddress, + Ip4Address.BIT_LENGTH); + } else { + ipPrefix = IpPrefix.valueOf(dstIpAddress, + Ip6Address.BIT_LENGTH); + } + } else { + // Get IP prefix from BGP route table + routeEntry = routingService.getLongestMatchableRouteEntry(dstIpAddress); + if (routeEntry != null) { + ipPrefix = routeEntry.prefix(); + } + } + if (ipPrefix != null + && intentRequestListener.mp2pIntentExists(ipPrefix)) { + intentRequestListener.updateExistingMp2pIntent(ipPrefix, + srcConnectPoint); + return; + } + + // + // Step2: There is no existing intent for the destination IP address. + // Check whether it is necessary to create a new one. If necessary then + // create a new one. + // + TrafficType trafficType = + trafficTypeClassifier(srcConnectPoint, dstIpAddress); + + switch (trafficType) { + case HOST_TO_INTERNET: + // If the destination IP address is outside the local SDN network. + // The Step 1 has already handled it. We do not need to do anything here. + intentRequestListener.setUpConnectivityHostToInternet(srcIpAddress, + ipPrefix, routeEntry.nextHop()); + break; + case INTERNET_TO_HOST: + intentRequestListener.setUpConnectivityInternetToHost(dstIpAddress); + break; + case HOST_TO_HOST: + intentRequestListener.setUpConnectivityHostToHost(dstIpAddress, + srcIpAddress, srcMacAddress, srcConnectPoint); + break; + case INTERNET_TO_INTERNET: + log.trace("This is transit traffic, " + + "the intent should be preinstalled already"); + break; + case DROP: + // TODO here should setUpDropPacketIntent(...); + // We need a new type of intent here. + break; + case UNKNOWN: + log.trace("This is unknown traffic, so we do nothing"); + break; + default: + break; + } + } + + /** + * Classifies the traffic and return the traffic type. + * + * @param srcConnectPoint the connect point where the packet comes from + * @param dstIp the destination IP address in packet + * @return the traffic type which this packet belongs to + */ + private TrafficType trafficTypeClassifier(ConnectPoint srcConnectPoint, + IpAddress dstIp) { + LocationType dstIpLocationType = getLocationType(dstIp); + Optional<Interface> srcInterface = + interfaceService.getInterfacesByPort(srcConnectPoint).stream().findFirst(); + Set<ConnectPoint> ingressPoints = config.getBgpPeerConnectPoints(); + + switch (dstIpLocationType) { + case INTERNET: + if (srcInterface.isPresent() && + (!ingressPoints.contains(srcConnectPoint))) { + return TrafficType.HOST_TO_INTERNET; + } else { + return TrafficType.INTERNET_TO_INTERNET; + } + case LOCAL: + if (srcInterface.isPresent() && + (!ingressPoints.contains(srcConnectPoint))) { + return TrafficType.HOST_TO_HOST; + } else { + // TODO Currently we only consider local public prefixes. + // In the future, we will consider the local private prefixes. + // If dstIpLocationType is a local private, we should return + // TrafficType.DROP. + return TrafficType.INTERNET_TO_HOST; + } + case NO_ROUTE: + return TrafficType.DROP; + default: + return TrafficType.UNKNOWN; + } + } + + /** + * Evaluates the location of an IP address and returns the location type. + * + * @param ipAddress the IP address to evaluate + * @return the IP address location type + */ + private LocationType getLocationType(IpAddress ipAddress) { + if (config.isIpAddressLocal(ipAddress)) { + return LocationType.LOCAL; + } else if (routingService.getLongestMatchableRouteEntry(ipAddress) != null) { + return LocationType.INTERNET; + } else { + return LocationType.NO_ROUTE; + } + } + + public ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress) { + LocationType type = getLocationType(dstIpAddress); + if (type == LocationType.LOCAL) { + Set<Host> hosts = hostService.getHostsByIp(dstIpAddress); + if (!hosts.isEmpty()) { + return hosts.iterator().next().location(); + } else { + hostService.startMonitoringIp(dstIpAddress); + return null; + } + } else if (type == LocationType.INTERNET) { + IpAddress nextHopIpAddress = null; + RouteEntry routeEntry = routingService.getLongestMatchableRouteEntry(dstIpAddress); + if (routeEntry != null) { + nextHopIpAddress = routeEntry.nextHop(); + Interface it = interfaceService.getMatchingInterface(nextHopIpAddress); + if (it != null) { + return it.connectPoint(); + } else { + return null; + } + } else { + return null; + } + } else { + return null; + } + } + + /** * Emits the specified packet onto the network. * * @param context the packet context diff --git a/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/TrafficType.java b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/TrafficType.java new file mode 100644 index 00000000..134126b3 --- /dev/null +++ b/framework/src/onos/apps/reactive-routing/src/main/java/org/onosproject/reactive/routing/TrafficType.java @@ -0,0 +1,56 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.reactive.routing; + +/** + * Specifies the type of traffic. + * <p> + * We classify traffic by the first packet of each traffic. + * </p> + */ +enum TrafficType { + /** + * Traffic from a host located in local SDN network wants to + * communicate with destination host located in Internet (outside + * local SDN network). + */ + HOST_TO_INTERNET, + /** + * Traffic from Internet wants to communicate with a host located + * in local SDN network. + */ + INTERNET_TO_HOST, + /** + * Both the source host and destination host of a traffic are in + * local SDN network. + */ + HOST_TO_HOST, + /** + * Traffic from Internet wants to traverse local SDN network. + */ + INTERNET_TO_INTERNET, + /** + * Any traffic wants to communicate with a destination which has + * no route, or traffic from Internet wants to access a local private + * IP address. + */ + DROP, + /** + * Traffic does not belong to the types above. + */ + UNKNOWN +} diff --git a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java index 2d1bb311..1069ec5a 100644 --- a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java +++ b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentRequestListener.java @@ -47,6 +47,16 @@ public interface IntentRequestListener { ConnectPoint srcConnectPoint); /** + * Sets up connectivity for packet from a local host to the Internet. + * + * @param hostIp IP address of the local host + * @param prefix external IP prefix that the host is talking to + * @param nextHopIpAddress IP address of the next hop router for the prefix + */ + void setUpConnectivityHostToInternet(IpAddress hostIp, IpPrefix prefix, + IpAddress nextHopIpAddress); + + /** * Adds one new ingress connect point into ingress points of an existing * intent and resubmits the new intent. * <p> diff --git a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentSynchronizationService.java b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentSynchronizationService.java new file mode 100644 index 00000000..dc6a838d --- /dev/null +++ b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/IntentSynchronizationService.java @@ -0,0 +1,51 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.routing; + +import org.onosproject.net.intent.Intent; + +/** + * Submits and withdraws intents to the IntentService from a single point in + * the cluster at any one time. The provided intents will be synchronized with + * the IntentService on leadership change. + */ +public interface IntentSynchronizationService { + + /** + * Submits and intent to the synchronizer. + * <p> + * The intent will be submitted directly to the IntentService if this node + * is the leader, otherwise it will be stored in the synchronizer for + * synchronization if this node becomes the leader. + * </p> + * + * @param intent intent to submit + */ + void submit(Intent intent); + + /** + * Withdraws an intent from the synchronizer. + * <p> + * The intent will be withdrawn directly from the IntentService if this node + * is the leader. The intent will be removed from the synchronizer's + * in-memory storage. + * </p> + * + * @param intent intent to withdraw + */ + void withdraw(Intent intent); +} diff --git a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java index 8b7040e2..7399ed75 100644 --- a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java +++ b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/RoutingService.java @@ -16,8 +16,6 @@ package org.onosproject.routing; import org.onlab.packet.IpAddress; -import org.onlab.packet.MacAddress; -import org.onosproject.net.ConnectPoint; import org.onosproject.routing.config.BgpConfig; import java.util.Collection; @@ -32,63 +30,6 @@ public interface RoutingService { Class<BgpConfig> CONFIG_CLASS = BgpConfig.class; /** - * Specifies the type of an IP address or an IP prefix location. - */ - enum LocationType { - /** - * The location of an IP address or an IP prefix is in local SDN network. - */ - LOCAL, - /** - * The location of an IP address or an IP prefix is outside local SDN network. - */ - INTERNET, - /** - * There is no route for this IP address or IP prefix. - */ - NO_ROUTE - } - - /** - * Specifies the type of traffic. - * <p> - * We classify traffic by the first packet of each traffic. - * </p> - */ - enum TrafficType { - /** - * Traffic from a host located in local SDN network wants to - * communicate with destination host located in Internet (outside - * local SDN network). - */ - HOST_TO_INTERNET, - /** - * Traffic from Internet wants to communicate with a host located - * in local SDN network. - */ - INTERNET_TO_HOST, - /** - * Both the source host and destination host of a traffic are in - * local SDN network. - */ - HOST_TO_HOST, - /** - * Traffic from Internet wants to traverse local SDN network. - */ - INTERNET_TO_INTERNET, - /** - * Any traffic wants to communicate with a destination which has - * no route, or traffic from Internet wants to access a local private - * IP address. - */ - DROP, - /** - * Traffic does not belong to the types above. - */ - UNKNOWN - } - - /** * Starts the routing service. */ void start(); @@ -101,15 +42,6 @@ public interface RoutingService { void addFibListener(FibListener fibListener); /** - * Adds intent creation and submission listener. - * - * @param intentRequestListener listener to send intent creation and - * submission request to - */ - void addIntentRequestListener(IntentRequestListener - intentRequestListener); - - /** * Stops the routing service. */ void stop(); @@ -129,14 +61,6 @@ public interface RoutingService { Collection<RouteEntry> getRoutes6(); /** - * Evaluates the location of an IP address and returns the location type. - * - * @param ipAddress the IP address to evaluate - * @return the IP address location type - */ - LocationType getLocationType(IpAddress ipAddress); - - /** * Finds out the route entry which has the longest matchable IP prefix. * * @param ipAddress IP address used to find out longest matchable IP prefix @@ -145,25 +69,4 @@ public interface RoutingService { */ RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress); - /** - * Finds out the egress connect point where to emit the first packet - * based on destination IP address. - * - * @param dstIpAddress the destination IP address - * @return the egress connect point if found, otherwise null - */ - ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress); - - /** - * Routes packet reactively. - * - * @param dstIpAddress the destination IP address of a packet - * @param srcIpAddress the source IP address of a packet - * @param srcConnectPoint the connect point where a packet comes from - * @param srcMacAddress the source MAC address of a packet - */ - void packetReactiveProcessor(IpAddress dstIpAddress, - IpAddress srcIpAddress, - ConnectPoint srcConnectPoint, - MacAddress srcMacAddress); } diff --git a/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/SdnIpService.java b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/SdnIpService.java new file mode 100644 index 00000000..0945336c --- /dev/null +++ b/framework/src/onos/apps/routing-api/src/main/java/org/onosproject/routing/SdnIpService.java @@ -0,0 +1,39 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.routing; + +/** + * Service interface exported by SDN-IP. + */ +public interface SdnIpService { + + /** + * Changes whether this SDN-IP instance is the primary or not based on the + * boolean parameter. + * + * @param isPrimary true if the instance is primary, false if it is not + */ + void modifyPrimary(boolean isPrimary); + + /** + * Gets the intent synchronization service. + * + * @return intent synchronization service + */ + // TODO fix service resolution in SDN-IP + IntentSynchronizationService getIntentSynchronizationService(); + +} diff --git a/framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/RouteEntryTest.java b/framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/RouteEntryTest.java index c47d2768..b89eb2d1 100644 --- a/framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/RouteEntryTest.java +++ b/framework/src/onos/apps/routing-api/src/test/java/org/onosproject/routing/RouteEntryTest.java @@ -19,10 +19,15 @@ import org.hamcrest.Matchers; import org.junit.Test; import org.onlab.packet.Ip4Address; import org.onlab.packet.Ip4Prefix; +import org.onlab.packet.Ip6Address; +import org.onlab.packet.Ip6Prefix; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Unit tests for the RouteEntry class. @@ -35,17 +40,22 @@ public class RouteEntryTest { public void testConstructor() { Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24"); Ip4Address nextHop = Ip4Address.valueOf("5.6.7.8"); - RouteEntry routeEntry = new RouteEntry(prefix, nextHop); assertThat(routeEntry.toString(), is("RouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8}")); + + Ip6Prefix prefix6 = Ip6Prefix.valueOf("1000::/64"); + Ip6Address nextHop6 = Ip6Address.valueOf("2000::1"); + RouteEntry routeEntry6 = new RouteEntry(prefix6, nextHop6); + assertThat(routeEntry6.toString(), + is("RouteEntry{prefix=1000::/64, nextHop=2000::1}")); } /** * Tests invalid class constructor for null IPv4 prefix. */ @Test(expected = NullPointerException.class) - public void testInvalidConstructorNullPrefix() { + public void testInvalidConstructorNullIpv4Prefix() { Ip4Prefix prefix = null; Ip4Address nextHop = Ip4Address.valueOf("5.6.7.8"); @@ -53,10 +63,21 @@ public class RouteEntryTest { } /** + * Tests invalid class constructor for null IPv6 prefix. + */ + @Test(expected = NullPointerException.class) + public void testInvalidConstructorNullIpv6Prefix() { + Ip6Prefix prefix = null; + Ip6Address nextHop = Ip6Address.valueOf("2000::1"); + + new RouteEntry(prefix, nextHop); + } + + /** * Tests invalid class constructor for null IPv4 next-hop. */ @Test(expected = NullPointerException.class) - public void testInvalidConstructorNullNextHop() { + public void testInvalidConstructorNullIpv4NextHop() { Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24"); Ip4Address nextHop = null; @@ -64,16 +85,32 @@ public class RouteEntryTest { } /** + * Tests invalid class constructor for null IPv6 next-hop. + */ + @Test(expected = NullPointerException.class) + public void testInvalidConstructorNullIpv6NextHop() { + Ip6Prefix prefix = Ip6Prefix.valueOf("1000::/64"); + Ip6Address nextHop = null; + + new RouteEntry(prefix, nextHop); + } + + /** * Tests getting the fields of a route entry. */ @Test public void testGetFields() { Ip4Prefix prefix = Ip4Prefix.valueOf("1.2.3.0/24"); Ip4Address nextHop = Ip4Address.valueOf("5.6.7.8"); - RouteEntry routeEntry = new RouteEntry(prefix, nextHop); assertThat(routeEntry.prefix(), is(prefix)); assertThat(routeEntry.nextHop(), is(nextHop)); + + Ip6Prefix prefix6 = Ip6Prefix.valueOf("1000::/64"); + Ip6Address nextHop6 = Ip6Address.valueOf("2000::1"); + RouteEntry routeEntry6 = new RouteEntry(prefix6, nextHop6); + assertThat(routeEntry6.prefix(), is(prefix6)); + assertThat(routeEntry6.nextHop(), is(nextHop6)); } /** @@ -105,6 +142,33 @@ public class RouteEntryTest { prefix = Ip4Prefix.valueOf("255.255.255.255/32"); assertThat(RouteEntry.createBinaryString(prefix), is("11111111111111111111111111111111")); + + Ip6Prefix prefix6; + Pattern pattern; + Matcher matcher; + + prefix6 = Ip6Prefix.valueOf("::/0"); + assertThat(RouteEntry.createBinaryString(prefix6), is("")); + + prefix6 = Ip6Prefix.valueOf("2000::1000/112"); + pattern = Pattern.compile("00100{108}"); + matcher = pattern.matcher(RouteEntry.createBinaryString(prefix6)); + assertTrue(matcher.matches()); + + prefix6 = Ip6Prefix.valueOf("2000::1000/116"); + pattern = Pattern.compile("00100{108}0001"); + matcher = pattern.matcher(RouteEntry.createBinaryString(prefix6)); + assertTrue(matcher.matches()); + + prefix6 = Ip6Prefix.valueOf("2000::2000/116"); + pattern = Pattern.compile("00100{108}0010"); + matcher = pattern.matcher(RouteEntry.createBinaryString(prefix6)); + assertTrue(matcher.matches()); + + prefix6 = Ip6Prefix.valueOf("2000::1234/128"); + pattern = Pattern.compile("00100{108}0001001000110100"); + matcher = pattern.matcher(RouteEntry.createBinaryString(prefix6)); + assertTrue(matcher.matches()); } /** @@ -121,6 +185,16 @@ public class RouteEntryTest { RouteEntry routeEntry2 = new RouteEntry(prefix2, nextHop2); assertThat(routeEntry1, is(routeEntry2)); + + Ip6Prefix prefix3 = Ip6Prefix.valueOf("1000::/64"); + Ip6Address nextHop3 = Ip6Address.valueOf("2000::2"); + RouteEntry routeEntry3 = new RouteEntry(prefix3, nextHop3); + + Ip6Prefix prefix4 = Ip6Prefix.valueOf("1000::/64"); + Ip6Address nextHop4 = Ip6Address.valueOf("2000::2"); + RouteEntry routeEntry4 = new RouteEntry(prefix4, nextHop4); + + assertThat(routeEntry3, is(routeEntry4)); } /** @@ -142,6 +216,21 @@ public class RouteEntryTest { assertThat(routeEntry1, Matchers.is(not(routeEntry2))); assertThat(routeEntry1, Matchers.is(not(routeEntry3))); + + Ip6Prefix prefix4 = Ip6Prefix.valueOf("1000::/64"); + Ip6Address nextHop4 = Ip6Address.valueOf("2000::1"); + RouteEntry routeEntry4 = new RouteEntry(prefix4, nextHop4); + + Ip6Prefix prefix5 = Ip6Prefix.valueOf("1000::/65"); + Ip6Address nextHop5 = Ip6Address.valueOf("2000::1"); + RouteEntry routeEntry5 = new RouteEntry(prefix5, nextHop5); + + Ip6Prefix prefix6 = Ip6Prefix.valueOf("1000::/64"); + Ip6Address nextHop6 = Ip6Address.valueOf("2000::2"); + RouteEntry routeEntry6 = new RouteEntry(prefix6, nextHop6); + + assertThat(routeEntry4, Matchers.is(not(routeEntry5))); + assertThat(routeEntry4, Matchers.is(not(routeEntry6))); } /** @@ -155,5 +244,12 @@ public class RouteEntryTest { assertThat(routeEntry.toString(), is("RouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8}")); + + Ip6Prefix prefix6 = Ip6Prefix.valueOf("1000::/64"); + Ip6Address nextHop6 = Ip6Address.valueOf("2000::1"); + RouteEntry routeEntry6 = new RouteEntry(prefix6, nextHop6); + + assertThat(routeEntry6.toString(), + is("RouteEntry{prefix=1000::/64, nextHop=2000::1}")); } } diff --git a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java index 45206903..c58bc1b9 100644 --- a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java +++ b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/Configuration.java @@ -32,8 +32,8 @@ import java.util.List; public class Configuration { // We call the BGP routers in our SDN network the BGP speakers, and call // the BGP routers outside our SDN network the BGP peers. - private List<BgpSpeaker> bgpSpeakers; - private List<BgpPeer> peers; + private List<BgpSpeaker> bgpSpeakers = Collections.emptyList(); + private List<BgpPeer> peers = Collections.emptyList(); private MacAddress virtualGatewayMacAddress; // All IP prefixes from the configuration are local diff --git a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java index 0a6f9d4c..19c3f70b 100644 --- a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java +++ b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/config/impl/RoutingConfigurationImpl.java @@ -195,13 +195,16 @@ public class RoutingConfigurationImpl implements RoutingConfigurationService { } BgpConfig bgpConfig = configService.getConfig(routerAppId, BgpConfig.class); - - return bgpConfig.bgpSpeakers().stream() - .flatMap(speaker -> speaker.peers().stream()) - .map(peer -> interfaceService.getMatchingInterface(peer)) - .filter(intf -> intf != null) - .map(intf -> intf.connectPoint()) - .collect(Collectors.toSet()); + if (bgpConfig == null) { + return Collections.emptySet(); + } else { + return bgpConfig.bgpSpeakers().stream() + .flatMap(speaker -> speaker.peers().stream()) + .map(peer -> interfaceService.getMatchingInterface(peer)) + .filter(intf -> intf != null) + .map(intf -> intf.connectPoint()) + .collect(Collectors.toSet()); + } } @Override diff --git a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java index 6700d530..75d789ab 100644 --- a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java +++ b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/Router.java @@ -35,9 +35,7 @@ import org.onlab.packet.IpAddress; import org.onlab.packet.IpPrefix; import org.onlab.packet.MacAddress; import org.onosproject.core.CoreService; -import org.onosproject.incubator.net.intf.Interface; import org.onosproject.incubator.net.intf.InterfaceService; -import org.onosproject.net.ConnectPoint; import org.onosproject.net.Host; import org.onosproject.net.host.HostEvent; import org.onosproject.net.host.HostListener; @@ -46,7 +44,6 @@ import org.onosproject.routing.BgpService; import org.onosproject.routing.FibEntry; import org.onosproject.routing.FibListener; import org.onosproject.routing.FibUpdate; -import org.onosproject.routing.IntentRequestListener; import org.onosproject.routing.RouteEntry; import org.onosproject.routing.RouteListener; import org.onosproject.routing.RouteUpdate; @@ -61,7 +58,6 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; @@ -100,7 +96,6 @@ public class Router implements RoutingService { private final Map<IpAddress, MacAddress> ip2Mac = new ConcurrentHashMap<>(); private FibListener fibComponent; - private IntentRequestListener intentRequestListener; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected CoreService coreService; @@ -145,12 +140,6 @@ public class Router implements RoutingService { @Override public void addFibListener(FibListener fibListener) { this.fibComponent = checkNotNull(fibListener); - - } - - @Override - public void addIntentRequestListener(IntentRequestListener intentRequestListener) { - this.intentRequestListener = checkNotNull(intentRequestListener); } @Override @@ -287,12 +276,10 @@ public class Router implements RoutingService { void addRibRoute(RouteEntry routeEntry) { if (routeEntry.isIp4()) { // IPv4 - ribTable4.put(createBinaryString(routeEntry.prefix()), - routeEntry); + ribTable4.put(createBinaryString(routeEntry.prefix()), routeEntry); } else { // IPv6 - ribTable6.put(createBinaryString(routeEntry.prefix()), - routeEntry); + ribTable6.put(createBinaryString(routeEntry.prefix()), routeEntry); } } @@ -553,17 +540,6 @@ public class Router implements RoutingService { } @Override - public LocationType getLocationType(IpAddress ipAddress) { - if (routingConfigurationService.isIpAddressLocal(ipAddress)) { - return LocationType.LOCAL; - } else if (getLongestMatchableRouteEntry(ipAddress) != null) { - return LocationType.INTERNET; - } else { - return LocationType.NO_ROUTE; - } - } - - @Override public RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress) { RouteEntry routeEntry = null; Iterable<RouteEntry> routeEntries; @@ -587,142 +563,4 @@ public class Router implements RoutingService { return routeEntry; } - @Override - public ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress) { - LocationType type = getLocationType(dstIpAddress); - if (type == LocationType.LOCAL) { - Set<Host> hosts = hostService.getHostsByIp(dstIpAddress); - if (!hosts.isEmpty()) { - return hosts.iterator().next().location(); - } else { - hostService.startMonitoringIp(dstIpAddress); - return null; - } - } else if (type == LocationType.INTERNET) { - IpAddress nextHopIpAddress = null; - RouteEntry routeEntry = getLongestMatchableRouteEntry(dstIpAddress); - if (routeEntry != null) { - nextHopIpAddress = routeEntry.nextHop(); - Interface it = interfaceService.getMatchingInterface(nextHopIpAddress); - if (it != null) { - return it.connectPoint(); - } else { - return null; - } - } else { - return null; - } - } else { - return null; - } - } - - @Override - public void packetReactiveProcessor(IpAddress dstIpAddress, - IpAddress srcIpAddress, - ConnectPoint srcConnectPoint, - MacAddress srcMacAddress) { - checkNotNull(dstIpAddress); - checkNotNull(srcIpAddress); - checkNotNull(srcConnectPoint); - checkNotNull(srcMacAddress); - - // - // Step1: Try to update the existing intent first if it exists. - // - IpPrefix ipPrefix = null; - if (routingConfigurationService.isIpAddressLocal(dstIpAddress)) { - if (dstIpAddress.isIp4()) { - ipPrefix = IpPrefix.valueOf(dstIpAddress, - Ip4Address.BIT_LENGTH); - } else { - ipPrefix = IpPrefix.valueOf(dstIpAddress, - Ip6Address.BIT_LENGTH); - } - } else { - // Get IP prefix from BGP route table - RouteEntry routeEntry = getLongestMatchableRouteEntry(dstIpAddress); - if (routeEntry != null) { - ipPrefix = routeEntry.prefix(); - } - } - if (ipPrefix != null - && intentRequestListener.mp2pIntentExists(ipPrefix)) { - intentRequestListener.updateExistingMp2pIntent(ipPrefix, - srcConnectPoint); - return; - } - - // - // Step2: There is no existing intent for the destination IP address. - // Check whether it is necessary to create a new one. If necessary then - // create a new one. - // - TrafficType trafficType = - trafficTypeClassifier(srcConnectPoint, dstIpAddress); - - switch (trafficType) { - case HOST_TO_INTERNET: - // If the destination IP address is outside the local SDN network. - // The Step 1 has already handled it. We do not need to do anything here. - break; - case INTERNET_TO_HOST: - intentRequestListener.setUpConnectivityInternetToHost(dstIpAddress); - break; - case HOST_TO_HOST: - intentRequestListener.setUpConnectivityHostToHost(dstIpAddress, - srcIpAddress, srcMacAddress, srcConnectPoint); - break; - case INTERNET_TO_INTERNET: - log.trace("This is transit traffic, " - + "the intent should be preinstalled already"); - break; - case DROP: - // TODO here should setUpDropPaccketIntent(...); - // We need a new type of intent here. - break; - case UNKNOWN: - log.trace("This is unknown traffic, so we do nothing"); - break; - default: - break; - } - } - - /** - * Classifies the traffic and return the traffic type. - * - * @param srcConnectPoint the connect point where the packet comes from - * @param dstIp the destination IP address in packet - * @return the traffic type which this packet belongs to - */ - private TrafficType trafficTypeClassifier(ConnectPoint srcConnectPoint, - IpAddress dstIp) { - LocationType dstIpLocationType = getLocationType(dstIp); - Optional<Interface> srcInterface = - interfaceService.getInterfacesByPort(srcConnectPoint).stream().findFirst(); - - switch (dstIpLocationType) { - case INTERNET: - if (!srcInterface.isPresent()) { - return TrafficType.HOST_TO_INTERNET; - } else { - return TrafficType.INTERNET_TO_INTERNET; - } - case LOCAL: - if (!srcInterface.isPresent()) { - return TrafficType.HOST_TO_HOST; - } else { - // TODO Currently we only consider local public prefixes. - // In the future, we will consider the local private prefixes. - // If dstIpLocationType is a local private, we should return - // TrafficType.DROP. - return TrafficType.INTERNET_TO_HOST; - } - case NO_ROUTE: - return TrafficType.DROP; - default: - return TrafficType.UNKNOWN; - } - } } diff --git a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/StaticRouter.java b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/StaticRouter.java index 29526fff..3c868202 100644 --- a/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/StaticRouter.java +++ b/framework/src/onos/apps/routing/src/main/java/org/onosproject/routing/impl/StaticRouter.java @@ -18,10 +18,7 @@ package org.onosproject.routing.impl; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Service; import org.onlab.packet.IpAddress; -import org.onlab.packet.MacAddress; -import org.onosproject.net.ConnectPoint; import org.onosproject.routing.FibListener; -import org.onosproject.routing.IntentRequestListener; import org.onosproject.routing.RouteEntry; import org.onosproject.routing.RoutingService; import org.onosproject.routing.StaticRoutingService; @@ -49,11 +46,6 @@ public class StaticRouter implements RoutingService, StaticRoutingService { } @Override - public void addIntentRequestListener(IntentRequestListener intentRequestListener) { - - } - - @Override public void stop() { } @@ -69,27 +61,11 @@ public class StaticRouter implements RoutingService, StaticRoutingService { } @Override - public LocationType getLocationType(IpAddress ipAddress) { - return null; - } - - @Override public RouteEntry getLongestMatchableRouteEntry(IpAddress ipAddress) { return null; } @Override - public ConnectPoint getEgressConnectPoint(IpAddress dstIpAddress) { - return null; - } - - @Override - public void packetReactiveProcessor(IpAddress dstIpAddress, IpAddress srcIpAddress, - ConnectPoint srcConnectPoint, MacAddress srcMacAddress) { - - } - - @Override public FibListener getFibListener() { return fibListener; } diff --git a/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java index 45bc309f..c73e18cb 100644 --- a/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java +++ b/framework/src/onos/apps/routing/src/test/java/org/onosproject/routing/impl/RouterTest.java @@ -22,6 +22,8 @@ import org.junit.Before; import org.junit.Test; import org.onlab.packet.Ip4Address; import org.onlab.packet.Ip4Prefix; +import org.onlab.packet.Ip6Address; +import org.onlab.packet.Ip6Prefix; import org.onlab.packet.IpAddress; import org.onlab.packet.IpPrefix; import org.onlab.packet.MacAddress; @@ -81,6 +83,13 @@ public class RouterTest { DeviceId.deviceId("of:0000000000000004"), PortNumber.portNumber(1)); + private static final ConnectPoint SW5_ETH1 = new ConnectPoint( + DeviceId.deviceId("of:0000000000000005"), + PortNumber.portNumber(1)); + + private static final ConnectPoint SW6_ETH1 = new ConnectPoint( + DeviceId.deviceId("of:0000000000000006"), + PortNumber.portNumber(1)); private Router router; @Before @@ -132,7 +141,6 @@ public class RouterTest { hostService.startMonitoringIp(host1Address); expectLastCall().anyTimes(); - IpAddress host2Address = IpAddress.valueOf("192.168.20.1"); Host host2 = new DefaultHost(ProviderId.NONE, HostId.NONE, MacAddress.valueOf("00:00:00:00:00:02"), VlanId.NONE, @@ -148,7 +156,7 @@ public class RouterTest { IpAddress host3Address = IpAddress.valueOf("192.168.40.1"); Host host3 = new DefaultHost(ProviderId.NONE, HostId.NONE, MacAddress.valueOf("00:00:00:00:00:03"), VlanId.vlanId((short) 1), - new HostLocation(SW4_ETH1, 1), + new HostLocation(SW3_ETH1, 1), Sets.newHashSet(host3Address)); expect(hostService.getHostsByIp(host3Address)) @@ -156,6 +164,41 @@ public class RouterTest { hostService.startMonitoringIp(host3Address); expectLastCall().anyTimes(); + IpAddress host4Address = IpAddress.valueOf("1000::1"); + Host host4 = new DefaultHost(ProviderId.NONE, HostId.NONE, + MacAddress.valueOf("00:00:00:00:00:04"), VlanId.NONE, + new HostLocation(SW4_ETH1, 1), + Sets.newHashSet(host4Address)); + + expect(hostService.getHostsByIp(host4Address)) + .andReturn(Sets.newHashSet(host4)).anyTimes(); + hostService.startMonitoringIp(host4Address); + expectLastCall().anyTimes(); + + IpAddress host5Address = IpAddress.valueOf("2000::1"); + Host host5 = new DefaultHost(ProviderId.NONE, HostId.NONE, + MacAddress.valueOf("00:00:00:00:00:05"), VlanId.NONE, + new HostLocation(SW5_ETH1, 1), + Sets.newHashSet(host5Address)); + + expect(hostService.getHostsByIp(host5Address)) + .andReturn(Sets.newHashSet(host5)).anyTimes(); + hostService.startMonitoringIp(host5Address); + expectLastCall().anyTimes(); + + // Next hop on a VLAN + IpAddress host6Address = IpAddress.valueOf("3000::1"); + Host host6 = new DefaultHost(ProviderId.NONE, HostId.NONE, + MacAddress.valueOf("00:00:00:00:00:06"), VlanId.vlanId((short) 1), + new HostLocation(SW6_ETH1, 1), + Sets.newHashSet(host6Address)); + + expect(hostService.getHostsByIp(host6Address)) + .andReturn(Sets.newHashSet(host6)).anyTimes(); + hostService.startMonitoringIp(host6Address); + expectLastCall().anyTimes(); + + // Called during shutdown hostService.removeListener(anyObject(HostListener.class)); @@ -163,10 +206,10 @@ public class RouterTest { } /** - * Tests adding a route entry. + * Tests adding a IPv4 route entry. */ @Test - public void testRouteAdd() { + public void testIpv4RouteAdd() { // Construct a route entry IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24"); IpAddress nextHopIp = Ip4Address.valueOf("192.168.10.1"); @@ -175,7 +218,34 @@ public class RouterTest { // Expected FIB entry FibEntry fibEntry = new FibEntry(prefix, nextHopIp, - MacAddress.valueOf("00:00:00:00:00:01")); + MacAddress.valueOf("00:00:00:00:00:01")); + + fibListener.update(Collections.singletonList(new FibUpdate( + FibUpdate.Type.UPDATE, fibEntry)), Collections.emptyList()); + + replay(fibListener); + + router.processRouteUpdates(Collections.singletonList( + new RouteUpdate(RouteUpdate.Type.UPDATE, routeEntry))); + + verify(fibListener); + } + + + /** + * Tests adding a IPv6 route entry. + */ + @Test + public void testIpv6RouteAdd() { + // Construct a route entry + IpPrefix prefix = Ip6Prefix.valueOf("4000::/64"); + IpAddress nextHopIp = Ip6Address.valueOf("1000::1"); + + RouteEntry routeEntry = new RouteEntry(prefix, nextHopIp); + + // Expected FIB entry + FibEntry fibEntry = new FibEntry(prefix, nextHopIp, + MacAddress.valueOf("00:00:00:00:00:04")); fibListener.update(Collections.singletonList(new FibUpdate( FibUpdate.Type.UPDATE, fibEntry)), Collections.emptyList()); @@ -188,13 +258,14 @@ public class RouterTest { verify(fibListener); } + /** - * Tests updating a route entry. + * Tests updating a IPv4 route entry. */ @Test public void testRouteUpdate() { // Firstly add a route - testRouteAdd(); + testIpv4RouteAdd(); // Route entry with updated next hop for the original prefix RouteEntry routeEntryUpdate = new RouteEntry( @@ -230,12 +301,53 @@ public class RouterTest { } /** - * Tests deleting a route entry. + * Tests updating a IPv6 route entry. */ @Test - public void testRouteDelete() { + public void testIpv6RouteUpdate() { // Firstly add a route - testRouteAdd(); + testIpv6RouteAdd(); + + // Route entry with updated next hop for the original prefix + RouteEntry routeEntryUpdate = new RouteEntry( + Ip6Prefix.valueOf("4000::/64"), + Ip6Address.valueOf("2000::1")); + + // The old FIB entry will be withdrawn + FibEntry withdrawFibEntry = new FibEntry( + Ip6Prefix.valueOf("4000::/64"), null, null); + + // A new FIB entry will be added + FibEntry updateFibEntry = new FibEntry( + Ip6Prefix.valueOf("4000::/64"), + Ip6Address.valueOf("2000::1"), + MacAddress.valueOf("00:00:00:00:00:05")); + + reset(fibListener); + fibListener.update(Collections.singletonList(new FibUpdate( + FibUpdate.Type.UPDATE, updateFibEntry)), + Collections.singletonList(new FibUpdate( + FibUpdate.Type.DELETE, withdrawFibEntry))); + replay(fibListener); + + reset(routingConfigurationService); + expect(routingConfigurationService.isIpPrefixLocal( + anyObject(IpPrefix.class))).andReturn(false); + replay(routingConfigurationService); + + router.processRouteUpdates(Collections.singletonList(new RouteUpdate( + RouteUpdate.Type.UPDATE, routeEntryUpdate))); + + verify(fibListener); + } + + /** + * Tests deleting a IPv4 route entry. + */ + @Test + public void testIpv4RouteDelete() { + // Firstly add a route + testIpv4RouteAdd(); RouteEntry deleteRouteEntry = new RouteEntry( Ip4Prefix.valueOf("1.1.1.0/24"), @@ -257,10 +369,37 @@ public class RouterTest { } /** - * Tests adding a route whose next hop is the local BGP speaker. + * Tests deleting a IPv6 route entry. */ @Test - public void testLocalRouteAdd() { + public void testIpv6RouteDelete() { + // Firstly add a route + testIpv6RouteAdd(); + + RouteEntry deleteRouteEntry = new RouteEntry( + Ip6Prefix.valueOf("4000::/64"), + Ip6Address.valueOf("1000::1")); + + FibEntry deleteFibEntry = new FibEntry( + Ip6Prefix.valueOf("4000::/64"), null, null); + + reset(fibListener); + fibListener.update(Collections.emptyList(), Collections.singletonList( + new FibUpdate(FibUpdate.Type.DELETE, deleteFibEntry))); + + replay(fibListener); + + router.processRouteUpdates(Collections.singletonList( + new RouteUpdate(RouteUpdate.Type.DELETE, deleteRouteEntry))); + + verify(fibListener); + } + + /** + * Tests adding a IPv4 route whose next hop is the local BGP speaker. + */ + @Test + public void testIpv4LocalRouteAdd() { // Construct a route entry, the next hop is the local BGP speaker RouteEntry routeEntry = new RouteEntry( Ip4Prefix.valueOf("1.1.1.0/24"), @@ -284,4 +423,33 @@ public class RouterTest { assertTrue(router.getRoutes4().contains(routeEntry)); verify(fibListener); } + + /** + * Tests adding a IPv6 route whose next hop is the local BGP speaker. + */ + @Test + public void testIpv6LocalRouteAdd() { + // Construct a route entry, the next hop is the local BGP speaker + RouteEntry routeEntry = new RouteEntry( + Ip6Prefix.valueOf("4000::/64"), + Ip6Address.valueOf("::")); + + // No methods on the FIB listener should be called + replay(fibListener); + + reset(routingConfigurationService); + expect(routingConfigurationService.isIpPrefixLocal( + anyObject(IpPrefix.class))).andReturn(true); + replay(routingConfigurationService); + + // Call the processRouteUpdates() method in Router class + RouteUpdate routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE, + routeEntry); + router.processRouteUpdates(Collections.singletonList(routeUpdate)); + + // Verify + assertEquals(1, router.getRoutes6().size()); + assertTrue(router.getRoutes6().contains(routeEntry)); + verify(fibListener); + } } diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java index d8d8f45d..eaabed33 100644 --- a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentSynchronizer.java @@ -15,118 +15,79 @@ */ package org.onosproject.sdnip; -import com.google.common.collect.ImmutableList; -import com.google.common.util.concurrent.ThreadFactoryBuilder; -import org.onlab.packet.Ethernet; -import org.onlab.packet.IpAddress; -import org.onlab.packet.IpPrefix; -import org.onlab.packet.MacAddress; -import org.onlab.packet.VlanId; import org.onosproject.core.ApplicationId; -import org.onosproject.incubator.net.intf.Interface; -import org.onosproject.incubator.net.intf.InterfaceService; -import org.onosproject.net.ConnectPoint; -import org.onosproject.net.Host; -import org.onosproject.net.flow.DefaultTrafficSelector; -import org.onosproject.net.flow.DefaultTrafficTreatment; -import org.onosproject.net.flow.TrafficSelector; -import org.onosproject.net.flow.TrafficTreatment; -import org.onosproject.net.flow.criteria.Criterion; -import org.onosproject.net.flow.criteria.IPCriterion; -import org.onosproject.net.host.HostService; -import org.onosproject.net.intent.Constraint; import org.onosproject.net.intent.Intent; import org.onosproject.net.intent.IntentService; import org.onosproject.net.intent.IntentState; import org.onosproject.net.intent.Key; -import org.onosproject.net.intent.MultiPointToSinglePointIntent; -import org.onosproject.net.intent.PointToPointIntent; -import org.onosproject.net.intent.constraint.PartialFailureConstraint; -import org.onosproject.routing.FibListener; -import org.onosproject.routing.FibUpdate; -import org.onosproject.routing.IntentRequestListener; -import org.onosproject.routing.config.RoutingConfigurationService; +import org.onosproject.routing.IntentSynchronizationService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Semaphore; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.concurrent.Executors.newSingleThreadExecutor; +import static org.onlab.util.Tools.groupedThreads; /** * Synchronizes intents between the in-memory intent store and the * IntentService. */ -public class IntentSynchronizer implements FibListener, IntentRequestListener { - private static final int PRIORITY_OFFSET = 100; - private static final int PRIORITY_MULTIPLIER = 5; - protected static final ImmutableList<Constraint> CONSTRAINTS - = ImmutableList.of(new PartialFailureConstraint()); +public class IntentSynchronizer implements IntentSynchronizationService { private static final Logger log = LoggerFactory.getLogger(IntentSynchronizer.class); private final ApplicationId appId; private final IntentService intentService; - private final HostService hostService; - private final InterfaceService interfaceService; - private final Map<IntentKey, PointToPointIntent> peerIntents; - private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents; + + private final Map<Key, Intent> intents; // // State to deal with SDN-IP Leader election and pushing Intents // private final ExecutorService bgpIntentsSynchronizerExecutor; - private final Semaphore intentsSynchronizerSemaphore = new Semaphore(0); private volatile boolean isElectedLeader = false; private volatile boolean isActivatedLeader = false; - private final RoutingConfigurationService configService; + /** + * Class constructor. + * + * @param appId the Application ID + * @param intentService the intent service + */ + IntentSynchronizer(ApplicationId appId, IntentService intentService) { + this(appId, intentService, + newSingleThreadExecutor(groupedThreads("onos/sdnip", "sync"))); + } /** * Class constructor. * * @param appId the Application ID * @param intentService the intent service - * @param hostService the host service - * @param configService the SDN-IP configuration service - * @param interfaceService the interface service + * @param executorService executor service for synchronization thread */ IntentSynchronizer(ApplicationId appId, IntentService intentService, - HostService hostService, - RoutingConfigurationService configService, - InterfaceService interfaceService) { + ExecutorService executorService) { this.appId = appId; this.intentService = intentService; - this.hostService = hostService; - this.interfaceService = interfaceService; - peerIntents = new ConcurrentHashMap<>(); - routeIntents = new ConcurrentHashMap<>(); - this.configService = configService; + intents = new ConcurrentHashMap<>(); - bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor( - new ThreadFactoryBuilder() - .setNameFormat("sdnip-intents-synchronizer-%d").build()); + bgpIntentsSynchronizerExecutor = executorService; } /** * Starts the synchronizer. */ public void start() { - bgpIntentsSynchronizerExecutor.execute(this::doIntentSynchronizationThread); + } /** @@ -187,794 +148,118 @@ public class IntentSynchronizer implements FibListener, IntentRequestListener { } } - /** - * Signals the synchronizer that the SDN-IP leadership has changed. - * - * @param isLeader true if this instance is now the leader, otherwise false - */ - public void leaderChanged(boolean isLeader) { - log.debug("SDN-IP Leader changed: {}", isLeader); - - if (!isLeader) { - this.isElectedLeader = false; - this.isActivatedLeader = false; - return; // Nothing to do - } - this.isActivatedLeader = false; - this.isElectedLeader = true; - - // - // Tell the Intents Synchronizer thread to start the synchronization - // - intentsSynchronizerSemaphore.release(); - } - - /** - * Gets the route intents. - * - * @return the route intents - */ - public Collection<MultiPointToSinglePointIntent> getRouteIntents() { - List<MultiPointToSinglePointIntent> result = new LinkedList<>(); - - for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry : - routeIntents.entrySet()) { - result.add(entry.getValue()); - } - return result; - } - - /** - * Thread for Intent Synchronization. - */ - private void doIntentSynchronizationThread() { - boolean interrupted = false; - try { - while (!interrupted) { - try { - intentsSynchronizerSemaphore.acquire(); - // - // Drain all permits, because a single synchronization is - // sufficient. - // - intentsSynchronizerSemaphore.drainPermits(); - } catch (InterruptedException e) { - interrupted = true; - break; - } - synchronizeIntents(); - } - } finally { - if (interrupted) { - Thread.currentThread().interrupt(); - } - } - } - - /** - * Submits a collection of point-to-point intents. - * - * @param intents the intents to submit - */ - void submitPeerIntents(Collection<PointToPointIntent> intents) { + @Override + public void submit(Intent intent) { synchronized (this) { - // Store the intents in memory - for (PointToPointIntent intent : intents) { - peerIntents.put(new IntentKey(intent), intent); - } - - // Push the intents + intents.put(intent.key(), intent); if (isElectedLeader && isActivatedLeader) { - log.debug("SDN-IP Submitting all Peer Intents..."); - for (Intent intent : intents) { - log.trace("SDN-IP Submitting intents: {}", intent); - intentService.submit(intent); - } + log.trace("SDN-IP Submitting intent: {}", intent); + intentService.submit(intent); } } } - /** - * Submits a MultiPointToSinglePointIntent for reactive routing. - * - * @param ipPrefix the IP prefix to match in a MultiPointToSinglePointIntent - * @param intent the intent to submit - */ - void submitReactiveIntent(IpPrefix ipPrefix, MultiPointToSinglePointIntent intent) { + @Override + public void withdraw(Intent intent) { synchronized (this) { - // Store the intent in memory - routeIntents.put(ipPrefix, intent); - - // Push the intent + intents.remove(intent.key(), intent); if (isElectedLeader && isActivatedLeader) { - log.trace("SDN-IP submitting reactive routing intent: {}", intent); - intentService.submit(intent); + log.trace("SDN-IP Withdrawing intent: {}", intent); + intentService.withdraw(intent); } } } /** - * Generates a route intent for a prefix, the next hop IP address, and - * the next hop MAC address. - * <p/> - * This method will find the egress interface for the intent. - * Intent will match dst IP prefix and rewrite dst MAC address at all other - * border switches, then forward packets according to dst MAC address. + * Signals the synchronizer that the SDN-IP leadership has changed. * - * @param prefix IP prefix of the route to add - * @param nextHopIpAddress IP address of the next hop - * @param nextHopMacAddress MAC address of the next hop - * @return the generated intent, or null if no intent should be submitted + * @param isLeader true if this instance is now the leader, otherwise false */ - private MultiPointToSinglePointIntent generateRouteIntent( - IpPrefix prefix, - IpAddress nextHopIpAddress, - MacAddress nextHopMacAddress) { - - // Find the attachment point (egress interface) of the next hop - Interface egressInterface = interfaceService.getMatchingInterface(nextHopIpAddress); - if (egressInterface == null) { - log.warn("No outgoing interface found for {}", - nextHopIpAddress); - return null; - } - - // - // Generate the intent itself - // - Set<ConnectPoint> ingressPorts = new HashSet<>(); - ConnectPoint egressPort = egressInterface.connectPoint(); - log.debug("Generating intent for prefix {}, next hop mac {}", - prefix, nextHopMacAddress); - - for (Interface intf : interfaceService.getInterfaces()) { - // TODO this should be only peering interfaces - if (!intf.connectPoint().equals(egressInterface.connectPoint())) { - ConnectPoint srcPort = intf.connectPoint(); - ingressPorts.add(srcPort); - } - } - - // Match the destination IP prefix at the first hop - TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); - if (prefix.isIp4()) { - selector.matchEthType(Ethernet.TYPE_IPV4); - selector.matchIPDst(prefix); - } else { - selector.matchEthType(Ethernet.TYPE_IPV6); - selector.matchIPv6Dst(prefix); - } - - // Rewrite the destination MAC address - TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder() - .setEthDst(nextHopMacAddress); - if (!egressInterface.vlan().equals(VlanId.NONE)) { - treatment.setVlanId(egressInterface.vlan()); - // If we set VLAN ID, we have to make sure a VLAN tag exists. - // TODO support no VLAN -> VLAN routing - selector.matchVlanId(VlanId.ANY); - } - - int priority = - prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET; - Key key = Key.of(prefix.toString(), appId); - return MultiPointToSinglePointIntent.builder() - .appId(appId) - .key(key) - .selector(selector.build()) - .treatment(treatment.build()) - .ingressPoints(ingressPorts) - .egressPoint(egressPort) - .priority(priority) - .constraints(CONSTRAINTS) - .build(); - } - - @Override - public void setUpConnectivityInternetToHost(IpAddress hostIpAddress) { - checkNotNull(hostIpAddress); - Set<ConnectPoint> ingressPoints = - configService.getBgpPeerConnectPoints(); - - TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); - - if (hostIpAddress.isIp4()) { - selector.matchEthType(Ethernet.TYPE_IPV4); - } else { - selector.matchEthType(Ethernet.TYPE_IPV6); - } + public void leaderChanged(boolean isLeader) { + log.debug("SDN-IP Leader changed: {}", isLeader); - // Match the destination IP prefix at the first hop - IpPrefix ipPrefix = hostIpAddress.toIpPrefix(); - selector.matchIPDst(ipPrefix); - - // Rewrite the destination MAC address - MacAddress hostMac = null; - ConnectPoint egressPoint = null; - for (Host host : hostService.getHostsByIp(hostIpAddress)) { - if (host.mac() != null) { - hostMac = host.mac(); - egressPoint = host.location(); - break; - } - } - if (hostMac == null) { - hostService.startMonitoringIp(hostIpAddress); - return; + if (!isLeader) { + this.isElectedLeader = false; + this.isActivatedLeader = false; + return; // Nothing to do } + this.isActivatedLeader = false; + this.isElectedLeader = true; - TrafficTreatment.Builder treatment = - DefaultTrafficTreatment.builder().setEthDst(hostMac); - Key key = Key.of(ipPrefix.toString(), appId); - int priority = ipPrefix.prefixLength() * PRIORITY_MULTIPLIER - + PRIORITY_OFFSET; - MultiPointToSinglePointIntent intent = - MultiPointToSinglePointIntent.builder() - .appId(appId) - .key(key) - .selector(selector.build()) - .treatment(treatment.build()) - .ingressPoints(ingressPoints) - .egressPoint(egressPoint) - .priority(priority) - .constraints(CONSTRAINTS) - .build(); - - log.trace("Generates ConnectivityInternetToHost intent {}", intent); - submitReactiveIntent(ipPrefix, intent); - } - - - @Override - public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) { - // - // NOTE: Semantically, we MUST withdraw existing intents before - // submitting new intents. - // - synchronized (this) { - MultiPointToSinglePointIntent intent; - - log.debug("SDN-IP submitting intents = {} withdrawing = {}", - updates.size(), withdraws.size()); - - // - // Prepare the Intent batch operations for the intents to withdraw - // - for (FibUpdate withdraw : withdraws) { - checkArgument(withdraw.type() == FibUpdate.Type.DELETE, - "FibUpdate with wrong type in withdraws list"); - - IpPrefix prefix = withdraw.entry().prefix(); - intent = routeIntents.remove(prefix); - if (intent == null) { - log.trace("SDN-IP No intent in routeIntents to delete " + - "for prefix: {}", prefix); - continue; - } - if (isElectedLeader && isActivatedLeader) { - log.trace("SDN-IP Withdrawing intent: {}", intent); - intentService.withdraw(intent); - } - } - - // - // Prepare the Intent batch operations for the intents to submit - // - for (FibUpdate update : updates) { - checkArgument(update.type() == FibUpdate.Type.UPDATE, - "FibUpdate with wrong type in updates list"); - - IpPrefix prefix = update.entry().prefix(); - intent = generateRouteIntent(prefix, update.entry().nextHopIp(), - update.entry().nextHopMac()); - - if (intent == null) { - // This preserves the old semantics - if an intent can't be - // generated, we don't do anything with that prefix. But - // perhaps we should withdraw the old intent anyway? - continue; - } - - MultiPointToSinglePointIntent oldIntent = - routeIntents.put(prefix, intent); - if (isElectedLeader && isActivatedLeader) { - if (oldIntent != null) { - log.trace("SDN-IP Withdrawing old intent: {}", - oldIntent); - intentService.withdraw(oldIntent); - } - log.trace("SDN-IP Submitting intent: {}", intent); - intentService.submit(intent); - } - } - } + // Run the synchronization method off-thread + bgpIntentsSynchronizerExecutor.execute(this::synchronizeIntents); } - /** - * Synchronize the in-memory Intents with the Intents in the Intent - * framework. - */ - void synchronizeIntents() { - synchronized (this) { - - Map<IntentKey, Intent> localIntents = new HashMap<>(); - Map<IntentKey, Intent> fetchedIntents = new HashMap<>(); - Collection<Intent> storeInMemoryIntents = new LinkedList<>(); - Collection<Intent> addIntents = new LinkedList<>(); - Collection<Intent> deleteIntents = new LinkedList<>(); - - if (!isElectedLeader) { - return; // Nothing to do: not the leader anymore - } - log.debug("SDN-IP synchronizing all intents..."); - - // Prepare the local intents - for (Intent intent : routeIntents.values()) { - localIntents.put(new IntentKey(intent), intent); - } - for (Intent intent : peerIntents.values()) { - localIntents.put(new IntentKey(intent), intent); + private void synchronizeIntents() { + Map<Key, Intent> serviceIntents = new HashMap<>(); + intentService.getIntents().forEach(i -> { + if (i.appId().equals(appId)) { + serviceIntents.put(i.key(), i); } + }); - // Fetch all intents for this application - for (Intent intent : intentService.getIntents()) { - if (!intent.appId().equals(appId)) { - continue; - } - fetchedIntents.put(new IntentKey(intent), intent); - } - if (log.isDebugEnabled()) { - for (Intent intent: fetchedIntents.values()) { - log.trace("SDN-IP Intent Synchronizer: fetched intent: {}", - intent); - } - } - - computeIntentsDelta(localIntents, fetchedIntents, - storeInMemoryIntents, addIntents, - deleteIntents); + List<Intent> intentsToAdd = new LinkedList<>(); + List<Intent> intentsToRemove = new LinkedList<>(); - // - // Perform the actions: - // 1. Store in memory fetched intents that are same. Can be done - // even if we are not the leader anymore - // 2. Delete intents: check if the leader before the operation - // 3. Add intents: check if the leader before the operation - // - for (Intent intent : storeInMemoryIntents) { - // Store the intent in memory based on its type - if (intent instanceof MultiPointToSinglePointIntent) { - MultiPointToSinglePointIntent mp2pIntent = - (MultiPointToSinglePointIntent) intent; - // Find the IP prefix - Criterion c = - mp2pIntent.selector().getCriterion(Criterion.Type.IPV4_DST); - if (c == null) { - // Try IPv6 - c = - mp2pIntent.selector().getCriterion(Criterion.Type.IPV6_DST); - } - if (c != null && c instanceof IPCriterion) { - IPCriterion ipCriterion = (IPCriterion) c; - IpPrefix ipPrefix = ipCriterion.ip(); - if (ipPrefix == null) { - continue; - } - log.trace("SDN-IP Intent Synchronizer: updating " + - "in-memory Route Intent for prefix {}", - ipPrefix); - routeIntents.put(ipPrefix, mp2pIntent); - } else { - log.warn("SDN-IP no IPV4_DST or IPV6_DST criterion found for Intent {}", - mp2pIntent.id()); - } - continue; - } - if (intent instanceof PointToPointIntent) { - PointToPointIntent p2pIntent = (PointToPointIntent) intent; - log.trace("SDN-IP Intent Synchronizer: updating " + - "in-memory Peer Intent {}", p2pIntent); - peerIntents.put(new IntentKey(intent), p2pIntent); - continue; - } - } - - // Withdraw Intents - for (Intent intent : deleteIntents) { - intentService.withdraw(intent); - log.trace("SDN-IP Intent Synchronizer: withdrawing intent: {}", - intent); - } - if (!isElectedLeader) { - log.trace("SDN-IP Intent Synchronizer: cannot withdraw intents: " + - "not elected leader anymore"); - isActivatedLeader = false; - return; - } - - // Add Intents - for (Intent intent : addIntents) { - intentService.submit(intent); - log.trace("SDN-IP Intent Synchronizer: submitting intent: {}", - intent); - } - if (!isElectedLeader) { - log.trace("SDN-IP Intent Synchronizer: cannot submit intents: " + - "not elected leader anymore"); - isActivatedLeader = false; - return; - } - - if (isElectedLeader) { - isActivatedLeader = true; // Allow push of Intents + for (Intent localIntent : intents.values()) { + Intent serviceIntent = serviceIntents.remove(localIntent.key()); + if (serviceIntent == null) { + intentsToAdd.add(localIntent); } else { - isActivatedLeader = false; - } - log.debug("SDN-IP intent synchronization completed"); - } - } - - /** - * Computes the delta in two sets of Intents: local in-memory Intents, - * and intents fetched from the Intent framework. - * - * @param localIntents the local in-memory Intents - * @param fetchedIntents the Intents fetched from the Intent framework - * @param storeInMemoryIntents the Intents that should be stored in memory. - * Note: This Collection must be allocated by the caller, and it will - * be populated by this method. - * @param addIntents the Intents that should be added to the Intent - * framework. Note: This Collection must be allocated by the caller, and - * it will be populated by this method. - * @param deleteIntents the Intents that should be deleted from the Intent - * framework. Note: This Collection must be allocated by the caller, and - * it will be populated by this method. - */ - private void computeIntentsDelta( - final Map<IntentKey, Intent> localIntents, - final Map<IntentKey, Intent> fetchedIntents, - Collection<Intent> storeInMemoryIntents, - Collection<Intent> addIntents, - Collection<Intent> deleteIntents) { - - // - // Compute the deltas between the LOCAL in-memory Intents and the - // FETCHED Intents: - // - If an Intent is in both the LOCAL and FETCHED sets: - // If the FETCHED Intent is WITHDRAWING or WITHDRAWN, then - // the LOCAL Intent should be added/installed; otherwise the - // FETCHED intent should be stored in the local memory - // (i.e., override the LOCAL Intent) to preserve the original - // Intent ID. - // - if a LOCAL Intent is not in the FETCHED set, then the LOCAL - // Intent should be added/installed. - // - If a FETCHED Intent is not in the LOCAL set, then the FETCHED - // Intent should be deleted/withdrawn. - // - for (Map.Entry<IntentKey, Intent> entry : localIntents.entrySet()) { - IntentKey intentKey = entry.getKey(); - Intent localIntent = entry.getValue(); - Intent fetchedIntent = fetchedIntents.get(intentKey); - - if (fetchedIntent == null) { - // - // No FETCHED Intent found: push the LOCAL Intent. - // - addIntents.add(localIntent); - continue; - } - - IntentState state = - intentService.getIntentState(fetchedIntent.key()); - if (state == null || - state == IntentState.WITHDRAWING || - state == IntentState.WITHDRAWN) { - // The intent has been withdrawn but according to our route - // table it should be installed. We'll reinstall it. - addIntents.add(localIntent); - continue; + IntentState state = intentService.getIntentState(serviceIntent.key()); + if (!IntentUtils.equals(serviceIntent, localIntent) || state == null || + state == IntentState.WITHDRAW_REQ || + state == IntentState.WITHDRAWING || + state == IntentState.WITHDRAWN) { + intentsToAdd.add(localIntent); + } } - storeInMemoryIntents.add(fetchedIntent); } - for (Map.Entry<IntentKey, Intent> entry : fetchedIntents.entrySet()) { - IntentKey intentKey = entry.getKey(); - Intent fetchedIntent = entry.getValue(); - Intent localIntent = localIntents.get(intentKey); - - if (localIntent != null) { - continue; + for (Intent serviceIntent : serviceIntents.values()) { + IntentState state = intentService.getIntentState(serviceIntent.key()); + if (state != null && state != IntentState.WITHDRAW_REQ + && state != IntentState.WITHDRAWING + && state != IntentState.WITHDRAWN) { + intentsToRemove.add(serviceIntent); } - - IntentState state = - intentService.getIntentState(fetchedIntent.key()); - if (state == null || - state == IntentState.WITHDRAWING || - state == IntentState.WITHDRAWN) { - // Nothing to do. The intent has been already withdrawn. - continue; - } - // - // No LOCAL Intent found: delete/withdraw the FETCHED Intent. - // - deleteIntents.add(fetchedIntent); - } - } - - /** - * Helper class that can be used to compute the key for an Intent by - * by excluding the Intent ID. - */ - static final class IntentKey { - private final Intent intent; - - /** - * Constructor. - * - * @param intent the intent to use - */ - IntentKey(Intent intent) { - checkArgument((intent instanceof MultiPointToSinglePointIntent) || - (intent instanceof PointToPointIntent), - "Intent type not recognized", intent); - this.intent = intent; } - /** - * Compares two Multi-Point to Single-Point Intents whether they - * represent same logical intention. - * - * @param intent1 the first Intent to compare - * @param intent2 the second Intent to compare - * @return true if both Intents represent same logical intention, - * otherwise false - */ - static boolean equalIntents(MultiPointToSinglePointIntent intent1, - MultiPointToSinglePointIntent intent2) { - return Objects.equals(intent1.appId(), intent2.appId()) && - Objects.equals(intent1.selector(), intent2.selector()) && - Objects.equals(intent1.treatment(), intent2.treatment()) && - Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) && - Objects.equals(intent1.egressPoint(), intent2.egressPoint()); - } + log.debug("SDN-IP Intent Synchronizer: submitting {}, withdrawing {}", + intentsToAdd.size(), intentsToRemove.size()); - /** - * Compares two Point-to-Point Intents whether they represent - * same logical intention. - * - * @param intent1 the first Intent to compare - * @param intent2 the second Intent to compare - * @return true if both Intents represent same logical intention, - * otherwise false - */ - static boolean equalIntents(PointToPointIntent intent1, - PointToPointIntent intent2) { - return Objects.equals(intent1.appId(), intent2.appId()) && - Objects.equals(intent1.selector(), intent2.selector()) && - Objects.equals(intent1.treatment(), intent2.treatment()) && - Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) && - Objects.equals(intent1.egressPoint(), intent2.egressPoint()); + // Withdraw Intents + for (Intent intent : intentsToRemove) { + intentService.withdraw(intent); + log.trace("SDN-IP Intent Synchronizer: withdrawing intent: {}", + intent); } - - @Override - public int hashCode() { - if (intent instanceof PointToPointIntent) { - PointToPointIntent p2pIntent = (PointToPointIntent) intent; - return Objects.hash(p2pIntent.appId(), - p2pIntent.resources(), - p2pIntent.selector(), - p2pIntent.treatment(), - p2pIntent.constraints(), - p2pIntent.ingressPoint(), - p2pIntent.egressPoint()); - } - if (intent instanceof MultiPointToSinglePointIntent) { - MultiPointToSinglePointIntent m2pIntent = - (MultiPointToSinglePointIntent) intent; - return Objects.hash(m2pIntent.appId(), - m2pIntent.resources(), - m2pIntent.selector(), - m2pIntent.treatment(), - m2pIntent.constraints(), - m2pIntent.ingressPoints(), - m2pIntent.egressPoint()); - } - checkArgument(false, "Intent type not recognized", intent); - return 0; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if ((obj == null) || (!(obj instanceof IntentKey))) { - return false; - } - IntentKey other = (IntentKey) obj; - - if (this.intent instanceof PointToPointIntent) { - if (!(other.intent instanceof PointToPointIntent)) { - return false; - } - return equalIntents((PointToPointIntent) this.intent, - (PointToPointIntent) other.intent); - } - if (this.intent instanceof MultiPointToSinglePointIntent) { - if (!(other.intent instanceof MultiPointToSinglePointIntent)) { - return false; - } - return equalIntents( - (MultiPointToSinglePointIntent) this.intent, - (MultiPointToSinglePointIntent) other.intent); - } - checkArgument(false, "Intent type not recognized", intent); - return false; + if (!isElectedLeader) { + log.debug("SDN-IP Intent Synchronizer: cannot withdraw intents: " + + "not elected leader anymore"); + isActivatedLeader = false; + return; } - } - @Override - public void setUpConnectivityHostToHost(IpAddress dstIpAddress, - IpAddress srcIpAddress, - MacAddress srcMacAddress, - ConnectPoint srcConnectPoint) { - checkNotNull(dstIpAddress); - checkNotNull(srcIpAddress); - checkNotNull(srcMacAddress); - checkNotNull(srcConnectPoint); - - IpPrefix srcIpPrefix = srcIpAddress.toIpPrefix(); - IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix(); - ConnectPoint dstConnectPoint = null; - MacAddress dstMacAddress = null; - - for (Host host : hostService.getHostsByIp(dstIpAddress)) { - if (host.mac() != null) { - dstMacAddress = host.mac(); - dstConnectPoint = host.location(); - break; - } + // Add Intents + for (Intent intent : intentsToAdd) { + intentService.submit(intent); + log.trace("SDN-IP Intent Synchronizer: submitting intent: {}", + intent); } - if (dstMacAddress == null) { - hostService.startMonitoringIp(dstIpAddress); + if (!isElectedLeader) { + log.debug("SDN-IP Intent Synchronizer: cannot submit intents: " + + "not elected leader anymore"); + isActivatedLeader = false; return; } - // - // Handle intent from source host to destination host - // - MultiPointToSinglePointIntent srcToDstIntent = - hostToHostIntentGenerator(dstIpAddress, dstConnectPoint, - dstMacAddress, srcConnectPoint); - submitReactiveIntent(dstIpPrefix, srcToDstIntent); - - // - // Handle intent from destination host to source host - // - - // Since we proactively handle the intent from destination host to - // source host, we should check whether there is an exiting intent - // first. - if (mp2pIntentExists(srcIpPrefix)) { - updateExistingMp2pIntent(srcIpPrefix, dstConnectPoint); - return; + if (isElectedLeader) { + isActivatedLeader = true; // Allow push of Intents } else { - // There is no existing intent, create a new one. - MultiPointToSinglePointIntent dstToSrcIntent = - hostToHostIntentGenerator(srcIpAddress, srcConnectPoint, - srcMacAddress, dstConnectPoint); - submitReactiveIntent(srcIpPrefix, dstToSrcIntent); + isActivatedLeader = false; } + log.debug("SDN-IP intent synchronization completed"); } - /** - * Generates MultiPointToSinglePointIntent for both source host and - * destination host located in local SDN network. - * - * @param dstIpAddress the destination IP address - * @param dstConnectPoint the destination host connect point - * @param dstMacAddress the MAC address of destination host - * @param srcConnectPoint the connect point where packet-in from - * @return the generated MultiPointToSinglePointIntent - */ - private MultiPointToSinglePointIntent hostToHostIntentGenerator( - IpAddress dstIpAddress, - ConnectPoint dstConnectPoint, - MacAddress dstMacAddress, - ConnectPoint srcConnectPoint) { - checkNotNull(dstIpAddress); - checkNotNull(dstConnectPoint); - checkNotNull(dstMacAddress); - checkNotNull(srcConnectPoint); - - Set<ConnectPoint> ingressPoints = new HashSet<>(); - ingressPoints.add(srcConnectPoint); - IpPrefix dstIpPrefix = dstIpAddress.toIpPrefix(); - - TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); - if (dstIpAddress.isIp4()) { - selector.matchEthType(Ethernet.TYPE_IPV4); - selector.matchIPDst(dstIpPrefix); - } else { - selector.matchEthType(Ethernet.TYPE_IPV6); - selector.matchIPv6Dst(dstIpPrefix); - } - - // Rewrite the destination MAC address - TrafficTreatment.Builder treatment = - DefaultTrafficTreatment.builder().setEthDst(dstMacAddress); - - Key key = Key.of(dstIpPrefix.toString(), appId); - int priority = dstIpPrefix.prefixLength() * PRIORITY_MULTIPLIER - + PRIORITY_OFFSET; - MultiPointToSinglePointIntent intent = - MultiPointToSinglePointIntent.builder() - .appId(appId) - .key(key) - .selector(selector.build()) - .treatment(treatment.build()) - .ingressPoints(ingressPoints) - .egressPoint(dstConnectPoint) - .priority(priority) - .constraints(CONSTRAINTS) - .build(); - - log.trace("Generates ConnectivityHostToHost = {} ", intent); - return intent; - } - - @Override - public void updateExistingMp2pIntent(IpPrefix ipPrefix, - ConnectPoint ingressConnectPoint) { - checkNotNull(ipPrefix); - checkNotNull(ingressConnectPoint); - - MultiPointToSinglePointIntent existingIntent = - getExistingMp2pIntent(ipPrefix); - if (existingIntent != null) { - Set<ConnectPoint> ingressPoints = existingIntent.ingressPoints(); - // Add host connect point into ingressPoints of the existing intent - if (ingressPoints.add(ingressConnectPoint)) { - MultiPointToSinglePointIntent updatedMp2pIntent = - MultiPointToSinglePointIntent.builder() - .appId(appId) - .key(existingIntent.key()) - .selector(existingIntent.selector()) - .treatment(existingIntent.treatment()) - .ingressPoints(ingressPoints) - .egressPoint(existingIntent.egressPoint()) - .priority(existingIntent.priority()) - .constraints(CONSTRAINTS) - .build(); - - log.trace("Update an existing MultiPointToSinglePointIntent " - + "to new intent = {} ", updatedMp2pIntent); - submitReactiveIntent(ipPrefix, updatedMp2pIntent); - } - // If adding ingressConnectPoint to ingressPoints failed, it - // because between the time interval from checking existing intent - // to generating new intent, onos updated this intent due to other - // packet-in and the new intent also includes the - // ingressConnectPoint. This will not affect reactive routing. - } - } - - @Override - public boolean mp2pIntentExists(IpPrefix ipPrefix) { - checkNotNull(ipPrefix); - return routeIntents.get(ipPrefix) != null; - } - - /** - * Gets the existing MultiPointToSinglePointIntent from memory for a given - * IP prefix. - * - * @param ipPrefix the IP prefix used to find MultiPointToSinglePointIntent - * @return the MultiPointToSinglePointIntent if found, otherwise null - */ - private MultiPointToSinglePointIntent getExistingMp2pIntent(IpPrefix - ipPrefix) { - checkNotNull(ipPrefix); - return routeIntents.get(ipPrefix); - } } diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentUtils.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentUtils.java new file mode 100644 index 00000000..8e2a3df3 --- /dev/null +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/IntentUtils.java @@ -0,0 +1,81 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.sdnip; + +import org.onosproject.net.intent.Intent; +import org.onosproject.net.intent.MultiPointToSinglePointIntent; +import org.onosproject.net.intent.PointToPointIntent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Objects; + +import static com.google.common.base.Preconditions.checkArgument; + +/** + * Utilities for dealing with intents. + */ +public final class IntentUtils { + + private static final Logger log = LoggerFactory.getLogger(IntentUtils.class); + + private IntentUtils() { + + } + + /** + * Checks if two intents represent the same value. + * + * <p>({@link Intent#equals(Object)} only checks ID equality)</p> + * + * <p>Both intents must be of the same type.</p> + * + * @param one first intent + * @param two second intent + * @return true if the two intents represent the same value, otherwise false + */ + public static boolean equals(Intent one, Intent two) { + checkArgument(one.getClass() == two.getClass(), + "Intents are not the same type"); + + if (!(Objects.equals(one.appId(), two.appId()) && + Objects.equals(one.key(), two.key()))) { + return false; + } + + if (one instanceof MultiPointToSinglePointIntent) { + MultiPointToSinglePointIntent intent1 = (MultiPointToSinglePointIntent) one; + MultiPointToSinglePointIntent intent2 = (MultiPointToSinglePointIntent) two; + + return Objects.equals(intent1.selector(), intent2.selector()) && + Objects.equals(intent1.treatment(), intent2.treatment()) && + Objects.equals(intent1.ingressPoints(), intent2.ingressPoints()) && + Objects.equals(intent1.egressPoint(), intent2.egressPoint()); + } else if (one instanceof PointToPointIntent) { + PointToPointIntent intent1 = (PointToPointIntent) one; + PointToPointIntent intent2 = (PointToPointIntent) two; + + return Objects.equals(intent1.selector(), intent2.selector()) && + Objects.equals(intent1.treatment(), intent2.treatment()) && + Objects.equals(intent1.ingressPoint(), intent2.ingressPoint()) && + Objects.equals(intent1.egressPoint(), intent2.egressPoint()); + } else { + log.error("Unimplemented intent type"); + return false; + } + } +} diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java index 459db2b7..b2ce0f8a 100644 --- a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java @@ -15,6 +15,8 @@ */ package org.onosproject.sdnip; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; import org.onlab.packet.Ethernet; import org.onlab.packet.IPv4; import org.onlab.packet.IPv6; @@ -22,16 +24,18 @@ import org.onlab.packet.IpAddress; import org.onlab.packet.IpPrefix; import org.onlab.packet.TpPort; import org.onosproject.core.ApplicationId; -import org.onosproject.net.config.NetworkConfigService; import org.onosproject.incubator.net.intf.Interface; import org.onosproject.incubator.net.intf.InterfaceService; import org.onosproject.net.ConnectPoint; +import org.onosproject.net.config.NetworkConfigService; import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.host.InterfaceIpAddress; +import org.onosproject.net.intent.Key; import org.onosproject.net.intent.PointToPointIntent; +import org.onosproject.routing.IntentSynchronizationService; import org.onosproject.routing.RoutingService; import org.onosproject.routing.config.BgpConfig; import org.slf4j.Logger; @@ -49,18 +53,26 @@ import static com.google.common.base.Preconditions.checkNotNull; public class PeerConnectivityManager { private static final int PRIORITY_OFFSET = 1000; + private static final String SUFFIX_DST = "dst"; + private static final String SUFFIX_SRC = "src"; + private static final String SUFFIX_ICMP = "icmp"; + private static final Logger log = LoggerFactory.getLogger( PeerConnectivityManager.class); private static final short BGP_PORT = 179; - private final IntentSynchronizer intentSynchronizer; + private final IntentSynchronizationService intentSynchronizer; private final NetworkConfigService configService; private final InterfaceService interfaceService; private final ApplicationId appId; private final ApplicationId routerAppId; + // Just putting something random here for now. Figure out exactly what + // indexes we need when we start making use of them. + private final Multimap<BgpConfig.BgpSpeakerConfig, PointToPointIntent> peerIntents; + /** * Creates a new PeerConnectivityManager. * @@ -71,7 +83,7 @@ public class PeerConnectivityManager { * @param routerAppId application ID */ public PeerConnectivityManager(ApplicationId appId, - IntentSynchronizer intentSynchronizer, + IntentSynchronizationService intentSynchronizer, NetworkConfigService configService, ApplicationId routerAppId, InterfaceService interfaceService) { @@ -80,6 +92,8 @@ public class PeerConnectivityManager { this.configService = configService; this.routerAppId = routerAppId; this.interfaceService = interfaceService; + + peerIntents = HashMultimap.create(); } /** @@ -100,8 +114,6 @@ public class PeerConnectivityManager { * BGP speakers and external BGP peers. */ private void setUpConnectivity() { - List<PointToPointIntent> intents = new ArrayList<>(); - BgpConfig config = configService.getConfig(routerAppId, RoutingService.CONFIG_CLASS); if (config == null) { @@ -113,11 +125,12 @@ public class PeerConnectivityManager { log.debug("Start to set up BGP paths for BGP speaker: {}", bgpSpeaker); - intents.addAll(buildSpeakerIntents(bgpSpeaker)); - } + buildSpeakerIntents(bgpSpeaker).forEach(i -> { + peerIntents.put(bgpSpeaker, i); + intentSynchronizer.submit(i); + }); - // Submit all the intents. - intentSynchronizer.submitPeerIntents(intents); + } } private Collection<PointToPointIntent> buildSpeakerIntents(BgpConfig.BgpSpeakerConfig speaker) { @@ -167,8 +180,8 @@ public class PeerConnectivityManager { List<PointToPointIntent> intents = new ArrayList<>(); TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment(); - TrafficSelector selector; + Key key; byte tcpProtocol; byte icmpProtocol; @@ -188,8 +201,11 @@ public class PeerConnectivityManager { null, BGP_PORT); + key = buildKey(ipOne, ipTwo, SUFFIX_DST); + intents.add(PointToPointIntent.builder() .appId(appId) + .key(key) .selector(selector) .treatment(treatment) .ingressPoint(portOne) @@ -204,8 +220,11 @@ public class PeerConnectivityManager { BGP_PORT, null); + key = buildKey(ipOne, ipTwo, SUFFIX_SRC); + intents.add(PointToPointIntent.builder() .appId(appId) + .key(key) .selector(selector) .treatment(treatment) .ingressPoint(portOne) @@ -220,8 +239,11 @@ public class PeerConnectivityManager { null, BGP_PORT); + key = buildKey(ipTwo, ipOne, SUFFIX_DST); + intents.add(PointToPointIntent.builder() .appId(appId) + .key(key) .selector(selector) .treatment(treatment) .ingressPoint(portTwo) @@ -236,8 +258,11 @@ public class PeerConnectivityManager { BGP_PORT, null); + key = buildKey(ipTwo, ipOne, SUFFIX_SRC); + intents.add(PointToPointIntent.builder() .appId(appId) + .key(key) .selector(selector) .treatment(treatment) .ingressPoint(portTwo) @@ -252,8 +277,11 @@ public class PeerConnectivityManager { null, null); + key = buildKey(ipOne, ipTwo, SUFFIX_ICMP); + intents.add(PointToPointIntent.builder() .appId(appId) + .key(key) .selector(selector) .treatment(treatment) .ingressPoint(portOne) @@ -268,8 +296,11 @@ public class PeerConnectivityManager { null, null); + key = buildKey(ipTwo, ipOne, SUFFIX_ICMP); + intents.add(PointToPointIntent.builder() .appId(appId) + .key(key) .selector(selector) .treatment(treatment) .ingressPoint(portTwo) @@ -316,4 +347,27 @@ public class PeerConnectivityManager { return builder.build(); } + /** + * Builds an intent Key for a point-to-point intent based off the source + * and destination IP address, as well as a suffix String to distinguish + * between different types of intents between the same source and + * destination. + * + * @param srcIp source IP address + * @param dstIp destination IP address + * @param suffix suffix string + * @return + */ + private Key buildKey(IpAddress srcIp, IpAddress dstIp, String suffix) { + String keyString = new StringBuilder() + .append(srcIp.toString()) + .append("-") + .append(dstIp.toString()) + .append("-") + .append(suffix) + .toString(); + + return Key.of(keyString, appId); + } + } diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java index 3d1fe65c..1b3eda9d 100644 --- a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIp.java @@ -32,7 +32,9 @@ import org.onosproject.net.config.NetworkConfigService; import org.onosproject.incubator.net.intf.InterfaceService; import org.onosproject.net.host.HostService; import org.onosproject.net.intent.IntentService; +import org.onosproject.routing.IntentSynchronizationService; import org.onosproject.routing.RoutingService; +import org.onosproject.routing.SdnIpService; import org.onosproject.routing.config.RoutingConfigurationService; import org.slf4j.Logger; @@ -79,6 +81,7 @@ public class SdnIp implements SdnIpService { private IntentSynchronizer intentSynchronizer; private PeerConnectivityManager peerConnectivity; + private SdnIpFib fib; private LeadershipEventListener leadershipEventListener = new InnerLeadershipEventListener(); @@ -93,10 +96,7 @@ public class SdnIp implements SdnIpService { localControllerNode = clusterService.getLocalNode(); - intentSynchronizer = new IntentSynchronizer(appId, intentService, - hostService, - config, - interfaceService); + intentSynchronizer = new IntentSynchronizer(appId, intentService); intentSynchronizer.start(); peerConnectivity = new PeerConnectivityManager(appId, @@ -106,8 +106,9 @@ public class SdnIp implements SdnIpService { interfaceService); peerConnectivity.start(); - routingService.addFibListener(intentSynchronizer); - routingService.addIntentRequestListener(intentSynchronizer); + fib = new SdnIpFib(appId, interfaceService, intentSynchronizer); + + routingService.addFibListener(fib); routingService.start(); leadershipService.addListener(leadershipEventListener); @@ -131,6 +132,11 @@ public class SdnIp implements SdnIpService { intentSynchronizer.leaderChanged(isPrimary); } + @Override + public IntentSynchronizationService getIntentSynchronizationService() { + return intentSynchronizer; + } + /** * Converts DPIDs of the form xx:xx:xx:xx:xx:xx:xx to OpenFlow provider * device URIs. diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java new file mode 100644 index 00000000..c0001bdc --- /dev/null +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java @@ -0,0 +1,216 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.sdnip; + +import com.google.common.collect.ImmutableList; +import org.onlab.packet.Ethernet; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; +import org.onosproject.core.ApplicationId; +import org.onosproject.incubator.net.intf.Interface; +import org.onosproject.incubator.net.intf.InterfaceService; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.intent.Constraint; +import org.onosproject.net.intent.Key; +import org.onosproject.net.intent.MultiPointToSinglePointIntent; +import org.onosproject.net.intent.constraint.PartialFailureConstraint; +import org.onosproject.routing.FibListener; +import org.onosproject.routing.FibUpdate; +import org.onosproject.routing.IntentSynchronizationService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import static com.google.common.base.Preconditions.checkArgument; + +/** + * FIB component of SDN-IP. + */ +public class SdnIpFib implements FibListener { + private Logger log = LoggerFactory.getLogger(getClass()); + + private static final int PRIORITY_OFFSET = 100; + private static final int PRIORITY_MULTIPLIER = 5; + protected static final ImmutableList<Constraint> CONSTRAINTS + = ImmutableList.of(new PartialFailureConstraint()); + + private final Map<IpPrefix, MultiPointToSinglePointIntent> routeIntents; + + private final ApplicationId appId; + private final InterfaceService interfaceService; + private final IntentSynchronizationService intentSynchronizer; + + /** + * Class constructor. + * + * @param appId application ID to use when generating intents + * @param interfaceService interface service + * @param intentSynchronizer intent synchronizer + */ + public SdnIpFib(ApplicationId appId, InterfaceService interfaceService, + IntentSynchronizationService intentSynchronizer) { + routeIntents = new ConcurrentHashMap<>(); + + this.appId = appId; + this.interfaceService = interfaceService; + this.intentSynchronizer = intentSynchronizer; + } + + + @Override + public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) { + int submitCount = 0, withdrawCount = 0; + // + // NOTE: Semantically, we MUST withdraw existing intents before + // submitting new intents. + // + synchronized (this) { + MultiPointToSinglePointIntent intent; + + // + // Prepare the Intent batch operations for the intents to withdraw + // + for (FibUpdate withdraw : withdraws) { + checkArgument(withdraw.type() == FibUpdate.Type.DELETE, + "FibUpdate with wrong type in withdraws list"); + + IpPrefix prefix = withdraw.entry().prefix(); + intent = routeIntents.remove(prefix); + if (intent == null) { + log.trace("SDN-IP No intent in routeIntents to delete " + + "for prefix: {}", prefix); + continue; + } + intentSynchronizer.withdraw(intent); + withdrawCount++; + } + + // + // Prepare the Intent batch operations for the intents to submit + // + for (FibUpdate update : updates) { + checkArgument(update.type() == FibUpdate.Type.UPDATE, + "FibUpdate with wrong type in updates list"); + + IpPrefix prefix = update.entry().prefix(); + intent = generateRouteIntent(prefix, update.entry().nextHopIp(), + update.entry().nextHopMac()); + + if (intent == null) { + // This preserves the old semantics - if an intent can't be + // generated, we don't do anything with that prefix. But + // perhaps we should withdraw the old intent anyway? + continue; + } + + routeIntents.put(prefix, intent); + intentSynchronizer.submit(intent); + submitCount++; + } + + log.debug("SDN-IP submitted {}/{}, withdrew = {}/{}", submitCount, + updates.size(), withdrawCount, withdraws.size()); + } + } + + /** + * Generates a route intent for a prefix, the next hop IP address, and + * the next hop MAC address. + * <p/> + * This method will find the egress interface for the intent. + * Intent will match dst IP prefix and rewrite dst MAC address at all other + * border switches, then forward packets according to dst MAC address. + * + * @param prefix IP prefix of the route to add + * @param nextHopIpAddress IP address of the next hop + * @param nextHopMacAddress MAC address of the next hop + * @return the generated intent, or null if no intent should be submitted + */ + private MultiPointToSinglePointIntent generateRouteIntent( + IpPrefix prefix, + IpAddress nextHopIpAddress, + MacAddress nextHopMacAddress) { + + // Find the attachment point (egress interface) of the next hop + Interface egressInterface = interfaceService.getMatchingInterface(nextHopIpAddress); + if (egressInterface == null) { + log.warn("No outgoing interface found for {}", + nextHopIpAddress); + return null; + } + + // Generate the intent itself + Set<ConnectPoint> ingressPorts = new HashSet<>(); + ConnectPoint egressPort = egressInterface.connectPoint(); + log.debug("Generating intent for prefix {}, next hop mac {}", + prefix, nextHopMacAddress); + + for (Interface intf : interfaceService.getInterfaces()) { + // TODO this should be only peering interfaces + if (!intf.connectPoint().equals(egressInterface.connectPoint())) { + ConnectPoint srcPort = intf.connectPoint(); + ingressPorts.add(srcPort); + } + } + + // Match the destination IP prefix at the first hop + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + if (prefix.isIp4()) { + selector.matchEthType(Ethernet.TYPE_IPV4); + selector.matchIPDst(prefix); + } else { + selector.matchEthType(Ethernet.TYPE_IPV6); + selector.matchIPv6Dst(prefix); + } + + // Rewrite the destination MAC address + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder() + .setEthDst(nextHopMacAddress); + if (!egressInterface.vlan().equals(VlanId.NONE)) { + treatment.setVlanId(egressInterface.vlan()); + // If we set VLAN ID, we have to make sure a VLAN tag exists. + // TODO support no VLAN -> VLAN routing + selector.matchVlanId(VlanId.ANY); + } + + int priority = + prefix.prefixLength() * PRIORITY_MULTIPLIER + PRIORITY_OFFSET; + Key key = Key.of(prefix.toString(), appId); + return MultiPointToSinglePointIntent.builder() + .appId(appId) + .key(key) + .selector(selector.build()) + .treatment(treatment.build()) + .ingressPoints(ingressPorts) + .egressPoint(egressPort) + .priority(priority) + .constraints(CONSTRAINTS) + .build(); + } + +} diff --git a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java index 72cc112e..7a17cfe0 100644 --- a/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java +++ b/framework/src/onos/apps/sdnip/src/main/java/org/onosproject/sdnip/cli/PrimaryChangeCommand.java @@ -18,7 +18,7 @@ package org.onosproject.sdnip.cli; import org.apache.karaf.shell.commands.Argument; import org.apache.karaf.shell.commands.Command; import org.onosproject.cli.AbstractShellCommand; -import org.onosproject.sdnip.SdnIpService; +import org.onosproject.routing.SdnIpService; /** * Command to change whether this SDNIP instance is primary or not. diff --git a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java index fc5782e4..6dc3ce10 100644 --- a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java +++ b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/IntentSyncTest.java @@ -16,6 +16,7 @@ package org.onosproject.sdnip; import com.google.common.collect.Sets; +import com.google.common.util.concurrent.MoreExecutors; import org.junit.Before; import org.junit.Test; import org.onlab.junit.TestUtils; @@ -27,10 +28,9 @@ import org.onlab.packet.IpAddress; import org.onlab.packet.IpPrefix; import org.onlab.packet.MacAddress; import org.onlab.packet.VlanId; +import org.onosproject.TestApplicationId; import org.onosproject.core.ApplicationId; -import org.onosproject.net.config.NetworkConfigService; import org.onosproject.incubator.net.intf.Interface; -import org.onosproject.incubator.net.intf.InterfaceService; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; import org.onosproject.net.PortNumber; @@ -43,20 +43,13 @@ import org.onosproject.net.intent.AbstractIntentTest; import org.onosproject.net.intent.Intent; import org.onosproject.net.intent.IntentService; import org.onosproject.net.intent.IntentState; +import org.onosproject.net.intent.Key; import org.onosproject.net.intent.MultiPointToSinglePointIntent; -import org.onosproject.routing.FibEntry; -import org.onosproject.routing.FibUpdate; import org.onosproject.routing.RouteEntry; -import org.onosproject.routing.config.BgpPeer; -import org.onosproject.routing.config.RoutingConfigurationService; -import org.onosproject.sdnip.IntentSynchronizer.IntentKey; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; @@ -64,11 +57,8 @@ import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.reset; import static org.easymock.EasyMock.verify; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.onosproject.sdnip.TestIntentServiceHelper.eqExceptId; /** * This class tests the intent synchronization function in the @@ -76,10 +66,7 @@ import static org.onosproject.sdnip.TestIntentServiceHelper.eqExceptId; */ public class IntentSyncTest extends AbstractIntentTest { - private RoutingConfigurationService routingConfig; - private InterfaceService interfaceService; private IntentService intentService; - private NetworkConfigService configService; private static final ConnectPoint SW1_ETH1 = new ConnectPoint( DeviceId.deviceId("of:0000000000000001"), @@ -100,65 +87,18 @@ public class IntentSyncTest extends AbstractIntentTest { private IntentSynchronizer intentSynchronizer; private final Set<Interface> interfaces = Sets.newHashSet(); - private static final ApplicationId APPID = new ApplicationId() { - @Override - public short id() { - return 1; - } - - @Override - public String name() { - return "SDNIP"; - } - }; + private static final ApplicationId APPID = TestApplicationId.create("SDNIP"); @Before public void setUp() throws Exception { super.setUp(); - routingConfig = createMock(RoutingConfigurationService.class); - interfaceService = createMock(InterfaceService.class); - configService = createMock(NetworkConfigService.class); - - // These will set expectations on routingConfig setUpInterfaceService(); - setUpBgpPeers(); - - replay(routingConfig); - replay(interfaceService); intentService = createMock(IntentService.class); intentSynchronizer = new IntentSynchronizer(APPID, intentService, - null, routingConfig, - interfaceService); - } - - /** - * Sets up BGP peers in external networks. - */ - private void setUpBgpPeers() { - - Map<IpAddress, BgpPeer> peers = new HashMap<>(); - - String peerSw1Eth1 = "192.168.10.1"; - peers.put(IpAddress.valueOf(peerSw1Eth1), - new BgpPeer("00:00:00:00:00:00:00:01", 1, peerSw1Eth1)); - - // Two BGP peers are connected to switch 2 port 1. - String peer1Sw2Eth1 = "192.168.20.1"; - peers.put(IpAddress.valueOf(peer1Sw2Eth1), - new BgpPeer("00:00:00:00:00:00:00:02", 1, peer1Sw2Eth1)); - - String peer2Sw2Eth1 = "192.168.20.2"; - peers.put(IpAddress.valueOf(peer2Sw2Eth1), - new BgpPeer("00:00:00:00:00:00:00:02", 1, peer2Sw2Eth1)); - - String peer1Sw4Eth1 = "192.168.40.1"; - peers.put(IpAddress.valueOf(peer1Sw4Eth1), - new BgpPeer("00:00:00:00:00:00:00:04", 1, peer1Sw4Eth1)); - - expect(routingConfig.getBgpPeers()).andReturn(peers).anyTimes(); + MoreExecutors.newDirectExecutorService()); } /** @@ -200,267 +140,13 @@ public class IntentSyncTest extends AbstractIntentTest { MacAddress.valueOf("00:00:00:00:00:04"), VlanId.vlanId((short) 1)); - expect(interfaceService.getInterfacesByPort(SW4_ETH1)).andReturn( - Collections.singleton(sw4Eth1)).anyTimes(); - expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.40.1"))) - .andReturn(sw4Eth1).anyTimes(); - interfaces.add(sw4Eth1); - - expect(interfaceService.getInterfacesByPort(SW1_ETH1)).andReturn( - Collections.singleton(sw1Eth1)).anyTimes(); - expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.10.1"))) - .andReturn(sw1Eth1).anyTimes(); - expect(interfaceService.getInterfacesByPort(SW2_ETH1)).andReturn( - Collections.singleton(sw2Eth1)).anyTimes(); - expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.20.1"))) - .andReturn(sw2Eth1).anyTimes(); - expect(interfaceService.getInterfacesByPort(SW3_ETH1)).andReturn( - Collections.singleton(sw3Eth1)).anyTimes(); - expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.30.1"))) - .andReturn(sw3Eth1).anyTimes(); - expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes(); - } - - /** - * Tests adding a FIB entry to the IntentSynchronizer. - * - * We verify that the synchronizer records the correct state and that the - * correct intent is submitted to the IntentService. - * - * @throws TestUtilsException - */ - @Test - public void testFibAdd() throws TestUtilsException { - FibEntry fibEntry = new FibEntry( - Ip4Prefix.valueOf("1.1.1.0/24"), - Ip4Address.valueOf("192.168.10.1"), - MacAddress.valueOf("00:00:00:00:00:01")); - - // Construct a MultiPointToSinglePointIntent intent - TrafficSelector.Builder selectorBuilder = - DefaultTrafficSelector.builder(); - selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst( - fibEntry.prefix()); - - TrafficTreatment.Builder treatmentBuilder = - DefaultTrafficTreatment.builder(); - treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01")); - - Set<ConnectPoint> ingressPoints = new HashSet<>(); - ingressPoints.add(SW2_ETH1); - ingressPoints.add(SW3_ETH1); - ingressPoints.add(SW4_ETH1); - - MultiPointToSinglePointIntent intent = - MultiPointToSinglePointIntent.builder() - .appId(APPID) - .selector(selectorBuilder.build()) - .treatment(treatmentBuilder.build()) - .ingressPoints(ingressPoints) - .egressPoint(SW1_ETH1) - .constraints(IntentSynchronizer.CONSTRAINTS) - .build(); - - // Setup the expected intents - intentService.submit(eqExceptId(intent)); - replay(intentService); - - intentSynchronizer.leaderChanged(true); - TestUtils.setField(intentSynchronizer, "isActivatedLeader", true); - - FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, - fibEntry); - intentSynchronizer.update(Collections.singleton(fibUpdate), - Collections.emptyList()); - - assertEquals(intentSynchronizer.getRouteIntents().size(), 1); - Intent firstIntent = - intentSynchronizer.getRouteIntents().iterator().next(); - IntentKey firstIntentKey = new IntentKey(firstIntent); - IntentKey intentKey = new IntentKey(intent); - assertTrue(firstIntentKey.equals(intentKey)); - verify(intentService); - } - - /** - * Tests adding a FIB entry with to a next hop in a VLAN. - * - * We verify that the synchronizer records the correct state and that the - * correct intent is submitted to the IntentService. - * - * @throws TestUtilsException - */ - @Test - public void testFibAddWithVlan() throws TestUtilsException { - FibEntry fibEntry = new FibEntry( - Ip4Prefix.valueOf("3.3.3.0/24"), - Ip4Address.valueOf("192.168.40.1"), - MacAddress.valueOf("00:00:00:00:00:04")); - - // Construct a MultiPointToSinglePointIntent intent - TrafficSelector.Builder selectorBuilder = - DefaultTrafficSelector.builder(); - selectorBuilder.matchEthType(Ethernet.TYPE_IPV4) - .matchIPDst(fibEntry.prefix()) - .matchVlanId(VlanId.ANY); - - TrafficTreatment.Builder treatmentBuilder = - DefaultTrafficTreatment.builder(); - treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:04")) - .setVlanId(VlanId.vlanId((short) 1)); - - Set<ConnectPoint> ingressPoints = new HashSet<>(); - ingressPoints.add(SW1_ETH1); - ingressPoints.add(SW2_ETH1); - ingressPoints.add(SW3_ETH1); - - MultiPointToSinglePointIntent intent = - MultiPointToSinglePointIntent.builder() - .appId(APPID) - .selector(selectorBuilder.build()) - .treatment(treatmentBuilder.build()) - .ingressPoints(ingressPoints) - .egressPoint(SW4_ETH1) - .constraints(IntentSynchronizer.CONSTRAINTS) - .build(); - - // Setup the expected intents - intentService.submit(eqExceptId(intent)); - - replay(intentService); - - // Run the test - intentSynchronizer.leaderChanged(true); - TestUtils.setField(intentSynchronizer, "isActivatedLeader", true); - FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, fibEntry); - - intentSynchronizer.update(Collections.singleton(fibUpdate), - Collections.emptyList()); - - // Verify - assertEquals(intentSynchronizer.getRouteIntents().size(), 1); - Intent firstIntent = - intentSynchronizer.getRouteIntents().iterator().next(); - IntentKey firstIntentKey = new IntentKey(firstIntent); - IntentKey intentKey = new IntentKey(intent); - assertTrue(firstIntentKey.equals(intentKey)); - verify(intentService); - } - - /** - * Tests updating a FIB entry. - * - * We verify that the synchronizer records the correct state and that the - * correct intent is submitted to the IntentService. - * - * @throws TestUtilsException - */ - @Test - public void testFibUpdate() throws TestUtilsException { - // Firstly add a route - testFibAdd(); - - Intent addedIntent = - intentSynchronizer.getRouteIntents().iterator().next(); - - // Start to construct a new route entry and new intent - FibEntry fibEntryUpdate = new FibEntry( - Ip4Prefix.valueOf("1.1.1.0/24"), - Ip4Address.valueOf("192.168.20.1"), - MacAddress.valueOf("00:00:00:00:00:02")); - - // Construct a new MultiPointToSinglePointIntent intent - TrafficSelector.Builder selectorBuilderNew = - DefaultTrafficSelector.builder(); - selectorBuilderNew.matchEthType(Ethernet.TYPE_IPV4).matchIPDst( - fibEntryUpdate.prefix()); - - TrafficTreatment.Builder treatmentBuilderNew = - DefaultTrafficTreatment.builder(); - treatmentBuilderNew.setEthDst(MacAddress.valueOf("00:00:00:00:00:02")); - - - Set<ConnectPoint> ingressPointsNew = new HashSet<>(); - ingressPointsNew.add(SW1_ETH1); - ingressPointsNew.add(SW3_ETH1); - ingressPointsNew.add(SW4_ETH1); - - MultiPointToSinglePointIntent intentNew = - MultiPointToSinglePointIntent.builder() - .appId(APPID) - .selector(selectorBuilderNew.build()) - .treatment(treatmentBuilderNew.build()) - .ingressPoints(ingressPointsNew) - .egressPoint(SW2_ETH1) - .constraints(IntentSynchronizer.CONSTRAINTS) - .build(); - - // Set up test expectation - reset(intentService); - // Setup the expected intents - intentService.withdraw(eqExceptId(addedIntent)); - intentService.submit(eqExceptId(intentNew)); - replay(intentService); - - // Call the update() method in IntentSynchronizer class - intentSynchronizer.leaderChanged(true); - TestUtils.setField(intentSynchronizer, "isActivatedLeader", true); - FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, - fibEntryUpdate); - intentSynchronizer.update(Collections.singletonList(fibUpdate), - Collections.emptyList()); - - // Verify - assertEquals(intentSynchronizer.getRouteIntents().size(), 1); - Intent firstIntent = - intentSynchronizer.getRouteIntents().iterator().next(); - IntentKey firstIntentKey = new IntentKey(firstIntent); - IntentKey intentNewKey = new IntentKey(intentNew); - assertTrue(firstIntentKey.equals(intentNewKey)); - verify(intentService); } /** - * Tests deleting a FIB entry. - * - * We verify that the synchronizer records the correct state and that the - * correct intent is withdrawn from the IntentService. - * - * @throws TestUtilsException - */ - @Test - public void testFibDelete() throws TestUtilsException { - // Firstly add a route - testFibAdd(); - - Intent addedIntent = - intentSynchronizer.getRouteIntents().iterator().next(); - - // Construct the existing route entry - FibEntry fibEntry = new FibEntry( - Ip4Prefix.valueOf("1.1.1.0/24"), null, null); - - // Set up expectation - reset(intentService); - // Setup the expected intents - intentService.withdraw(eqExceptId(addedIntent)); - replay(intentService); - - // Call the update() method in IntentSynchronizer class - intentSynchronizer.leaderChanged(true); - TestUtils.setField(intentSynchronizer, "isActivatedLeader", true); - FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.DELETE, fibEntry); - intentSynchronizer.update(Collections.emptyList(), - Collections.singletonList(fibUpdate)); - - // Verify - assertEquals(intentSynchronizer.getRouteIntents().size(), 0); - verify(intentService); - } - - /** - * This method tests the behavior of intent Synchronizer. + * Tests the synchronization behavior of intent synchronizer. We set up + * a discrepancy between the intent service state and the intent + * synchronizer's state and ensure that this is reconciled correctly. * * @throws TestUtilsException */ @@ -529,27 +215,13 @@ public class IntentSyncTest extends AbstractIntentTest { // Compose a intent, which is equal to intent5 but the id is different. MultiPointToSinglePointIntent intent5New = staticIntentBuilder(intent5, routeEntry5, "00:00:00:00:00:01"); - assertThat(IntentSynchronizer.IntentKey.equalIntents( - intent5, intent5New), - is(true)); + assertThat(IntentUtils.equals(intent5, intent5New), is(true)); assertFalse(intent5.equals(intent5New)); MultiPointToSinglePointIntent intent6 = intentBuilder( routeEntry6.prefix(), "00:00:00:00:00:01", SW1_ETH1); - // Set up the routeIntents field in IntentSynchronizer class - ConcurrentHashMap<IpPrefix, MultiPointToSinglePointIntent> - routeIntents = new ConcurrentHashMap<>(); - routeIntents.put(routeEntry1.prefix(), intent1); - routeIntents.put(routeEntry3.prefix(), intent3); - routeIntents.put(routeEntry4Update.prefix(), intent4Update); - routeIntents.put(routeEntry5.prefix(), intent5New); - routeIntents.put(routeEntry6.prefix(), intent6); - routeIntents.put(routeEntry7.prefix(), intent7); - TestUtils.setField(intentSynchronizer, "routeIntents", routeIntents); - // Set up expectation - reset(intentService); Set<Intent> intents = new HashSet<>(); intents.add(intent1); expect(intentService.getIntentState(intent1.key())) @@ -568,9 +240,9 @@ public class IntentSyncTest extends AbstractIntentTest { .andReturn(IntentState.WITHDRAWING).anyTimes(); expect(intentService.getIntents()).andReturn(intents).anyTimes(); + // These are the operations that should be done to the intentService + // during synchronization intentService.withdraw(intent2); - intentService.withdraw(intent4); - intentService.submit(intent3); intentService.submit(intent4Update); intentService.submit(intent6); @@ -578,16 +250,101 @@ public class IntentSyncTest extends AbstractIntentTest { replay(intentService); // Start the test + + // Simulate some input from the clients. The intent synchronizer has not + // gained the global leadership yet, but it will remember this input for + // when it does. + intentSynchronizer.submit(intent1); + intentSynchronizer.submit(intent2); + intentSynchronizer.withdraw(intent2); + intentSynchronizer.submit(intent3); + intentSynchronizer.submit(intent4); + intentSynchronizer.submit(intent4Update); + intentSynchronizer.submit(intent5); + intentSynchronizer.submit(intent6); + intentSynchronizer.submit(intent7); + + // Give the leadership to the intent synchronizer. It will now attempt + // to synchronize the intents in the store with the intents it has + // recorded based on the earlier user input. + intentSynchronizer.leaderChanged(true); + + verify(intentService); + } + + /** + * Tests the behavior of the submit API, both when the synchronizer has + * leadership and when it does not. + */ + @Test + public void testSubmit() { + IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24"); + Intent intent = intentBuilder(prefix, "00:00:00:00:00:01", SW1_ETH1); + + // Set up expectations + intentService.submit(intent); + expect(intentService.getIntents()).andReturn(Collections.emptyList()) + .anyTimes(); + replay(intentService); + + // Give the intent synchronizer leadership so it will submit intents + // to the intent service + intentSynchronizer.leaderChanged(true); + + // Test the submit + intentSynchronizer.submit(intent); + + verify(intentService); + + // Now we'll remove leadership from the intent synchronizer and verify + // that it does not submit any intents to the intent service when we + // call the submit API + reset(intentService); + replay(intentService); + + intentSynchronizer.leaderChanged(false); + + intentSynchronizer.submit(intent); + + verify(intentService); + } + + /** + * Tests the behavior of the withdraw API, both when the synchronizer has + * leadership and when it does not. + */ + @Test + public void testWithdraw() { + IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24"); + Intent intent = intentBuilder(prefix, "00:00:00:00:00:01", SW1_ETH1); + + // Submit an intent first so we can withdraw it later + intentService.submit(intent); + intentService.withdraw(intent); + expect(intentService.getIntents()).andReturn(Collections.emptyList()) + .anyTimes(); + replay(intentService); + + // Give the intent synchronizer leadership so it will submit intents + // to the intent service intentSynchronizer.leaderChanged(true); - intentSynchronizer.synchronizeIntents(); - // Verify - assertEquals(intentSynchronizer.getRouteIntents().size(), 6); - assertTrue(intentSynchronizer.getRouteIntents().contains(intent1)); - assertTrue(intentSynchronizer.getRouteIntents().contains(intent3)); - assertTrue(intentSynchronizer.getRouteIntents().contains(intent4Update)); - assertTrue(intentSynchronizer.getRouteIntents().contains(intent5)); - assertTrue(intentSynchronizer.getRouteIntents().contains(intent6)); + // Test the submit then withdraw + intentSynchronizer.submit(intent); + intentSynchronizer.withdraw(intent); + + verify(intentService); + + // Now we'll remove leadership from the intent synchronizer and verify + // that it does not withdraw any intents to the intent service when we + // call the withdraw API + reset(intentService); + replay(intentService); + + intentSynchronizer.leaderChanged(false); + + intentSynchronizer.submit(intent); + intentSynchronizer.withdraw(intent); verify(intentService); } @@ -607,10 +364,10 @@ public class IntentSyncTest extends AbstractIntentTest { TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder(); if (ipPrefix.isIp4()) { - selectorBuilder.matchEthType(Ethernet.TYPE_IPV4); // IPv4 + selectorBuilder.matchEthType(Ethernet.TYPE_IPV4); selectorBuilder.matchIPDst(ipPrefix); } else { - selectorBuilder.matchEthType(Ethernet.TYPE_IPV6); // IPv6 + selectorBuilder.matchEthType(Ethernet.TYPE_IPV6); selectorBuilder.matchIPv6Dst(ipPrefix); } @@ -628,11 +385,12 @@ public class IntentSyncTest extends AbstractIntentTest { MultiPointToSinglePointIntent intent = MultiPointToSinglePointIntent.builder() .appId(APPID) + .key(Key.of(ipPrefix.toString(), APPID)) .selector(selectorBuilder.build()) .treatment(treatmentBuilder.build()) .ingressPoints(ingressPoints) .egressPoint(egressPoint) - .constraints(IntentSynchronizer.CONSTRAINTS) + .constraints(SdnIpFib.CONSTRAINTS) .build(); return intent; } @@ -646,7 +404,7 @@ public class IntentSyncTest extends AbstractIntentTest { * @return the newly constructed MultiPointToSinglePointIntent * @throws TestUtilsException */ - private MultiPointToSinglePointIntent staticIntentBuilder( + private MultiPointToSinglePointIntent staticIntentBuilder( MultiPointToSinglePointIntent intent, RouteEntry routeEntry, String nextHopMacAddress) throws TestUtilsException { diff --git a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java index d89c3c2b..c4b2daad 100644 --- a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java +++ b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/PeerConnectivityManagerTest.java @@ -19,7 +19,6 @@ import com.google.common.collect.Sets; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; -import org.onlab.junit.TestUtils; import org.onlab.junit.TestUtils.TestUtilsException; import org.onlab.packet.Ethernet; import org.onlab.packet.IPv4; @@ -28,13 +27,14 @@ import org.onlab.packet.IpPrefix; import org.onlab.packet.MacAddress; import org.onlab.packet.TpPort; import org.onlab.packet.VlanId; +import org.onosproject.TestApplicationId; import org.onosproject.core.ApplicationId; -import org.onosproject.net.config.NetworkConfigService; import org.onosproject.incubator.net.intf.Interface; import org.onosproject.incubator.net.intf.InterfaceService; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; import org.onosproject.net.PortNumber; +import org.onosproject.net.config.NetworkConfigService; import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.TrafficSelector; @@ -42,8 +42,9 @@ import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.host.InterfaceIpAddress; import org.onosproject.net.intent.AbstractIntentTest; import org.onosproject.net.intent.Intent; -import org.onosproject.net.intent.IntentService; +import org.onosproject.net.intent.Key; import org.onosproject.net.intent.PointToPointIntent; +import org.onosproject.routing.IntentSynchronizationService; import org.onosproject.routing.config.BgpConfig; import org.onosproject.routing.config.BgpPeer; import org.onosproject.routing.config.BgpSpeaker; @@ -71,26 +72,15 @@ import static org.onosproject.sdnip.TestIntentServiceHelper.eqExceptId; */ public class PeerConnectivityManagerTest extends AbstractIntentTest { - private static final ApplicationId APPID = new ApplicationId() { - @Override - public short id() { - return 0; - } - - @Override - public String name() { - return "foo"; - } - }; + private static final ApplicationId APPID = TestApplicationId.create("foo"); private static final ApplicationId CONFIG_APP_ID = APPID; private PeerConnectivityManager peerConnectivityManager; - private IntentSynchronizer intentSynchronizer; + private IntentSynchronizationService intentSynchronizer; private RoutingConfigurationService routingConfig; private InterfaceService interfaceService; private NetworkConfigService networkConfigService; - private IntentService intentService; private Set<BgpConfig.BgpSpeakerConfig> bgpSpeakers; private Map<String, Interface> interfaces; @@ -98,8 +88,6 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { private BgpConfig bgpConfig; - private Map<String, Interface> configuredInterfaces; - private Map<IpAddress, BgpPeer> configuredPeers; private List<PointToPointIntent> intentList; private final String dpid1 = "00:00:00:00:00:00:00:01"; @@ -136,7 +124,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { // These will set expectations on routingConfig and interfaceService bgpSpeakers = setUpBgpSpeakers(); interfaces = Collections.unmodifiableMap(setUpInterfaces()); - peers = Collections.unmodifiableMap(setUpPeers()); + peers = setUpPeers(); initPeerConnectivity(); intentList = setUpIntentList(); @@ -169,11 +157,11 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { * Sets up logical interfaces, which emulate the configured interfaces * in SDN-IP application. * - * @return configured interfaces as a MAP from Interface name to Interface + * @return configured interfaces as a map from interface name to Interface */ private Map<String, Interface> setUpInterfaces() { - configuredInterfaces = new HashMap<>(); + Map<String, Interface> configuredInterfaces = new HashMap<>(); String interfaceSw1Eth1 = "s1-eth1"; InterfaceIpAddress ia1 = @@ -242,7 +230,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { */ private Map<IpAddress, BgpPeer> setUpPeers() { - configuredPeers = new HashMap<>(); + Map<IpAddress, BgpPeer> configuredPeers = new HashMap<>(); String peerSw1Eth1 = "192.168.10.1"; configuredPeers.put(IpAddress.valueOf(peerSw1Eth1), @@ -266,14 +254,12 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { * @return point to point intent list */ private List<PointToPointIntent> setUpIntentList() { - intentList = new ArrayList<>(); setUpBgpIntents(); setUpIcmpIntents(); return intentList; - } /** @@ -306,8 +292,12 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { builder.matchTcpDst(TpPort.tpPort(dstTcpPort)); } + Key key = Key.of(srcPrefix.split("/")[0] + "-" + dstPrefix.split("/")[0] + + "-" + ((srcTcpPort == null) ? "dst" : "src"), APPID); + PointToPointIntent intent = PointToPointIntent.builder() .appId(APPID) + .key(key) .selector(builder.build()) .treatment(noTreatment) .ingressPoint(srcConnectPoint) @@ -392,8 +382,12 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { .matchIPDst(IpPrefix.valueOf(dstPrefix)) .build(); + Key key = Key.of(srcPrefix.split("/")[0] + "-" + dstPrefix.split("/")[0] + + "-" + "icmp", APPID); + PointToPointIntent intent = PointToPointIntent.builder() .appId(APPID) + .key(key) .selector(selector) .treatment(noTreatment) .ingressPoint(srcConnectPoint) @@ -434,19 +428,14 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { expect(routingConfig.getBgpPeers()).andReturn(peers).anyTimes(); expect(bgpConfig.bgpSpeakers()).andReturn(bgpSpeakers).anyTimes(); replay(bgpConfig); - expect(networkConfigService.getConfig(APPID, BgpConfig.class)).andReturn(bgpConfig).anyTimes(); + expect(networkConfigService.getConfig(APPID, BgpConfig.class)) + .andReturn(bgpConfig).anyTimes(); replay(networkConfigService); replay(routingConfig); replay(interfaceService); - intentService = createMock(IntentService.class); - replay(intentService); - - intentSynchronizer = new IntentSynchronizer(APPID, intentService, - null, routingConfig, - interfaceService); - intentSynchronizer.leaderChanged(true); - TestUtils.setField(intentSynchronizer, "isActivatedLeader", true); + intentSynchronizer = createMock(IntentSynchronizationService.class); + replay(intentSynchronizer); peerConnectivityManager = new PeerConnectivityManager(APPID, intentSynchronizer, @@ -464,20 +453,18 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { */ @Test public void testConnectionSetup() { - - reset(intentService); + reset(intentSynchronizer); // Setup the expected intents for (Intent intent : intentList) { - intentService.submit(eqExceptId(intent)); + intentSynchronizer.submit(eqExceptId(intent)); } - replay(intentService); + replay(intentSynchronizer); // Running the interface to be tested. peerConnectivityManager.start(); - verify(intentService); - + verify(intentSynchronizer); } /** @@ -488,7 +475,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { reset(interfaceService); expect(interfaceService.getInterfaces()).andReturn( - Sets.<Interface>newHashSet()).anyTimes(); + Sets.newHashSet()).anyTimes(); expect(interfaceService.getInterfacesByPort(s2Eth1)) .andReturn(Collections.emptySet()).anyTimes(); expect(interfaceService.getInterfacesByPort(s1Eth1)) @@ -508,10 +495,10 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { replay(interfaceService); - reset(intentService); - replay(intentService); + reset(intentSynchronizer); + replay(intentSynchronizer); peerConnectivityManager.start(); - verify(intentService); + verify(intentSynchronizer); } /** @@ -527,10 +514,10 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { expect(routingConfig.getBgpPeers()).andReturn(peers).anyTimes(); replay(routingConfig); - reset(intentService); - replay(intentService); + reset(intentSynchronizer); + replay(intentSynchronizer); peerConnectivityManager.start(); - verify(intentService); + verify(intentSynchronizer); } /** @@ -540,7 +527,7 @@ public class PeerConnectivityManagerTest extends AbstractIntentTest { @Test public void testNoPeerInterface() { String peerSw100Eth1 = "192.168.200.1"; - configuredPeers.put(IpAddress.valueOf(peerSw100Eth1), + peers.put(IpAddress.valueOf(peerSw100Eth1), new BgpPeer("00:00:00:00:00:00:01:00", 1, peerSw100Eth1)); testConnectionSetup(); } diff --git a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java new file mode 100644 index 00000000..5466d520 --- /dev/null +++ b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/SdnIpFibTest.java @@ -0,0 +1,417 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.sdnip; + +import com.google.common.collect.Sets; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.Ethernet; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.Ip4Prefix; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; +import org.onosproject.TestApplicationId; +import org.onosproject.core.ApplicationId; +import org.onosproject.incubator.net.intf.Interface; +import org.onosproject.incubator.net.intf.InterfaceService; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DeviceId; +import org.onosproject.net.PortNumber; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.host.InterfaceIpAddress; +import org.onosproject.net.intent.AbstractIntentTest; +import org.onosproject.net.intent.Key; +import org.onosproject.net.intent.MultiPointToSinglePointIntent; +import org.onosproject.routing.FibEntry; +import org.onosproject.routing.FibUpdate; +import org.onosproject.routing.IntentSynchronizationService; +import org.onosproject.routing.config.BgpPeer; +import org.onosproject.routing.config.RoutingConfigurationService; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.reset; +import static org.easymock.EasyMock.verify; +import static org.onosproject.sdnip.TestIntentServiceHelper.eqExceptId; + +/** + * Unit tests for SdnIpFib. + */ +public class SdnIpFibTest extends AbstractIntentTest { + + private RoutingConfigurationService routingConfig; + private InterfaceService interfaceService; + + private static final ConnectPoint SW1_ETH1 = new ConnectPoint( + DeviceId.deviceId("of:0000000000000001"), + PortNumber.portNumber(1)); + + private static final ConnectPoint SW2_ETH1 = new ConnectPoint( + DeviceId.deviceId("of:0000000000000002"), + PortNumber.portNumber(1)); + + private static final ConnectPoint SW3_ETH1 = new ConnectPoint( + DeviceId.deviceId("of:0000000000000003"), + PortNumber.portNumber(1)); + + private static final ConnectPoint SW4_ETH1 = new ConnectPoint( + DeviceId.deviceId("of:0000000000000004"), + PortNumber.portNumber(1)); + + private SdnIpFib sdnipFib; + private IntentSynchronizationService intentSynchronizer; + private final Set<Interface> interfaces = Sets.newHashSet(); + + private static final ApplicationId APPID = TestApplicationId.create("SDNIP"); + + @Before + public void setUp() throws Exception { + super.setUp(); + + routingConfig = createMock(RoutingConfigurationService.class); + interfaceService = createMock(InterfaceService.class); + + // These will set expectations on routingConfig and interfaceService + setUpInterfaceService(); + setUpBgpPeers(); + + replay(routingConfig); + replay(interfaceService); + + intentSynchronizer = createMock(IntentSynchronizationService.class); + + sdnipFib = new SdnIpFib(APPID, interfaceService, intentSynchronizer); + } + + /** + * Sets up BGP peers in external networks. + */ + private void setUpBgpPeers() { + + Map<IpAddress, BgpPeer> peers = new HashMap<>(); + + String peerSw1Eth1 = "192.168.10.1"; + peers.put(IpAddress.valueOf(peerSw1Eth1), + new BgpPeer("00:00:00:00:00:00:00:01", 1, peerSw1Eth1)); + + // Two BGP peers are connected to switch 2 port 1. + String peer1Sw2Eth1 = "192.168.20.1"; + peers.put(IpAddress.valueOf(peer1Sw2Eth1), + new BgpPeer("00:00:00:00:00:00:00:02", 1, peer1Sw2Eth1)); + + String peer2Sw2Eth1 = "192.168.20.2"; + peers.put(IpAddress.valueOf(peer2Sw2Eth1), + new BgpPeer("00:00:00:00:00:00:00:02", 1, peer2Sw2Eth1)); + + String peer1Sw4Eth1 = "192.168.40.1"; + peers.put(IpAddress.valueOf(peer1Sw4Eth1), + new BgpPeer("00:00:00:00:00:00:00:04", 1, peer1Sw4Eth1)); + + expect(routingConfig.getBgpPeers()).andReturn(peers).anyTimes(); + } + + /** + * Sets up InterfaceService. + */ + private void setUpInterfaceService() { + Set<InterfaceIpAddress> interfaceIpAddresses1 = Sets.newHashSet(); + interfaceIpAddresses1.add(new InterfaceIpAddress( + IpAddress.valueOf("192.168.10.101"), + IpPrefix.valueOf("192.168.10.0/24"))); + Interface sw1Eth1 = new Interface(SW1_ETH1, + interfaceIpAddresses1, MacAddress.valueOf("00:00:00:00:00:01"), + VlanId.NONE); + interfaces.add(sw1Eth1); + + Set<InterfaceIpAddress> interfaceIpAddresses2 = Sets.newHashSet(); + interfaceIpAddresses2.add( + new InterfaceIpAddress(IpAddress.valueOf("192.168.20.101"), + IpPrefix.valueOf("192.168.20.0/24"))); + Interface sw2Eth1 = new Interface(SW2_ETH1, + interfaceIpAddresses2, MacAddress.valueOf("00:00:00:00:00:02"), + VlanId.NONE); + interfaces.add(sw2Eth1); + + Set<InterfaceIpAddress> interfaceIpAddresses3 = Sets.newHashSet(); + interfaceIpAddresses3.add( + new InterfaceIpAddress(IpAddress.valueOf("192.168.30.101"), + IpPrefix.valueOf("192.168.30.0/24"))); + Interface sw3Eth1 = new Interface(SW3_ETH1, + interfaceIpAddresses3, MacAddress.valueOf("00:00:00:00:00:03"), + VlanId.NONE); + interfaces.add(sw3Eth1); + + InterfaceIpAddress interfaceIpAddress4 = + new InterfaceIpAddress(IpAddress.valueOf("192.168.40.101"), + IpPrefix.valueOf("192.168.40.0/24")); + Interface sw4Eth1 = new Interface(SW4_ETH1, + Sets.newHashSet(interfaceIpAddress4), + MacAddress.valueOf("00:00:00:00:00:04"), + VlanId.vlanId((short) 1)); + + expect(interfaceService.getInterfacesByPort(SW4_ETH1)).andReturn( + Collections.singleton(sw4Eth1)).anyTimes(); + expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.40.1"))) + .andReturn(sw4Eth1).anyTimes(); + + interfaces.add(sw4Eth1); + + expect(interfaceService.getInterfacesByPort(SW1_ETH1)).andReturn( + Collections.singleton(sw1Eth1)).anyTimes(); + expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.10.1"))) + .andReturn(sw1Eth1).anyTimes(); + expect(interfaceService.getInterfacesByPort(SW2_ETH1)).andReturn( + Collections.singleton(sw2Eth1)).anyTimes(); + expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.20.1"))) + .andReturn(sw2Eth1).anyTimes(); + expect(interfaceService.getInterfacesByPort(SW3_ETH1)).andReturn( + Collections.singleton(sw3Eth1)).anyTimes(); + expect(interfaceService.getMatchingInterface(Ip4Address.valueOf("192.168.30.1"))) + .andReturn(sw3Eth1).anyTimes(); + expect(interfaceService.getInterfaces()).andReturn(interfaces).anyTimes(); + } + + /** + * Tests adding a FIB entry to the IntentSynchronizer. + * + * We verify that the synchronizer records the correct state and that the + * correct intent is submitted to the IntentService. + */ + @Test + public void testFibAdd() { + IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24"); + FibEntry fibEntry = new FibEntry(prefix, + Ip4Address.valueOf("192.168.10.1"), + MacAddress.valueOf("00:00:00:00:00:01")); + + // Construct a MultiPointToSinglePointIntent intent + TrafficSelector.Builder selectorBuilder = + DefaultTrafficSelector.builder(); + selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst( + fibEntry.prefix()); + + TrafficTreatment.Builder treatmentBuilder = + DefaultTrafficTreatment.builder(); + treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01")); + + Set<ConnectPoint> ingressPoints = new HashSet<>(); + ingressPoints.add(SW2_ETH1); + ingressPoints.add(SW3_ETH1); + ingressPoints.add(SW4_ETH1); + + MultiPointToSinglePointIntent intent = + MultiPointToSinglePointIntent.builder() + .appId(APPID) + .key(Key.of(prefix.toString(), APPID)) + .selector(selectorBuilder.build()) + .treatment(treatmentBuilder.build()) + .ingressPoints(ingressPoints) + .egressPoint(SW1_ETH1) + .constraints(SdnIpFib.CONSTRAINTS) + .build(); + + // Setup the expected intents + intentSynchronizer.submit(eqExceptId(intent)); + replay(intentSynchronizer); + + // Send in the UPDATE FibUpdate + FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, fibEntry); + sdnipFib.update(Collections.singleton(fibUpdate), Collections.emptyList()); + + verify(intentSynchronizer); + } + + /** + * Tests adding a FIB entry with to a next hop in a VLAN. + * + * We verify that the synchronizer records the correct state and that the + * correct intent is submitted to the IntentService. + */ + @Test + public void testFibAddWithVlan() { + IpPrefix prefix = Ip4Prefix.valueOf("3.3.3.0/24"); + FibEntry fibEntry = new FibEntry(prefix, + Ip4Address.valueOf("192.168.40.1"), + MacAddress.valueOf("00:00:00:00:00:04")); + + // Construct a MultiPointToSinglePointIntent intent + TrafficSelector.Builder selectorBuilder = + DefaultTrafficSelector.builder(); + selectorBuilder.matchEthType(Ethernet.TYPE_IPV4) + .matchIPDst(fibEntry.prefix()) + .matchVlanId(VlanId.ANY); + + TrafficTreatment.Builder treatmentBuilder = + DefaultTrafficTreatment.builder(); + treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:04")) + .setVlanId(VlanId.vlanId((short) 1)); + + Set<ConnectPoint> ingressPoints = new HashSet<>(); + ingressPoints.add(SW1_ETH1); + ingressPoints.add(SW2_ETH1); + ingressPoints.add(SW3_ETH1); + + MultiPointToSinglePointIntent intent = + MultiPointToSinglePointIntent.builder() + .appId(APPID) + .key(Key.of(prefix.toString(), APPID)) + .selector(selectorBuilder.build()) + .treatment(treatmentBuilder.build()) + .ingressPoints(ingressPoints) + .egressPoint(SW4_ETH1) + .constraints(SdnIpFib.CONSTRAINTS) + .build(); + + // Setup the expected intents + intentSynchronizer.submit(eqExceptId(intent)); + + replay(intentSynchronizer); + + // Send in the UPDATE FibUpdate + FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, fibEntry); + sdnipFib.update(Collections.singleton(fibUpdate), Collections.emptyList()); + + verify(intentSynchronizer); + } + + /** + * Tests updating a FIB entry. + * + * We verify that the synchronizer records the correct state and that the + * correct intent is submitted to the IntentService. + */ + @Test + public void testFibUpdate() { + // Firstly add a route + testFibAdd(); + + IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24"); + + // Start to construct a new route entry and new intent + FibEntry fibEntryUpdate = new FibEntry(prefix, + Ip4Address.valueOf("192.168.20.1"), + MacAddress.valueOf("00:00:00:00:00:02")); + + // Construct a new MultiPointToSinglePointIntent intent + TrafficSelector.Builder selectorBuilderNew = + DefaultTrafficSelector.builder(); + selectorBuilderNew.matchEthType(Ethernet.TYPE_IPV4).matchIPDst( + fibEntryUpdate.prefix()); + + TrafficTreatment.Builder treatmentBuilderNew = + DefaultTrafficTreatment.builder(); + treatmentBuilderNew.setEthDst(MacAddress.valueOf("00:00:00:00:00:02")); + + Set<ConnectPoint> ingressPointsNew = new HashSet<>(); + ingressPointsNew.add(SW1_ETH1); + ingressPointsNew.add(SW3_ETH1); + ingressPointsNew.add(SW4_ETH1); + + MultiPointToSinglePointIntent intentNew = + MultiPointToSinglePointIntent.builder() + .appId(APPID) + .key(Key.of(prefix.toString(), APPID)) + .selector(selectorBuilderNew.build()) + .treatment(treatmentBuilderNew.build()) + .ingressPoints(ingressPointsNew) + .egressPoint(SW2_ETH1) + .constraints(SdnIpFib.CONSTRAINTS) + .build(); + + // Set up test expectation + reset(intentSynchronizer); + + // Setup the expected intents + intentSynchronizer.submit(eqExceptId(intentNew)); + replay(intentSynchronizer); + + // Send in the UPDATE FibUpdate + FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.UPDATE, + fibEntryUpdate); + sdnipFib.update(Collections.singletonList(fibUpdate), + Collections.emptyList()); + + verify(intentSynchronizer); + } + + /** + * Tests deleting a FIB entry. + * + * We verify that the synchronizer records the correct state and that the + * correct intent is withdrawn from the IntentService. + */ + @Test + public void testFibDelete() { + // Firstly add a route + testFibAdd(); + + IpPrefix prefix = Ip4Prefix.valueOf("1.1.1.0/24"); + + // Construct the existing route entry + FibEntry fibEntry = new FibEntry(prefix, null, null); + + // Construct the existing MultiPointToSinglePoint intent + TrafficSelector.Builder selectorBuilder = + DefaultTrafficSelector.builder(); + selectorBuilder.matchEthType(Ethernet.TYPE_IPV4).matchIPDst( + fibEntry.prefix()); + + TrafficTreatment.Builder treatmentBuilder = + DefaultTrafficTreatment.builder(); + treatmentBuilder.setEthDst(MacAddress.valueOf("00:00:00:00:00:01")); + + Set<ConnectPoint> ingressPoints = new HashSet<>(); + ingressPoints.add(SW2_ETH1); + ingressPoints.add(SW3_ETH1); + ingressPoints.add(SW4_ETH1); + + MultiPointToSinglePointIntent addedIntent = + MultiPointToSinglePointIntent.builder() + .appId(APPID) + .key(Key.of(prefix.toString(), APPID)) + .selector(selectorBuilder.build()) + .treatment(treatmentBuilder.build()) + .ingressPoints(ingressPoints) + .egressPoint(SW1_ETH1) + .constraints(SdnIpFib.CONSTRAINTS) + .build(); + + // Set up expectation + reset(intentSynchronizer); + // Setup the expected intents + intentSynchronizer.withdraw(eqExceptId(addedIntent)); + replay(intentSynchronizer); + + // Send in the DELETE FibUpdate + FibUpdate fibUpdate = new FibUpdate(FibUpdate.Type.DELETE, fibEntry); + sdnipFib.update(Collections.emptyList(), Collections.singletonList(fibUpdate)); + + verify(intentSynchronizer); + } +} diff --git a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java index 69b18aa9..7f825e81 100644 --- a/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java +++ b/framework/src/onos/apps/sdnip/src/test/java/org/onosproject/sdnip/TestIntentServiceHelper.java @@ -17,7 +17,6 @@ package org.onosproject.sdnip; import org.easymock.IArgumentMatcher; import org.onosproject.net.intent.Intent; -import org.onosproject.sdnip.IntentSynchronizer.IntentKey; import static org.easymock.EasyMock.reportMatcher; @@ -53,8 +52,6 @@ public final class TestIntentServiceHelper { * the solution is to use an EasyMock matcher that verifies that all the * value properties of the provided intent match the expected values, but * ignores the intent ID when testing equality. - * - * FIXME this currently does not take key into account */ private static final class IdAgnosticIntentMatcher implements IArgumentMatcher { @@ -86,9 +83,7 @@ public final class TestIntentServiceHelper { Intent providedIntent = (Intent) object; providedString = providedIntent.toString(); - IntentKey thisIntentKey = new IntentKey(intent); - IntentKey providedIntentKey = new IntentKey(providedIntent); - return thisIntentKey.equals(providedIntentKey); + return IntentUtils.equals(intent, providedIntent); } } diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java index ef9d444a..8fdf81a2 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Open Networking Laboratory + * Copyright 2014-2015 Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,24 +18,26 @@ package org.onosproject.segmentrouting; import com.google.common.collect.Lists; import org.onlab.packet.Ip4Address; import org.onlab.packet.Ip4Prefix; -import org.onlab.packet.IpPrefix; import org.onlab.packet.MacAddress; +import org.onosproject.incubator.net.config.basics.ConfigException; +import org.onosproject.incubator.net.config.basics.InterfaceConfig; +import org.onosproject.incubator.net.intf.Interface; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.config.NetworkConfigRegistry; +import org.onosproject.net.host.InterfaceIpAddress; +import org.onosproject.segmentrouting.config.SegmentRoutingConfig; +import org.onosproject.segmentrouting.config.SegmentRoutingConfig.AdjacencySid; import org.onosproject.segmentrouting.grouphandler.DeviceProperties; import org.onosproject.net.DeviceId; import org.onosproject.net.PortNumber; -import org.onosproject.segmentrouting.config.NetworkConfig.SwitchConfig; -import org.onosproject.segmentrouting.config.NetworkConfigManager; -import org.onosproject.segmentrouting.config.SegmentRouterConfig; -import org.onosproject.segmentrouting.config.SegmentRouterConfig.Subnet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static com.google.common.base.Preconditions.checkNotNull; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; /** * Segment Routing configuration component that reads the @@ -50,7 +52,6 @@ public class DeviceConfiguration implements DeviceProperties { .getLogger(DeviceConfiguration.class); private final List<Integer> allSegmentIds = new ArrayList<>(); private final HashMap<DeviceId, SegmentRouterInfo> deviceConfigMap = new HashMap<>(); - private final NetworkConfigManager configService; private class SegmentRouterInfo { int nodeSid; @@ -60,48 +61,60 @@ public class DeviceConfiguration implements DeviceProperties { boolean isEdge; HashMap<PortNumber, Ip4Address> gatewayIps; HashMap<PortNumber, Ip4Prefix> subnets; - List<SegmentRouterConfig.AdjacencySid> adjacencySids; + List<AdjacencySid> adjacencySids; } /** * Constructor. Reads all the configuration for all devices of type * Segment Router and organizes into various maps for easier access. - * - * @param configService handle to network configuration manager - * component from where the relevant configuration is retrieved. */ - public DeviceConfiguration(NetworkConfigManager configService) { - this.configService = checkNotNull(configService); - List<SwitchConfig> allSwitchCfg = - this.configService.getConfiguredAllowedSwitches(); - for (SwitchConfig cfg : allSwitchCfg) { - if (!(cfg instanceof SegmentRouterConfig)) { - continue; - } + public DeviceConfiguration(NetworkConfigRegistry cfgService) { + // Read config from device subject, excluding gatewayIps and subnets. + Set<DeviceId> deviceSubjects = + cfgService.getSubjects(DeviceId.class, SegmentRoutingConfig.class); + deviceSubjects.forEach(subject -> { + SegmentRoutingConfig config = + cfgService.getConfig(subject, SegmentRoutingConfig.class); SegmentRouterInfo info = new SegmentRouterInfo(); - info.nodeSid = ((SegmentRouterConfig) cfg).getNodeSid(); - info.deviceId = ((SegmentRouterConfig) cfg).getDpid(); - info.mac = MacAddress.valueOf((( - SegmentRouterConfig) cfg).getRouterMac()); - String routerIp = ((SegmentRouterConfig) cfg).getRouterIp(); - Ip4Prefix prefix = checkNotNull(IpPrefix.valueOf(routerIp).getIp4Prefix()); - info.ip = prefix.address(); - info.isEdge = ((SegmentRouterConfig) cfg).isEdgeRouter(); - info.subnets = new HashMap<>(); + info.deviceId = subject; + info.nodeSid = config.getSid(); + info.ip = config.getIp(); + info.mac = config.getMac(); + info.isEdge = config.isEdgeRouter(); + info.adjacencySids = config.getAdjacencySids(); info.gatewayIps = new HashMap<>(); - for (Subnet s: ((SegmentRouterConfig) cfg).getSubnets()) { - info.subnets.put(PortNumber.portNumber(s.getPortNo()), - Ip4Prefix.valueOf(s.getSubnetIp())); - String gatewayIp = s.getSubnetIp(). - substring(0, s.getSubnetIp().indexOf('/')); - info.gatewayIps.put(PortNumber.portNumber(s.getPortNo()), - Ip4Address.valueOf(gatewayIp)); - } - info.adjacencySids = ((SegmentRouterConfig) cfg).getAdjacencySids(); + info.subnets = new HashMap<>(); + this.deviceConfigMap.put(info.deviceId, info); this.allSegmentIds.add(info.nodeSid); - - } + }); + + // Read gatewayIps and subnets from port subject. + Set<ConnectPoint> portSubjects = + cfgService.getSubjects(ConnectPoint.class, InterfaceConfig.class); + portSubjects.forEach(subject -> { + InterfaceConfig config = + cfgService.getConfig(subject, InterfaceConfig.class); + Set<Interface> networkInterfaces; + try { + networkInterfaces = config.getInterfaces(); + } catch (ConfigException e) { + log.error("Error loading port configuration"); + return; + } + networkInterfaces.forEach(networkInterface -> { + DeviceId dpid = networkInterface.connectPoint().deviceId(); + PortNumber port = networkInterface.connectPoint().port(); + SegmentRouterInfo info = this.deviceConfigMap.get(dpid); + + Set<InterfaceIpAddress> interfaceAddresses = networkInterface.ipAddresses(); + interfaceAddresses.forEach(interfaceAddress -> { + info.gatewayIps.put(port, interfaceAddress.ipAddress().getIp4Address()); + info.subnets.put(port, interfaceAddress.subnetAddress().getIp4Prefix()); + }); + }); + + }); } /** @@ -379,8 +392,8 @@ public class DeviceConfiguration implements DeviceProperties { */ public List<Integer> getPortsForAdjacencySid(DeviceId deviceId, int sid) { if (deviceConfigMap.get(deviceId) != null) { - for (SegmentRouterConfig.AdjacencySid asid : deviceConfigMap.get(deviceId).adjacencySids) { - if (asid.getAdjSid() == sid) { + for (AdjacencySid asid : deviceConfigMap.get(deviceId).adjacencySids) { + if (asid.getAsid() == sid) { return asid.getPorts(); } } @@ -402,9 +415,9 @@ public class DeviceConfiguration implements DeviceProperties { if (deviceConfigMap.get(deviceId).adjacencySids.isEmpty()) { return false; } else { - for (SegmentRouterConfig.AdjacencySid asid: + for (AdjacencySid asid: deviceConfigMap.get(deviceId).adjacencySids) { - if (asid.getAdjSid() == sid) { + if (asid.getAsid() == sid) { return true; } } @@ -414,4 +427,4 @@ public class DeviceConfiguration implements DeviceProperties { return false; } -} +}
\ No newline at end of file diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java index 0f8fa59d..f65f03e0 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java @@ -111,10 +111,10 @@ public class IcmpHandler { icmpReplyIpv4.setChecksum((short) 0); ICMP icmpReply = new ICMP(); + icmpReply.setPayload(((ICMP) icmpRequestIpv4.getPayload()).getPayload()); icmpReply.setIcmpType(ICMP.TYPE_ECHO_REPLY); icmpReply.setIcmpCode(ICMP.SUBTYPE_ECHO_REPLY); icmpReply.setChecksum((short) 0); - icmpReplyIpv4.setPayload(icmpReply); icmpReplyEth.setPayload(icmpReplyIpv4); diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java index 874faabf..05663129 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java @@ -27,6 +27,12 @@ import org.onlab.util.KryoNamespace; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; import org.onosproject.event.Event; +import org.onosproject.net.config.ConfigFactory; +import org.onosproject.net.config.NetworkConfigEvent; +import org.onosproject.net.config.NetworkConfigRegistry; +import org.onosproject.net.config.NetworkConfigListener; +import org.onosproject.net.config.basics.SubjectFactories; +import org.onosproject.segmentrouting.config.SegmentRoutingConfig; import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler; import org.onosproject.segmentrouting.grouphandler.NeighborSet; import org.onosproject.segmentrouting.grouphandler.NeighborSetNextObjectiveStoreKey; @@ -50,7 +56,6 @@ import org.onosproject.net.packet.PacketContext; import org.onosproject.net.packet.PacketProcessor; import org.onosproject.net.packet.PacketService; import org.onosproject.net.topology.TopologyService; -import org.onosproject.segmentrouting.config.NetworkConfigManager; import org.onosproject.store.service.EventuallyConsistentMap; import org.onosproject.store.service.EventuallyConsistentMapBuilder; import org.onosproject.store.service.StorageService; @@ -133,7 +138,21 @@ public class SegmentRoutingManager implements SegmentRoutingService { @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected StorageService storageService; - private NetworkConfigManager networkConfigService = new NetworkConfigManager();; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected NetworkConfigRegistry cfgService; + + private final InternalConfigListener cfgListener = + new InternalConfigListener(this); + + private final ConfigFactory cfgFactory = + new ConfigFactory(SubjectFactories.DEVICE_SUBJECT_FACTORY, + SegmentRoutingConfig.class, + "segmentrouting") { + @Override + public SegmentRoutingConfig createConfig() { + return new SegmentRoutingConfig(); + } + }; private Object threadSchedulerLock = new Object(); private static int numOfEventsQueued = 0; @@ -192,44 +211,17 @@ public class SegmentRoutingManager implements SegmentRoutingService { .withTimestampProvider((k, v) -> new WallClockTimestamp()) .build(); - networkConfigService.init(); - deviceConfiguration = new DeviceConfiguration(networkConfigService); - arpHandler = new ArpHandler(this); - icmpHandler = new IcmpHandler(this); - ipHandler = new IpHandler(this); - routingRulePopulator = new RoutingRulePopulator(this); - defaultRoutingHandler = new DefaultRoutingHandler(this); - tunnelHandler = new TunnelHandler(linkService, deviceConfiguration, - groupHandlerMap, tunnelStore); - policyHandler = new PolicyHandler(appId, deviceConfiguration, - flowObjectiveService, tunnelHandler, policyStore); - - packetService.addProcessor(processor, PacketProcessor.director(2)); - linkService.addListener(new InternalLinkListener()); - deviceService.addListener(new InternalDeviceListener()); - - for (Device device : deviceService.getDevices()) { - //Irrespective whether the local is a MASTER or not for this device, - //create group handler instance and push default TTP flow rules. - //Because in a multi-instance setup, instances can initiate - //groups for any devices. Also the default TTP rules are needed - //to be pushed before inserting any IP table entries for any device - DefaultGroupHandler groupHandler = DefaultGroupHandler - .createGroupHandler(device.id(), appId, - deviceConfiguration, linkService, - flowObjectiveService, - nsNextObjStore); - groupHandlerMap.put(device.id(), groupHandler); - defaultRoutingHandler.populateTtpRules(device.id()); - } + cfgService.addListener(cfgListener); + cfgService.registerConfigFactory(cfgFactory); - defaultRoutingHandler.startPopulationProcess(); log.info("Started"); - } @Deactivate protected void deactivate() { + cfgService.removeListener(cfgListener); + cfgService.unregisterConfigFactory(cfgFactory); + packetService.removeProcessor(processor); processor = null; log.info("Stopped"); @@ -512,6 +504,59 @@ public class SegmentRoutingManager implements SegmentRoutingService { } } + private class InternalConfigListener implements NetworkConfigListener { + SegmentRoutingManager segmentRoutingManager; + + public InternalConfigListener(SegmentRoutingManager srMgr) { + this.segmentRoutingManager = srMgr; + } + public void configureNetwork() { + deviceConfiguration = new DeviceConfiguration(segmentRoutingManager.cfgService); + + arpHandler = new ArpHandler(segmentRoutingManager); + icmpHandler = new IcmpHandler(segmentRoutingManager); + ipHandler = new IpHandler(segmentRoutingManager); + routingRulePopulator = new RoutingRulePopulator(segmentRoutingManager); + defaultRoutingHandler = new DefaultRoutingHandler(segmentRoutingManager); + + tunnelHandler = new TunnelHandler(linkService, deviceConfiguration, + groupHandlerMap, tunnelStore); + policyHandler = new PolicyHandler(appId, deviceConfiguration, + flowObjectiveService, + tunnelHandler, policyStore); + + packetService.addProcessor(processor, PacketProcessor.director(2)); + linkService.addListener(new InternalLinkListener()); + deviceService.addListener(new InternalDeviceListener()); + + for (Device device : deviceService.getDevices()) { + //Irrespective whether the local is a MASTER or not for this device, + //create group handler instance and push default TTP flow rules. + //Because in a multi-instance setup, instances can initiate + //groups for any devices. Also the default TTP rules are needed + //to be pushed before inserting any IP table entries for any device + DefaultGroupHandler groupHandler = DefaultGroupHandler + .createGroupHandler(device.id(), appId, + deviceConfiguration, linkService, + flowObjectiveService, + nsNextObjStore); + groupHandlerMap.put(device.id(), groupHandler); + defaultRoutingHandler.populateTtpRules(device.id()); + } + defaultRoutingHandler.startPopulationProcess(); + } + + @Override + public void event(NetworkConfigEvent event) { + if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED || + event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) && + event.configClass().equals(SegmentRoutingConfig.class)) { + log.info("Network configuration change detected. (Re)Configuring..."); + configureNetwork(); + return; + } + } + } } diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java new file mode 100644 index 00000000..6dc3f0db --- /dev/null +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java @@ -0,0 +1,128 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.segmentrouting.config; + +import com.fasterxml.jackson.databind.node.ArrayNode; +import org.onlab.packet.Ip4Address; +import org.onlab.packet.MacAddress; +import org.onosproject.net.DeviceId; +import org.onosproject.net.config.Config; +import org.onosproject.net.config.basics.BasicElementConfig; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +/** + * Configuration object for Segment Routing Application. + */ +public class SegmentRoutingConfig extends Config<DeviceId> { + private static final String NAME = "name"; + private static final String IP = "routerIp"; + private static final String MAC = "routerMac"; + private static final String SID = "nodeSid"; + private static final String EDGE = "isEdgeRouter"; + private static final String ADJSID = "adjacencySids"; + + public Optional<String> getName() { + String name = get(NAME, null); + return name != null ? Optional.of(name) : Optional.empty(); + } + + public BasicElementConfig setName(String name) { + return (BasicElementConfig) setOrClear(NAME, name); + } + + public Ip4Address getIp() { + String ip = get(IP, null); + return ip != null ? Ip4Address.valueOf(ip) : null; + } + + public BasicElementConfig setIp(String ip) { + return (BasicElementConfig) setOrClear(IP, ip); + } + + public MacAddress getMac() { + String mac = get(MAC, null); + return mac != null ? MacAddress.valueOf(mac) : null; + } + + public BasicElementConfig setMac(String mac) { + return (BasicElementConfig) setOrClear(MAC, mac); + } + + public int getSid() { + return get(SID, -1); + } + + public BasicElementConfig setSid(int sid) { + return (BasicElementConfig) setOrClear(SID, sid); + } + + public boolean isEdgeRouter() { + return get(EDGE, false); + } + + public BasicElementConfig setEdgeRouter(boolean isEdgeRouter) { + return (BasicElementConfig) setOrClear(EDGE, isEdgeRouter); + } + + public List<AdjacencySid> getAdjacencySids() { + ArrayList<AdjacencySid> adjacencySids = new ArrayList<>(); + + if (!object.has(ADJSID)) { + return adjacencySids; + } + + ArrayNode adjacencySidNodes = (ArrayNode) object.path(ADJSID); + adjacencySidNodes.forEach(adjacencySidNode -> { + int asid = adjacencySidNode.path(AdjacencySid.ASID).asInt(); + + ArrayList<Integer> ports = new ArrayList<Integer>(); + ArrayNode portsNodes = (ArrayNode) adjacencySidNode.path(AdjacencySid.PORTS); + portsNodes.forEach(portNode -> { + ports.add(portNode.asInt()); + }); + + AdjacencySid adjacencySid = new AdjacencySid(asid, ports); + adjacencySids.add(adjacencySid); + }); + + return adjacencySids; + } + + public class AdjacencySid { + private static final String ASID = "adjSid"; + private static final String PORTS = "ports"; + + int asid; + List<Integer> ports; + + public AdjacencySid(int asid, List<Integer> ports) { + this.asid = asid; + this.ports = ports; + } + + public int getAsid() { + return asid; + } + + public List<Integer> getPorts() { + return ports; + } + } +}
\ No newline at end of file diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java index fdae9c9e..95f7e244 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Open Networking Laboratory + * Copyright 2014-2015 Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java index 816ca7bc..497f5256 100644 --- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java +++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DeviceProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 Open Networking Laboratory + * Copyright 2014-2015 Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/framework/src/onos/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/CounterTestIncrementCommand.java b/framework/src/onos/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/CounterTestIncrementCommand.java index 12c8140a..d93ad78f 100644 --- a/framework/src/onos/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/CounterTestIncrementCommand.java +++ b/framework/src/onos/apps/test/distributed-primitives/src/main/java/org/onosproject/distributedprimitives/cli/CounterTestIncrementCommand.java @@ -92,7 +92,8 @@ public class CounterTestIncrementCommand extends AbstractShellCommand { } catch (InterruptedException e) { return; } catch (ExecutionException | TimeoutException e) { - e.printStackTrace(); + print("Error executing command"); + log.error("Error executing command counter-test-increment", e); } } } diff --git a/framework/src/onos/apps/test/intent-perf/src/main/java/org/onosproject/intentperf/IntentPerfInstaller.java b/framework/src/onos/apps/test/intent-perf/src/main/java/org/onosproject/intentperf/IntentPerfInstaller.java index de9e9f21..ad3236e5 100644 --- a/framework/src/onos/apps/test/intent-perf/src/main/java/org/onosproject/intentperf/IntentPerfInstaller.java +++ b/framework/src/onos/apps/test/intent-perf/src/main/java/org/onosproject/intentperf/IntentPerfInstaller.java @@ -179,8 +179,8 @@ public class IntentPerfInstaller { workers = Executors.newFixedThreadPool(DEFAULT_NUM_WORKERS, groupedThreads("onos/intent-perf", "worker-%d")); // disable flow backups for testing - configService.setProperty("org.onosproject.store.flow.impl.DistributedFlowRuleStore", - "backupEnabled", "false"); + configService.setProperty("org.onosproject.store.flow.impl.NewDistributedFlowRuleStore", + "backupEnabled", "true"); // TODO: replace with shared executor messageHandlingExecutor = Executors.newSingleThreadExecutor( diff --git a/framework/src/onos/apps/vtn/app/app.xml b/framework/src/onos/apps/vtn/app/app.xml new file mode 100644 index 00000000..a0efd7f4 --- /dev/null +++ b/framework/src/onos/apps/vtn/app/app.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<app name="org.onosproject.vtn" origin="ON.Lab" version="${project.version}" + featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features" + features="${project.artifactId}"> + <description>${project.description}</description> + + <artifact>mvn:${project.groupId}/onos-app-vtn-mgr/${project.version}</artifact> + <artifact>mvn:${project.groupId}/onos-app-vtn-web/${project.version}</artifact> + <artifact>mvn:${project.groupId}/onos-app-vtn-rsc/${project.version}</artifact> +</app> diff --git a/framework/src/onos/apps/vtn/app/features.xml b/framework/src/onos/apps/vtn/app/features.xml new file mode 100644 index 00000000..c82b41d5 --- /dev/null +++ b/framework/src/onos/apps/vtn/app/features.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}"> + <repository>mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features</repository> + <feature name="${project.artifactId}" version="${project.version}" + description="${project.description}"> + <feature>onos-api</feature> + <feature>onos-drivers</feature> + <bundle>mvn:${project.groupId}/onos-app-vtn-mgr/${project.version}</bundle> + <bundle>mvn:${project.groupId}/onos-app-vtn-web/${project.version}</bundle> + <bundle>mvn:${project.groupId}/onos-app-vtn-rsc/${project.version}</bundle> + </feature> +</features> diff --git a/framework/src/onos/apps/vtn/app/pom.xml b/framework/src/onos/apps/vtn/app/pom.xml new file mode 100644 index 00000000..4ed66172 --- /dev/null +++ b/framework/src/onos/apps/vtn/app/pom.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- ~ Copyright 2014 Open Networking Laboratory ~ ~ Licensed under the Apache + License, Version 2.0 (the "License"); ~ you may not use this file except + in compliance with the License. ~ You may obtain a copy of the License at + ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless required by applicable + law or agreed to in writing, software ~ distributed under the License is + distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. ~ See the License for the specific language + governing permissions and ~ limitations under the License. --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-app-vtn</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>onos-app-vtn-onosfw</artifactId> + <packaging>pom</packaging> + + <description>ONOS framework applications</description> + + <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-app-vtn-rsc</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-app-vtn-web</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-app-vtn-mgr</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + +</project> diff --git a/framework/src/onos/apps/vtn/pom.xml b/framework/src/onos/apps/vtn/pom.xml index fb8fcb13..c2cfe2be 100644 --- a/framework/src/onos/apps/vtn/pom.xml +++ b/framework/src/onos/apps/vtn/pom.xml @@ -1,6 +1,6 @@ -<?xml version="1.0"?> +<?xml version="1.0" encoding="UTF-8"?> <!-- - ~ Copyright 2015 Open Networking Laboratory + ~ Copyright 2014 Open Networking Laboratory ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -14,11 +14,11 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<project - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" - xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> + <parent> <groupId>org.onosproject</groupId> <artifactId>onos-apps</artifactId> @@ -27,32 +27,14 @@ </parent> <artifactId>onos-app-vtn</artifactId> - <packaging>bundle</packaging> + <packaging>pom</packaging> + <description>ONOS framework applications</description> - <properties> - <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> - <onos.app.name>org.onosproject.vtn</onos.app.name> - </properties> - <dependencies> - <dependency> - <groupId>javax.ws.rs</groupId> - <artifactId>jsr311-api</artifactId> - <version>1.1.1</version> - </dependency> - <dependency> - <groupId>org.onosproject</groupId> - <artifactId>onos-incubator-api</artifactId> - </dependency> - <dependency> - <groupId>org.onosproject</groupId> - <artifactId>onos-core-serializers</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>org.onosproject</groupId> - <artifactId>onos-app-vtnrsc</artifactId> - <version>${project.version}</version> - </dependency> - </dependencies> + <modules> + <module>vtnrsc</module> + <module>vtnmgr</module> + <module>vtnweb</module> + <module>app</module> + </modules> </project> diff --git a/framework/src/onos/apps/vtn/vtnmgr/pom.xml b/framework/src/onos/apps/vtn/vtnmgr/pom.xml new file mode 100644 index 00000000..03e66708 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnmgr/pom.xml @@ -0,0 +1,53 @@ +<?xml version="1.0"?> +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-app-vtn</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>onos-app-vtn-mgr</artifactId> + <packaging>bundle</packaging> + + <dependencies> + <dependency> + <groupId>javax.ws.rs</groupId> + <artifactId>jsr311-api</artifactId> + <version>1.1.1</version> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-incubator-api</artifactId> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-core-serializers</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-app-vtn-rsc</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> +</project> diff --git a/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/VTNService.java b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/VTNService.java new file mode 100644 index 00000000..a20f852b --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/VTNService.java @@ -0,0 +1,68 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtn; + +import org.onosproject.net.Device; +import org.onosproject.net.Host; + +/** + * VTN application that applies configuration and flows to the device. + */ +public interface VTNService { + + /** + * Creates a vxlan tunnel and creates the ovs when a ovs controller node is detected. + * + * @param device controller-type device + */ + void onServerDetected(Device device); + + /** + * Drops a vxlan tunnel and drops the ovs when a ovs controller node is vanished. + * + * @param device controller-type device + */ + void onServerVanished(Device device); + + /** + * Applies default forwarding flows when a ovs is detected. + * + * @param device switch-type device + */ + void onOvsDetected(Device device); + + /** + * Remove default forwarding flows when a ovs is vanished. + * + * @param device switch-type device + */ + void onOvsVanished(Device device); + + /** + * Applies multicast flows and tunnel flows when a VM is detected. + * + * @param host a VM + */ + void onHostDetected(Host host); + + /** + * Remove multicast flows and tunnel flows when a VM is vanished. + * + * @param host a VM + */ + void onHostVanished(Host host); + +} diff --git a/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/VTNManager.java b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/VTNManager.java new file mode 100644 index 00000000..090ef0f1 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/VTNManager.java @@ -0,0 +1,672 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtn.impl; + +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; +import static org.onlab.util.Tools.groupedThreads; +import static org.slf4j.LoggerFactory.getLogger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; +import java.util.stream.Collectors; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.Service; +import org.onlab.osgi.DefaultServiceDirectory; +import org.onlab.osgi.ServiceDirectory; +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; +import org.onlab.util.KryoNamespace; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; +import org.onosproject.net.behaviour.BridgeConfig; +import org.onosproject.net.behaviour.BridgeDescription; +import org.onosproject.net.behaviour.BridgeName; +import org.onosproject.net.behaviour.DefaultTunnelDescription; +import org.onosproject.net.behaviour.IpTunnelEndPoint; +import org.onosproject.net.behaviour.Pipeliner; +import org.onosproject.net.behaviour.PipelinerContext; +import org.onosproject.net.behaviour.TunnelConfig; +import org.onosproject.net.behaviour.TunnelDescription; +import org.onosproject.net.behaviour.TunnelEndPoint; +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.Driver; +import org.onosproject.net.driver.DriverHandler; +import org.onosproject.net.driver.DriverService; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.DefaultTrafficTreatment; +import org.onosproject.net.flow.FlowRuleService; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.flow.criteria.Criteria; +import org.onosproject.net.flow.instructions.Instructions; +import org.onosproject.net.flowobjective.DefaultForwardingObjective; +import org.onosproject.net.flowobjective.FlowObjectiveService; +import org.onosproject.net.flowobjective.FlowObjectiveStore; +import org.onosproject.net.flowobjective.ForwardingObjective; +import org.onosproject.net.flowobjective.ForwardingObjective.Flag; +import org.onosproject.net.flowobjective.Objective; +import org.onosproject.net.host.HostEvent; +import org.onosproject.net.host.HostListener; +import org.onosproject.net.host.HostService; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.EventuallyConsistentMap; +import org.onosproject.store.service.StorageService; +import org.onosproject.store.service.WallClockTimestamp; +import org.onosproject.vtn.VTNService; +import org.onosproject.vtnrsc.SegmentationId; +import org.onosproject.vtnrsc.TenantNetwork; +import org.onosproject.vtnrsc.VirtualPort; +import org.onosproject.vtnrsc.VirtualPortId; +import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService; +import org.onosproject.vtnrsc.virtualport.VirtualPortService; +import org.slf4j.Logger; + +import com.google.common.collect.Sets; + +/** + * Provides implementation of VTNService. + */ +@Component(immediate = true) +@Service +public class VTNManager implements VTNService { + private final Logger log = getLogger(getClass()); + + private static final String APP_ID = "org.onosproject.app.vtn"; + private ScheduledExecutorService backgroundService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DeviceService deviceService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected HostService hostService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected FlowRuleService flowRuleService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected StorageService storageService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected TenantNetworkService tenantNetworkService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected VirtualPortService virtualPortService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DriverService driverService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected FlowObjectiveService flowObjectiveService; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected FlowObjectiveStore flowObjectiveStore; + protected ServiceDirectory serviceDirectory = new DefaultServiceDirectory(); + private EventuallyConsistentMap<HostId, SegmentationId> binding; + private ApplicationId appId; + private HostListener hostListener = new InnerHostListener(); + private DeviceListener deviceListener = new InnerDeviceListener(); + private static final String IFACEID = "ifaceid"; + private static final String PORT_HEAD = "vxlan"; + private static final String DEFAULT_BRIDGE_NAME = "br-int"; + private static final String CONTROLLER_IP_KEY = "ipaddress"; + private static final int DEFAULT_MAC_PRIORITY = 0x0000; + private static final int MAC_PRIORITY = 0xffff; + private static final int DEFAULT_PORT_PRIORITY = 0x0000; + private static final int PORT_PRIORITY = 0xffff; + private static final String SWITCH_CHANNEL_ID = "channelId"; + private static final String DRIVER_NAME = "onosfw"; + + @Activate + public void activate() { + KryoNamespace.Builder serializer = KryoNamespace.newBuilder() + .register(KryoNamespaces.API); + appId = coreService.registerApplication(APP_ID); + deviceService.addListener(deviceListener); + hostService.addListener(hostListener); + backgroundService = newSingleThreadScheduledExecutor(groupedThreads("onos-apps/vtn", + "manager-background")); + binding = storageService + .<HostId, SegmentationId>eventuallyConsistentMapBuilder() + .withName("all_tunnel").withSerializer(serializer) + .withTimestampProvider((k, v) -> new WallClockTimestamp()) + .build(); + log.info("Started"); + } + + @Deactivate + public void deactivate() { + backgroundService.shutdown(); + binding.destroy(); + log.info("Stopped"); + } + + @Override + public void onServerDetected(Device device) { + Iterable<Device> devices = deviceService.getAvailableDevices(); + DriverHandler handler = driverService.createHandler(device.id()); + BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class); + bridgeConfig.addBridge(BridgeName.bridgeName(DEFAULT_BRIDGE_NAME)); + String ipAddress = device.annotations().value(CONTROLLER_IP_KEY); + IpAddress ip = IpAddress.valueOf(ipAddress); + Sets.newHashSet(devices).stream() + .filter(d -> Device.Type.CONTROLLER == d.type()) + .filter(d -> !device.id().equals(d.id())).forEach(d -> { + String ipAddress1 = d.annotations() + .value(CONTROLLER_IP_KEY); + IpAddress ip1 = IpAddress.valueOf(ipAddress1); + applyTunnelConfig(ip, ip1, handler); + DriverHandler handler1 = driverService + .createHandler(d.id()); + applyTunnelConfig(ip1, ip, handler1); + + }); + } + + @Override + public void onServerVanished(Device device) { + Iterable<Device> devices = deviceService.getAvailableDevices(); + String ipAddress = device.annotations().value(CONTROLLER_IP_KEY); + IpAddress dst = IpAddress.valueOf(ipAddress); + Sets.newHashSet(devices).stream() + .filter(d -> d.type() == Device.Type.CONTROLLER) + .filter(d -> !device.id().equals(d.id())).forEach(d -> { + String ipAddress1 = d.annotations() + .value(CONTROLLER_IP_KEY); + DriverHandler handler = driverService.createHandler(d.id()); + IpAddress src = IpAddress.valueOf(ipAddress1); + removeTunnelConfig(src, dst, handler); + }); + } + + private void applyTunnelConfig(IpAddress src, IpAddress dst, + DriverHandler handler) { + TunnelEndPoint tunnelAsSrc = IpTunnelEndPoint.ipTunnelPoint(src); + TunnelEndPoint tunnelAsDst = IpTunnelEndPoint.ipTunnelPoint(dst); + TunnelDescription tunnel = new DefaultTunnelDescription( + tunnelAsSrc, + tunnelAsDst, + TunnelDescription.Type.VXLAN, + null); + TunnelConfig config = handler.behaviour(TunnelConfig.class); + config.createTunnel(tunnel); + } + + private void removeTunnelConfig(IpAddress src, IpAddress dst, + DriverHandler handler) { + TunnelEndPoint tunnelAsSrc = IpTunnelEndPoint.ipTunnelPoint(src); + TunnelEndPoint tunnelAsDst = IpTunnelEndPoint.ipTunnelPoint(dst); + TunnelDescription tunnel = new DefaultTunnelDescription( + tunnelAsSrc, + tunnelAsDst, + TunnelDescription.Type.VXLAN, + null); + TunnelConfig config = handler.behaviour(TunnelConfig.class); + config.removeTunnel(tunnel); + } + + @Override + public void onOvsDetected(Device device) { + programMacDefaultRules(device.id(), appId, Objective.Operation.ADD); + programPortDefaultRules(device.id(), appId, Objective.Operation.ADD); + } + + @Override + public void onOvsVanished(Device device) { + programMacDefaultRules(device.id(), appId, Objective.Operation.REMOVE); + programPortDefaultRules(device.id(), appId, Objective.Operation.REMOVE); + } + + @Override + public void onHostDetected(Host host) { + String ifaceId = host.annotations().value(IFACEID); + DeviceId deviceId = host.location().deviceId(); + String currentControllerIp = getControllerIpOfSwitch(deviceId); + Iterable<Device> devices = deviceService.getAvailableDevices(); + VirtualPortId portId = VirtualPortId.portId(ifaceId); + VirtualPort port = virtualPortService.getPort(portId); + TenantNetwork network = tenantNetworkService + .getNetwork(port.networkId()); + String tunnelName = "vxlan-" + currentControllerIp; + binding.put(host.id(), network.segmentationId()); + List<Port> allPorts = deviceService.getPorts(deviceId); + PortNumber inPort = host.location().port(); + List<PortNumber> localVmPorts = getLocalPorts(deviceId, ifaceId); + List<PortNumber> localTunnelPorts = new ArrayList<>(); + Sets.newHashSet(allPorts.iterator()).stream() + .filter(p -> !p.number().equals(PortNumber.LOCAL)).forEach(p -> { + if (p.annotations().value("portName").startsWith(PORT_HEAD)) { + localTunnelPorts.add(p.number()); + } + }); + + localVmPorts.forEach(lp -> programLocalBcastRules(deviceId, network.segmentationId(), lp, localVmPorts, + localTunnelPorts, appId, Objective.Operation.ADD)); + programLocalOut(deviceId, network.segmentationId(), inPort, host.mac(), + appId, Objective.Operation.ADD); + localTunnelPorts + .forEach(tp -> programTunnelFloodOut(deviceId, + network.segmentationId(), + tp, localVmPorts, + appId, + Objective.Operation.ADD)); + Sets.newHashSet(devices).stream() + .filter(d -> d.type() == Device.Type.CONTROLLER).forEach(d -> { + DriverHandler handler = driverService.createHandler(d.id()); + BridgeConfig bridgeConfig = handler + .behaviour(BridgeConfig.class); + Collection<BridgeDescription> bridgeDescriptions = bridgeConfig + .getBridges(); + + Iterator<BridgeDescription> it = bridgeDescriptions + .iterator(); + if (it.hasNext()) { + BridgeDescription sw = it.next(); + Set<PortNumber> ports = bridgeConfig.getPortNumbers(); + ports.stream() + .filter(p -> p.name() + .equalsIgnoreCase(tunnelName)) + .forEach(p -> programTunnelOut(sw.deviceId(), + network.segmentationId(), p, + host.mac(), appId, + Objective.Operation.ADD)); + } + }); + programLocalIn(deviceId, network.segmentationId(), inPort, host.mac(), + appId, Objective.Operation.ADD); + localTunnelPorts + .forEach(tp -> programTunnelIn(deviceId, + network.segmentationId(), + tp, inPort, host.mac(), + appId, Objective.Operation.ADD)); + + } + + @Override + public void onHostVanished(Host host) { + String ifaceId = host.annotations().value(IFACEID); + SegmentationId segId = binding.remove(host.id()); + DeviceId deviceId = host.location().deviceId(); + String currentControllerIp = getControllerIpOfSwitch(deviceId); + Iterable<Device> devices = deviceService.getAvailableDevices(); + + String tunnelName = "vxlan-" + currentControllerIp; + List<Port> allPorts = deviceService.getPorts(deviceId); + PortNumber inPort = host.location().port(); + + List<PortNumber> localTunnelPorts = new ArrayList<>(); + Sets.newHashSet(allPorts.iterator()).stream() + .filter(p -> !p.number().equals(PortNumber.LOCAL)).forEach(p -> { + if (p.annotations().value("portName").startsWith(PORT_HEAD)) { + localTunnelPorts.add(p.number()); + } + }); + + List<PortNumber> localVmPorts = getLocalPorts(deviceId, ifaceId); + localVmPorts.add(inPort); + localVmPorts.forEach(lp -> programLocalBcastRules(deviceId, segId, lp, localVmPorts, + localTunnelPorts, appId, Objective.Operation.REMOVE)); + programLocalOut(deviceId, segId, inPort, host.mac(), + appId, Objective.Operation.REMOVE); + localTunnelPorts + .forEach(tp -> programTunnelFloodOut(deviceId, + segId, + tp, localVmPorts, + appId, + Objective.Operation.REMOVE)); + Sets.newHashSet(devices).stream() + .filter(d -> d.type() == Device.Type.CONTROLLER).forEach(d -> { + DriverHandler handler = driverService.createHandler(d.id()); + BridgeConfig bridgeConfig = handler + .behaviour(BridgeConfig.class); + Collection<BridgeDescription> bridgeDescriptions = bridgeConfig + .getBridges(); + + Iterator<BridgeDescription> it = bridgeDescriptions + .iterator(); + if (it.hasNext()) { + BridgeDescription sw = it.next(); + Set<PortNumber> ports = bridgeConfig.getPortNumbers(); + ports.stream() + .filter(p -> p.name() + .equalsIgnoreCase(tunnelName)) + .forEach(p -> programTunnelOut(sw.deviceId(), + segId, p, + host.mac(), appId, + Objective.Operation.REMOVE)); + } + }); + programLocalIn(deviceId, segId, inPort, host.mac(), + appId, Objective.Operation.REMOVE); + localTunnelPorts + .forEach(tp -> programTunnelIn(deviceId, + segId, + tp, inPort, host.mac(), + appId, Objective.Operation.REMOVE)); + } + + private class InnerDeviceListener implements DeviceListener { + + @Override + public void event(DeviceEvent event) { + Device device = event.subject(); + if (Device.Type.CONTROLLER == device.type() + && DeviceEvent.Type.DEVICE_ADDED == event.type()) { + backgroundService.execute(() -> onServerDetected(device)); + } else if (Device.Type.CONTROLLER == device.type() + && DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED == event + .type()) { + backgroundService.execute(() -> onServerVanished(device)); + } else if (Device.Type.SWITCH == device.type() + && DeviceEvent.Type.DEVICE_ADDED == event.type()) { + backgroundService.execute(() -> onOvsDetected(device)); + } else if (Device.Type.SWITCH == device.type() + && DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED == event + .type()) { + backgroundService.execute(() -> onOvsVanished(device)); + } else { + log.info("Do nothing for this device type"); + } + } + + } + + private class InnerHostListener implements HostListener { + + @Override + public void event(HostEvent event) { + Host host = event.subject(); + if (HostEvent.Type.HOST_ADDED == event.type()) { + backgroundService.execute(() -> onHostDetected(host)); + } else if (HostEvent.Type.HOST_REMOVED == event.type()) { + backgroundService.execute(() -> onHostVanished(host)); + } else if (HostEvent.Type.HOST_UPDATED == event.type()) { + backgroundService.execute(() -> { + onHostVanished(host); + onHostDetected(host); + }); + } + } + + } + + // Used to forward the flows to the local VM. + private void programLocalOut(DeviceId dpid, SegmentationId segmentationId, + PortNumber outPort, MacAddress sourceMac, + ApplicationId appid, + Objective.Operation type) { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchTunnelId(Long.parseLong(segmentationId.toString())) + .matchEthDst(sourceMac).build(); + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + .setOutput(outPort).build(); + ForwardingObjective.Builder objective = DefaultForwardingObjective + .builder().withTreatment(treatment).withSelector(selector) + .fromApp(appId).withFlag(Flag.SPECIFIC) + .withPriority(MAC_PRIORITY); + if (type.equals(Objective.Operation.ADD)) { + flowServiceForward(dpid, objective.add()); + } else { + flowServiceForward(dpid, objective.remove()); + } + + } + + // Used to forward the flows into the VXLAN tunnel. + private void programTunnelOut(DeviceId dpid, SegmentationId segmentationId, + PortNumber tunnelOutPort, MacAddress dstMac, + ApplicationId appid, + Objective.Operation type) { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchEthDst(dstMac).add(Criteria.matchTunnelId(Long + .parseLong(segmentationId.toString()))) + .build(); + TrafficTreatment treatment = DefaultTrafficTreatment.builder() + + .setOutput(tunnelOutPort).build(); + ForwardingObjective.Builder objective = DefaultForwardingObjective + .builder().withTreatment(treatment).withSelector(selector) + .fromApp(appId).withFlag(Flag.SPECIFIC) + .withPriority(MAC_PRIORITY); + if (type.equals(Objective.Operation.ADD)) { + flowServiceForward(dpid, objective.add()); + } else { + flowServiceForward(dpid, objective.remove()); + } + + } + + // Used to forward multicast flows to remote VMs of the same tenant via + // VXLAN tunnel. + private void programTunnelFloodOut(DeviceId deviceId, + SegmentationId segmentationId, + PortNumber ofPortOut, + List<PortNumber> localVmPorts, + ApplicationId appid, + Objective.Operation type) { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchInPort(ofPortOut) + + .add(Criteria.matchTunnelId(Long.parseLong(segmentationId + .toString()))).matchEthDst(MacAddress.BROADCAST) + .build(); + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); + + for (PortNumber outPort : localVmPorts) { + treatment.setOutput(outPort); + } + + ForwardingObjective.Builder objective = DefaultForwardingObjective + .builder().withTreatment(treatment.build()) + .withSelector(selector).fromApp(appId).makePermanent() + .withFlag(Flag.SPECIFIC).withPriority(MAC_PRIORITY); + if (type.equals(Objective.Operation.ADD)) { + flowServiceForward(deviceId, objective.add()); + } else { + flowServiceForward(deviceId, objective.remove()); + } + } + + // Applies default flows to mac table. + private void programMacDefaultRules(DeviceId dpid, ApplicationId appid, + Objective.Operation type) { + TrafficSelector selector = DefaultTrafficSelector.builder().build(); + TrafficTreatment treatment = DefaultTrafficTreatment.builder().drop() + .build(); + ForwardingObjective.Builder objective = DefaultForwardingObjective + .builder().withTreatment(treatment).withSelector(selector) + .fromApp(appId).makePermanent().withFlag(Flag.SPECIFIC) + .withPriority(DEFAULT_MAC_PRIORITY); + if (type.equals(Objective.Operation.ADD)) { + flowServiceForward(dpid, objective.add()); + } else { + flowServiceForward(dpid, objective.remove()); + } + } + + // Used to forward the flows to the local VMs with the same tenant. + private void programLocalBcastRules(DeviceId deviceId, + SegmentationId segmentationId, + PortNumber inPort, + List<PortNumber> localVmPorts, + List<PortNumber> localTunnelPorts, + ApplicationId appid, + Objective.Operation type) { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchInPort(inPort).matchEthDst(MacAddress.BROADCAST) + .add(Criteria.matchTunnelId(Long + .parseLong(segmentationId.toString()))) + .build(); + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); + for (PortNumber outPort : localVmPorts) { + if (inPort != outPort) { + treatment.setOutput(outPort); + } + } + for (PortNumber outport : localTunnelPorts) { + treatment.setOutput(outport); + } + ForwardingObjective.Builder objective = DefaultForwardingObjective + .builder().withTreatment(treatment.build()) + .withSelector(selector).fromApp(appId).makePermanent() + .withFlag(Flag.SPECIFIC).withPriority(MAC_PRIORITY); + if (type.equals(Objective.Operation.ADD)) { + flowServiceForward(deviceId, objective.add()); + } else { + flowServiceForward(deviceId, objective.remove()); + } + } + + // Used to apply local entry flow. + private void programLocalIn(DeviceId dpid, SegmentationId segmentationId, + PortNumber inPort, MacAddress srcMac, + ApplicationId appid, Objective.Operation type) { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchInPort(inPort).matchEthSrc(srcMac).build(); + TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder(); + treatment.add(Instructions.modTunnelId(Long.parseLong(segmentationId + .toString()))); + ForwardingObjective.Builder objective = DefaultForwardingObjective + .builder().withTreatment(treatment.build()) + .withSelector(selector).fromApp(appId).makePermanent() + .withFlag(Flag.SPECIFIC).withPriority(PORT_PRIORITY); + if (type.equals(Objective.Operation.ADD)) { + flowServiceForward(dpid, objective.add()); + } else { + flowServiceForward(dpid, objective.remove()); + } + } + + // Used to forward the flows from the egress tunnel to the VM. + private void programTunnelIn(DeviceId dpid, SegmentationId segmentationId, + PortNumber tunnelInPort, PortNumber outPort, + MacAddress sourceMac, ApplicationId appid, + Objective.Operation type) { + TrafficSelector selector = DefaultTrafficSelector.builder() + .matchInPort(tunnelInPort).add(Criteria.matchTunnelId(Long + .parseLong(segmentationId.toString()))) + .build(); + TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); + + ForwardingObjective.Builder objective = DefaultForwardingObjective + .builder().withTreatment(treatment).withSelector(selector) + .fromApp(appId).makePermanent().withFlag(Flag.SPECIFIC) + .withPriority(PORT_PRIORITY); + if (type.equals(Objective.Operation.ADD)) { + flowServiceForward(dpid, objective.add()); + } else { + flowServiceForward(dpid, objective.remove()); + } + } + + // Applies the default flows to port table. + private void programPortDefaultRules(DeviceId dpid, ApplicationId appid, + Objective.Operation type) { + TrafficSelector selector = DefaultTrafficSelector.builder().build(); + TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); + ForwardingObjective.Builder objective = DefaultForwardingObjective + .builder().withTreatment(treatment).withSelector(selector) + .fromApp(appId).makePermanent().withFlag(Flag.SPECIFIC) + .withPriority(DEFAULT_PORT_PRIORITY); + if (type.equals(Objective.Operation.ADD)) { + flowServiceForward(dpid, objective.add()); + } else { + flowServiceForward(dpid, objective.remove()); + } + } + + // Used to get channelId from the device annotations. + private String getControllerIpOfSwitch(DeviceId deviceId) { + Device device = deviceService.getDevice(deviceId); + String url = device.annotations().value(SWITCH_CHANNEL_ID); + return url.substring(0, url.lastIndexOf(":")); + } + + private Iterable<String> getIfaceIds(String ifaceId) { + VirtualPortId portId = VirtualPortId.portId(ifaceId); + VirtualPort port = virtualPortService.getPort(portId); + if (port == null) { + return Collections.emptyList(); + } + + TenantNetwork network = tenantNetworkService + .getNetwork(port.networkId()); + if (network == null) { + return Collections.emptyList(); + } + + Collection<VirtualPort> ports = virtualPortService + .getPorts(network.id()); + return ports.stream().map(p -> p.portId().portId()) + .collect(Collectors.toSet()); + } + + private List<PortNumber> getLocalPorts(DeviceId deviceId, String ifaceId) { + DriverHandler handler = driverService + .createHandler(getController(deviceId)); + BridgeConfig bridgeConfig = handler.behaviour(BridgeConfig.class); + Iterable<String> ifaceIds = getIfaceIds(ifaceId); + return bridgeConfig.getLocalPorts(ifaceIds); + } + + private DeviceId getController(DeviceId deviceId) { + Iterable<Device> devices = deviceService.getAvailableDevices(); + for (Device device : devices) { + if (device.type() == Device.Type.CONTROLLER && device.id() + .toString().contains(getControllerIpOfSwitch(deviceId))) { + return device.id(); + } + } + log.info("Can not find controller for device : {}", deviceId); + return null; + } + + //Used to apply flowRule + private void flowServiceForward(DeviceId deviceId, ForwardingObjective forwardingObjective) { + Driver driver = driverService.getDriver(DRIVER_NAME); + Pipeliner pipeLiner = driver.createBehaviour(new DefaultDriverData(driver, deviceId), Pipeliner.class); + if (pipeLiner != null) { + final PipelinerContext context = new InnerPipelineContext(); + pipeLiner.init(deviceId, context); + pipeLiner.forward(forwardingObjective); + } + } + + // Processing context for initializing pipeline driver behaviours. + private class InnerPipelineContext implements PipelinerContext { + @Override + public ServiceDirectory directory() { + return serviceDirectory; + } + + @Override + public FlowObjectiveStore store() { + return flowObjectiveStore; + } + } + +} diff --git a/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/package-info.java b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/package-info.java new file mode 100644 index 00000000..f18dbf8a --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * VTN application that applies configuration and flows to the device. + */ +package org.onosproject.vtn.impl; diff --git a/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/package-info.java b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/package-info.java new file mode 100644 index 00000000..371466c3 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * VTN application that applies configuration and flows to the device. + */ +package org.onosproject.vtn; diff --git a/framework/src/onos/apps/vtn/vtnrsc/pom.xml b/framework/src/onos/apps/vtn/vtnrsc/pom.xml new file mode 100644 index 00000000..8696295c --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/pom.xml @@ -0,0 +1,56 @@ +<?xml version="1.0"?> +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-app-vtn</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + + <artifactId>onos-app-vtn-rsc</artifactId> + <packaging>bundle</packaging> + + <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-cli</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + </dependency> + <dependency> + <groupId>org.apache.karaf.shell</groupId> + <artifactId>org.apache.karaf.shell.console</artifactId> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-core-serializers</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> +</project> diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllocationPool.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllocationPool.java new file mode 100644 index 00000000..3d6ba8e8 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllocationPool.java @@ -0,0 +1,38 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import org.onlab.packet.IpAddress; + +/** + * The continuous IP address range between the start address and the end address for the allocation pools. + */ +public interface AllocationPool { + + /** + * The start address for the allocation pool. + * + * @return startIp + */ + IpAddress startIp(); + + /** + * The end address for the allocation pool. + * + * @return endIp + */ + IpAddress endIp(); +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllowedAddressPair.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllowedAddressPair.java new file mode 100644 index 00000000..4e1028d8 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/AllowedAddressPair.java @@ -0,0 +1,94 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Objects; + +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; + +/** + * Immutable representation of a allowed address pair. + */ +public final class AllowedAddressPair { + private final IpAddress ip; + private final MacAddress mac; + // Public construction is prohibited + private AllowedAddressPair(IpAddress ip, MacAddress mac) { + checkNotNull(ip, "IpAddress cannot be null"); + checkNotNull(mac, "MacAddress cannot be null"); + this.ip = ip; + this.mac = mac; + } + /** + * Returns the AllowedAddressPair ip address. + * + * @return ip address + */ + public IpAddress ip() { + return ip; + } + + /** + * Returns the AllowedAddressPair MAC address. + * + * @return MAC address + */ + public MacAddress mac() { + return mac; + } + + + /** + * Creates a allowedAddressPair using the supplied ipAddress & + * macAddress. + * + * @param ip IP address + * @param mac MAC address + * @return AllowedAddressPair + */ + public static AllowedAddressPair allowedAddressPair(IpAddress ip, + MacAddress mac) { + return new AllowedAddressPair(ip, mac); + } + + @Override + public int hashCode() { + return Objects.hash(ip, mac); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof AllowedAddressPair) { + final AllowedAddressPair that = (AllowedAddressPair) obj; + return Objects.equals(this.ip, that.ip) + && Objects.equals(this.mac, that.mac); + } + return false; + } + + @Override + public String toString() { + return toStringHelper(this).add("ip", ip).add("mac", mac).toString(); + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/BindingHostId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/BindingHostId.java new file mode 100644 index 00000000..c715d08a --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/BindingHostId.java @@ -0,0 +1,72 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Objects; + +public final class BindingHostId { + private final String bindingHostId; + + // Public construction is prohibited + private BindingHostId(String bindingHostId) { + checkNotNull(bindingHostId, "BindingHosttId cannot be null"); + this.bindingHostId = bindingHostId; + } + + /** + * Creates a BindingHostId identifier. + * + * @param bindingHostId the bindingHostId identifier + * @return the bindingHostId identifier + */ + public static BindingHostId bindingHostId(String bindingHostId) { + return new BindingHostId(bindingHostId); + } + + /** + * Returns the bindingHostId identifier. + * + * @return the bindingHostId identifier + */ + public String bindingHostId() { + return bindingHostId; + } + + @Override + public int hashCode() { + return Objects.hash(bindingHostId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof BindingHostId) { + final BindingHostId that = (BindingHostId) obj; + return this.getClass() == that.getClass() + && Objects.equals(this.bindingHostId, that.bindingHostId); + } + return false; + } + + @Override + public String toString() { + return bindingHostId; + } +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultAllocationPool.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultAllocationPool.java new file mode 100644 index 00000000..8a480194 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultAllocationPool.java @@ -0,0 +1,81 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkNotNull; +import java.util.Objects; + +import org.onlab.packet.IpAddress; + +/** + * The continuous IP address range between the start address and the end address + * for the allocation pools. + */ +public final class DefaultAllocationPool implements AllocationPool { + + private final IpAddress startIp; + private final IpAddress endIp; + + /** + * Creates an AllocationPool by using the start IP address and the end IP + * address. + * + * @param startIp the start IP address of the allocation pool + * @param endIp the end IP address of the allocation pool + */ + public DefaultAllocationPool(IpAddress startIp, IpAddress endIp) { + checkNotNull(startIp, "StartIp cannot be null"); + checkNotNull(endIp, "EndIp cannot be null"); + this.startIp = startIp; + this.endIp = endIp; + } + + @Override + public IpAddress startIp() { + return startIp; + } + + @Override + public IpAddress endIp() { + return endIp; + } + + @Override + public int hashCode() { + return Objects.hash(startIp, endIp); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DefaultAllocationPool) { + final DefaultAllocationPool other = (DefaultAllocationPool) obj; + return Objects.equals(this.startIp, other.startIp) + && Objects.equals(this.endIp, other.endIp); + } + return false; + } + + @Override + public String toString() { + return toStringHelper(this).add("startIp", startIp).add("endIp", endIp) + .toString(); + } +} + diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultHostRoute.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultHostRoute.java new file mode 100644 index 00000000..8679100d --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultHostRoute.java @@ -0,0 +1,79 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import static com.google.common.base.MoreObjects.toStringHelper; + +import java.util.Objects; + +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; + +/** + * Host route dictionaries for the subnet. + */ +public final class DefaultHostRoute implements HostRoute { + + private final IpAddress nexthop; + private final IpPrefix destination; + + /** + * + * Creates a DefaultHostRoute by using the next hop and the destination. + * + * @param nexthop of the DefaultHostRoute + * @param destination of the DefaultHostRoute + */ + public DefaultHostRoute(IpAddress nexthop, IpPrefix destination) { + this.nexthop = nexthop; + this.destination = destination; + } + + @Override + public IpAddress nexthop() { + return nexthop; + } + + @Override + public IpPrefix destination() { + return destination; + } + + @Override + public String toString() { + return toStringHelper(this).add("nexthop", nexthop) + .add("destination", destination).toString(); + } + + @Override + public int hashCode() { + return Objects.hash(nexthop, destination); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DefaultHostRoute) { + final DefaultHostRoute other = (DefaultHostRoute) obj; + return Objects.equals(this.nexthop, other.nexthop) + && Objects.equals(this.destination, other.destination); + } + return false; + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultSubnet.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultSubnet.java new file mode 100644 index 00000000..6049b558 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultSubnet.java @@ -0,0 +1,183 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import static com.google.common.base.MoreObjects.toStringHelper; + +import java.util.Objects; +import java.util.Set; + +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpAddress.Version; +import org.onlab.packet.IpPrefix; + +/** + * Default implementation of Subnet interface . + */ +public final class DefaultSubnet implements Subnet { + private final SubnetId id; + private final String subnetName; + private final TenantNetworkId networkId; + private final TenantId tenantId; + private final Version ipVersion; + private final IpPrefix cidr; + private final IpAddress gatewayIp; + private final boolean dhcpEnabled; + private final boolean shared; + private final Mode ipV6AddressMode; + private final Mode ipV6RaMode; + private final Set<HostRoute> hostRoutes; + private final Set<AllocationPool> allocationPools; + + /** + * Creates a subnet object. + * + * @param id subnet identifier + * @param subnetName the name of subnet + * @param networkId network identifier + * @param tenantId tenant identifier + * @param ipVersion Version of ipv4 or ipv6 + * @param cidr the cidr + * @param gatewayIp gateway ip + * @param dhcpEnabled dhcp enabled or not + * @param shared indicates whether this network is shared across all + * tenants, By default, only administrative user can change this + * value + * @param hostRoutes a collection of host routes + * @param ipV6AddressMode ipV6AddressMode + * @param ipV6RaMode ipV6RaMode + * @param allocationPoolsIt a collection of allocationPools + */ + public DefaultSubnet(SubnetId id, String subnetName, + TenantNetworkId networkId, TenantId tenantId, + Version ipVersion, IpPrefix cidr, IpAddress gatewayIp, + boolean dhcpEnabled, boolean shared, + Set<HostRoute> hostRoutes, Mode ipV6AddressMode, + Mode ipV6RaMode, + Set<AllocationPool> allocationPoolsIt) { + this.id = id; + this.subnetName = subnetName; + this.networkId = networkId; + this.tenantId = tenantId; + this.ipVersion = ipVersion; + this.cidr = cidr; + this.gatewayIp = gatewayIp; + this.dhcpEnabled = dhcpEnabled; + this.shared = shared; + this.ipV6AddressMode = ipV6AddressMode; + this.ipV6RaMode = ipV6RaMode; + this.hostRoutes = hostRoutes; + this.allocationPools = allocationPoolsIt; + } + + @Override + public SubnetId id() { + return id; + } + + @Override + public String subnetName() { + return subnetName; + } + + @Override + public TenantNetworkId networkId() { + return networkId; + } + + @Override + public TenantId tenantId() { + return tenantId; + } + + @Override + public Version ipVersion() { + return ipVersion; + } + + @Override + public IpPrefix cidr() { + return cidr; + } + + @Override + public IpAddress gatewayIp() { + return gatewayIp; + } + + @Override + public boolean dhcpEnabled() { + return dhcpEnabled; + } + + @Override + public boolean shared() { + return shared; + } + + @Override + public Iterable<HostRoute> hostRoutes() { + return hostRoutes; + } + + @Override + public Mode ipV6AddressMode() { + return ipV6AddressMode; + } + + @Override + public Mode ipV6RaMode() { + return ipV6RaMode; + } + + @Override + public Iterable<AllocationPool> allocationPools() { + return allocationPools; + } + + @Override + public int hashCode() { + return Objects.hash(id, subnetName, ipVersion, cidr, gatewayIp, + dhcpEnabled, shared, tenantId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DefaultSubnet) { + final DefaultSubnet that = (DefaultSubnet) obj; + return Objects.equals(this.id, that.id) + && Objects.equals(this.subnetName, that.subnetName) + && Objects.equals(this.ipVersion, that.ipVersion) + && Objects.equals(this.cidr, that.cidr) + && Objects.equals(this.shared, that.shared) + && Objects.equals(this.gatewayIp, that.gatewayIp) + && Objects.equals(this.dhcpEnabled, that.dhcpEnabled); + } + return false; + } + + @Override + public String toString() { + return toStringHelper(this).add("id", id).add("subnetName", subnetName) + .add("ipVersion", ipVersion).add("cidr", cidr) + .add("shared", shared).add("gatewayIp", gatewayIp) + .add("dhcpEnabled", dhcpEnabled).toString(); + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultTenantNetwork.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultTenantNetwork.java new file mode 100644 index 00000000..8c941ea2 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultTenantNetwork.java @@ -0,0 +1,160 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import static com.google.common.base.MoreObjects.toStringHelper; + +import java.util.Objects; + +/** + * Default implementation of TenantNetwork interface. + */ +public final class DefaultTenantNetwork implements TenantNetwork { + private final TenantNetworkId id; + private final String name; + private final boolean adminStateUp; + private final State state; + private final boolean shared; + private final Type type; + private final TenantId tenantId; + private final boolean routerExternal; + private final PhysicalNetwork physicalNetwork; + private final SegmentationId segmentationId; + + /** + * Creates a neutronNetwork element attributed to the specified provider. + * + * @param id network identifier + * @param name the network name + * @param adminStateUp administrative state of the network + * @param state the network state + * @param shared indicates whether this network is shared across all + * tenants, By default, only administrative user can change this + * value + * @param tenantId tenant identifier + * @param routerExternal network routerExternal + * @param type the network type + * @param physicalNetwork physicalNetwork identifier + * @param segmentationId segmentation identifier + */ + public DefaultTenantNetwork(TenantNetworkId id, String name, + boolean adminStateUp, State state, + boolean shared, TenantId tenantId, + boolean routerExternal, Type type, + PhysicalNetwork physicalNetwork, + SegmentationId segmentationId) { + this.id = id; + this.name = name; + this.adminStateUp = adminStateUp; + this.state = state; + this.shared = shared; + this.type = type; + this.tenantId = tenantId; + this.routerExternal = routerExternal; + this.physicalNetwork = physicalNetwork; + this.segmentationId = segmentationId; + } + + @Override + public TenantNetworkId id() { + return id; + } + + @Override + public String name() { + return name; + } + + @Override + public boolean adminStateUp() { + return adminStateUp; + } + + @Override + public State state() { + return state; + } + + @Override + public boolean shared() { + return shared; + } + + @Override + public TenantId tenantId() { + return tenantId; + } + + @Override + public boolean routerExternal() { + return routerExternal; + } + + @Override + public Type type() { + return type; + } + + @Override + public PhysicalNetwork physicalNetwork() { + return physicalNetwork; + } + + @Override + public SegmentationId segmentationId() { + return segmentationId; + } + + @Override + public int hashCode() { + return Objects.hash(id, name, adminStateUp, state, shared, tenantId, + routerExternal, type, physicalNetwork, + segmentationId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DefaultTenantNetwork) { + final DefaultTenantNetwork that = (DefaultTenantNetwork) obj; + return Objects.equals(this.id, that.id) + && Objects.equals(this.name, that.name) + && Objects.equals(this.adminStateUp, that.adminStateUp) + && Objects.equals(this.state, that.state) + && Objects.equals(this.shared, that.shared) + && Objects.equals(this.tenantId, that.tenantId) + && Objects.equals(this.routerExternal, that.routerExternal) + && Objects.equals(this.type, that.type) + && Objects.equals(this.physicalNetwork, + that.physicalNetwork) + && Objects.equals(this.segmentationId, that.segmentationId); + } + return false; + } + + @Override + public String toString() { + return toStringHelper(this).add("id", id).add("name", name) + .add("adminStateUp", adminStateUp).add("state", state) + .add("shared", shared).add("tenantId", tenantId) + .add("routeExternal", routerExternal).add("type", type) + .add("physicalNetwork", physicalNetwork) + .add("segmentationId", segmentationId).toString(); + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultVirtualPort.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultVirtualPort.java new file mode 100644 index 00000000..9ee85da7 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/DefaultVirtualPort.java @@ -0,0 +1,229 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import static com.google.common.base.MoreObjects.toStringHelper; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import org.onlab.packet.MacAddress; +import org.onosproject.net.DeviceId; + +/** + * Default implementation of VirtualPort interface . + */ +public final class DefaultVirtualPort implements VirtualPort { + private final VirtualPortId id; + private final TenantNetworkId networkId; + private final Boolean adminStateUp; + private final String name; + private final State state; + private final MacAddress macAddress; + private final TenantId tenantId; + private final String deviceOwner; + private final DeviceId deviceId; + private final Set<FixedIp> fixedIps; + private final BindingHostId bindingHostId; + private final String bindingVnicType; + private final String bindingVifType; + private final String bindingVifDetails; + private final Set<AllowedAddressPair> allowedAddressPairs; + private final Set<SecurityGroup> securityGroups; + + /** + * Creates a VirtualPort object. + * + * @param id the virtual port identifier + * @param networkId the network identifier + * @param adminStateUp adminStateup true or false + * @param strMap the map of properties of virtual port + * @param state virtual port state + * @param macAddress the MAC address + * @param tenantId the tenant identifier + * @param deviceId the device identifier + * @param fixedIps set of fixed IP + * @param bindingHostId the binding host identifier + * @param allowedAddressPairs the collection of allowdeAddressPairs + * @param securityGroups the collection of securityGroups + */ + public DefaultVirtualPort(VirtualPortId id, + TenantNetworkId networkId, + Boolean adminStateUp, + Map<String, String> strMap, + State state, + MacAddress macAddress, + TenantId tenantId, + DeviceId deviceId, + Set<FixedIp> fixedIps, + BindingHostId bindingHostId, + Set<AllowedAddressPair> allowedAddressPairs, + Set<SecurityGroup> securityGroups) { + this.id = id; + this.networkId = networkId; + this.adminStateUp = adminStateUp; + this.name = strMap.get("name"); + this.state = state; + this.macAddress = macAddress; + this.tenantId = tenantId; + this.deviceOwner = strMap.get("deviceOwner"); + this.deviceId = deviceId; + this.fixedIps = fixedIps; + this.bindingHostId = bindingHostId; + this.bindingVnicType = strMap.get("bindingVnicType"); + this.bindingVifType = strMap.get("bindingVifType"); + this.bindingVifDetails = strMap.get("bindingVifDetails"); + this.allowedAddressPairs = allowedAddressPairs; + this.securityGroups = securityGroups; + } + + @Override + public VirtualPortId portId() { + return id; + } + + @Override + public TenantNetworkId networkId() { + return networkId; + } + + @Override + public String name() { + return name; + } + + @Override + public boolean adminStateUp() { + return adminStateUp; + } + + @Override + public State state() { + return state; + } + + @Override + public MacAddress macAddress() { + return macAddress; + } + + @Override + public TenantId tenantId() { + return tenantId; + } + + @Override + public DeviceId deviceId() { + return deviceId; + } + + @Override + public String deviceOwner() { + return deviceOwner; + } + + @Override + public Collection<AllowedAddressPair> allowedAddressPairs() { + return allowedAddressPairs; + } + + @Override + public Set<FixedIp> fixedIps() { + return fixedIps; + } + + @Override + public BindingHostId bindingHostId() { + return bindingHostId; + } + + @Override + public String bindingVnicType() { + return bindingVifType; + } + + @Override + public String bindingVifType() { + return bindingVifType; + } + + @Override + public String bindingVifDetails() { + return bindingVifDetails; + } + + @Override + public Collection<SecurityGroup> securityGroups() { + return securityGroups; + } + + @Override + public int hashCode() { + return Objects.hash(id, networkId, adminStateUp, name, state, + macAddress, tenantId, deviceId, deviceOwner, + allowedAddressPairs, fixedIps, bindingHostId, + bindingVnicType, bindingVifType, bindingVifDetails, + securityGroups); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DefaultVirtualPort) { + final DefaultVirtualPort that = (DefaultVirtualPort) obj; + return Objects.equals(this.id, that.id) + && Objects.equals(this.networkId, that.networkId) + && Objects.equals(this.adminStateUp, that.adminStateUp) + && Objects.equals(this.state, that.state) + && Objects.equals(this.name, that.name) + && Objects.equals(this.tenantId, that.tenantId) + && Objects.equals(this.macAddress, that.macAddress) + && Objects.equals(this.deviceId, that.deviceId) + && Objects.equals(this.deviceOwner, that.deviceOwner) + && Objects.equals(this.allowedAddressPairs, + that.allowedAddressPairs) + && Objects.equals(this.fixedIps, that.fixedIps) + && Objects.equals(this.bindingHostId, that.bindingHostId) + && Objects.equals(this.bindingVifDetails, + that.bindingVifDetails) + && Objects.equals(this.bindingVifType, that.bindingVifType) + && Objects.equals(this.bindingVnicType, + that.bindingVnicType) + && Objects.equals(this.securityGroups, that.securityGroups); + } + return false; + } + + @Override + public String toString() { + return toStringHelper(this).add("id", id).add("network_id", networkId) + .add("adminStateUp", adminStateUp).add("state", state) + .add("name", name).add("state", state) + .add("macAddress", macAddress).add("tenantId", tenantId) + .add("deviced", deviceId).add("deviceOwner", deviceOwner) + .add("allowedAddressPairs", allowedAddressPairs) + .add("fixedIp", fixedIps).add("bindingHostId", bindingHostId) + .add("bindingVnicType", bindingVnicType) + .add("bindingVifDetails", bindingVifDetails) + .add("bindingVifType", bindingVifType) + .add("securityGroups", securityGroups).toString(); + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/FixedIp.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/FixedIp.java new file mode 100644 index 00000000..c6569a7b --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/FixedIp.java @@ -0,0 +1,93 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Objects; + +import org.onlab.packet.IpAddress; + +/** + * Immutable representation of a IP address for the port, Include the IP address + * and subnet identity. + */ +public final class FixedIp { + private final SubnetId subnetId; + private final IpAddress ip; + // Public construction is prohibited + private FixedIp(SubnetId subnetId, IpAddress ip) { + checkNotNull(subnetId, "SubnetId cannot be null"); + checkNotNull(ip, "IpAddress cannot be null"); + this.subnetId = subnetId; + this.ip = ip; + } + + /** + * Returns the FixedIp subnet identifier. + * + * @return subnet identifier + */ + public SubnetId subnetId() { + return subnetId; + } + + /** + * Returns the FixedIp IP address. + * + * @return IP address + */ + public IpAddress ip() { + return ip; + } + + /** + * Creates a fixed ip using the supplied fixedIp. + * + * @param subnetId subnet identity + * @param ip IP address + * @return FixedIp + */ + public static FixedIp fixedIp(SubnetId subnetId, IpAddress ip) { + return new FixedIp(subnetId, ip); + } + + @Override + public int hashCode() { + return Objects.hash(subnetId, ip); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof FixedIp) { + final FixedIp that = (FixedIp) obj; + return Objects.equals(this.subnetId, that.subnetId) + && Objects.equals(this.ip, that.ip); + } + return false; + } + + @Override + public String toString() { + return toStringHelper(this).add("subnetId", subnetId).add("ip", ip) + .toString(); + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/HostRoute.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/HostRoute.java new file mode 100644 index 00000000..b18cb950 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/HostRoute.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; + +/** + * Host route dictionaries for the subnet. + */ +public interface HostRoute { + + /** + * Returns the next hop address. + * + * @return next hop address + */ + IpAddress nexthop(); + + /** + * Returns the destination address. + * + * @return destination address + */ + IpPrefix destination(); +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PhysicalNetwork.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PhysicalNetwork.java new file mode 100644 index 00000000..e96e666a --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/PhysicalNetwork.java @@ -0,0 +1,78 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import java.util.Objects; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Immutable representation of a physical network identity. + */ +public final class PhysicalNetwork { + + private final String physicalNetwork; + + // Public construction is prohibited + private PhysicalNetwork(String physicalNetwork) { + checkNotNull(physicalNetwork, "PhysicalNetwork cannot be null"); + this.physicalNetwork = physicalNetwork; + } + + /** + * Creates a PhysicalNetwork object. + * + * @param physicalNetwork physical network + * @return physical network + */ + public static PhysicalNetwork physicalNetwork(String physicalNetwork) { + return new PhysicalNetwork(physicalNetwork); + } + + /** + * Returns a physicalNetwork. + * + * @return physical network + */ + public String physicalNetwork() { + return physicalNetwork; + } + + @Override + public int hashCode() { + return Objects.hash(physicalNetwork); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof PhysicalNetwork) { + final PhysicalNetwork that = (PhysicalNetwork) obj; + return this.getClass() == that.getClass() + && Objects.equals(this.physicalNetwork, + that.physicalNetwork); + } + return false; + } + + @Override + public String toString() { + return physicalNetwork; + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SecurityGroup.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SecurityGroup.java new file mode 100644 index 00000000..9ec1dc63 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SecurityGroup.java @@ -0,0 +1,77 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import java.util.Objects; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Immutable representation of a security group. + */ +public final class SecurityGroup { + private final String securityGroup; + + /** + * Returns the securityGroup. + * + * @return securityGroup + */ + public String securityGroup() { + return securityGroup; + } + // Public construction is prohibited + private SecurityGroup(String securityGroup) { + checkNotNull(securityGroup, "SecurityGroup cannot be null"); + this.securityGroup = securityGroup; + } + + /** + * Creates a securityGroup using the supplied securityGroup. + * + * @param securityGroup security group + * @return securityGroup + */ + public static SecurityGroup securityGroup(String securityGroup) { + return new SecurityGroup(securityGroup); + } + + @Override + public int hashCode() { + return Objects.hash(securityGroup); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof SecurityGroup) { + final SecurityGroup that = (SecurityGroup) obj; + return this.getClass() == that.getClass() + && Objects.equals(this.securityGroup, that.securityGroup); + } + return false; + } + + @Override + public String toString() { + return toStringHelper(this).add("securityGroup", securityGroup) + .toString(); + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SegmentationId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SegmentationId.java new file mode 100644 index 00000000..a076265f --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SegmentationId.java @@ -0,0 +1,77 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import java.util.Objects; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Immutable representation of a Segmentation identifier. + */ +public final class SegmentationId { + + private final String segmentationId; + + // Public construction is prohibited + private SegmentationId(String segmentationId) { + checkNotNull(segmentationId, "SegmentationId cannot be null"); + this.segmentationId = segmentationId; + } + + /** + * Creates a SegmentationId object. + * + * @param segmentationId segmentation identifier + * @return SegmentationId + */ + public static SegmentationId segmentationId(String segmentationId) { + return new SegmentationId(segmentationId); + } + + /** + * Returns the segmentation identifier. + * + * @return segmentationId + */ + public String segmentationId() { + return segmentationId; + } + + @Override + public int hashCode() { + return Objects.hash(segmentationId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof SegmentationId) { + final SegmentationId that = (SegmentationId) obj; + return this.getClass() == that.getClass() + && Objects.equals(this.segmentationId, that.segmentationId); + } + return false; + } + + @Override + public String toString() { + return segmentationId; + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/Subnet.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/Subnet.java new file mode 100644 index 00000000..f563a78f --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/Subnet.java @@ -0,0 +1,129 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpAddress.Version; +import org.onlab.packet.IpPrefix; + +/** + * Representation of a subnet. + */ +public interface Subnet { + + /** + * Coarse classification of the type of the ipV6Mode. + */ + enum Mode { + DHCPV6_STATEFUL, DHCPV6_STATELESS, SLAAC + } + + /** + * Returns the subnet identifier. + * + * @return identifier + */ + SubnetId id(); + + /** + * Returns the name of the subnet. + * + * @return subnetName + */ + String subnetName(); + + /** + * Returns the network identifier. + * + * @return the network identifier + */ + TenantNetworkId networkId(); + + /** + * Returns tenant identifier. + * + * @return the tenant identifier + */ + TenantId tenantId(); + + /** + * Returns the IP version, which is 4 or 6. + * + * @return ipVersion + */ + Version ipVersion(); + + /** + * Returns the cidr. + * + * @return cidr + */ + IpPrefix cidr(); + + /** + * Returns the gateway IP address. + * + * @return gatewayIp + */ + IpAddress gatewayIp(); + + /** + * Returns true if DHCP is enabled and return false if DHCP is disabled. + * + * @return true or false + */ + boolean dhcpEnabled(); + + /** + * Indicates whether this tenantNetwork is shared across all tenants. By + * default, only administrative user can change this value. + * + * @return true or false + */ + boolean shared(); + + /** + * Returns a collection of hostRoutes. + * + * @return a collection of hostRoutes + */ + Iterable<HostRoute> hostRoutes(); + + /** + * Returns the ipV6AddressMode. A valid value is dhcpv6-stateful, + * dhcpv6-stateless, or slaac. + * + * @return ipV6AddressMode whose value is dhcpv6-stateful, dhcpv6-stateless + * or slaac + */ + Mode ipV6AddressMode(); + + /** + * Returns the ipV6RaMode.A valid value is dhcpv6-stateful, + * dhcpv6-stateless, or slaac. + * + * @return ipV6RaMode whose value is dhcpv6-stateful, dhcpv6-stateless or + * slaac + */ + Mode ipV6RaMode(); + + /** + * Returns a collection of allocation_pools. + * + * @return a collection of allocationPools + */ + Iterable<AllocationPool> allocationPools(); +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SubnetId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SubnetId.java new file mode 100644 index 00000000..4bcc3329 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/SubnetId.java @@ -0,0 +1,76 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Objects; + +/** + * Immutable representation of a subnet identifier. + */ +public final class SubnetId { + + private final String subnetId; + + // Public construction is prohibited + private SubnetId(String subnetId) { + checkNotNull(subnetId, "SubnetId cannot be null"); + this.subnetId = subnetId; + } + + /** + * Creates a Subnet identifier. + * + * @param subnetId the subnet identifier + * @return the subnet identifier + */ + public static SubnetId subnetId(String subnetId) { + return new SubnetId(subnetId); + } + + /** + * Returns the subnet identifier. + * + * @return the subnet identifier + */ + public String subnetId() { + return subnetId; + } + + @Override + public int hashCode() { + return Objects.hash(subnetId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof SubnetId) { + final SubnetId that = (SubnetId) obj; + return this.getClass() == that.getClass() + && Objects.equals(this.subnetId, that.subnetId); + } + return false; + } + + @Override + public String toString() { + return subnetId; + } +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantId.java new file mode 100644 index 00000000..c4d99e49 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantId.java @@ -0,0 +1,77 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import java.util.Objects; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Immutable representation of a tenant identifier. + */ +public final class TenantId { + + private final String tenantId; + + // Public construction is prohibited + private TenantId(String tenantId) { + this.tenantId = tenantId; + } + + /** + * Creates a network id using the tenantid. + * + * @param tenantid network String + * @return TenantId + */ + public static TenantId tenantId(String tenantid) { + checkNotNull(tenantid, "Tenantid can not be null"); + return new TenantId(tenantid); + } + + /** + * Returns the tenant identifier. + * + * @return the tenant identifier + */ + public String tenantId() { + return tenantId; + } + + @Override + public int hashCode() { + return Objects.hash(tenantId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof TenantId) { + final TenantId that = (TenantId) obj; + return this.getClass() == that.getClass() + && Objects.equals(this.tenantId, that.tenantId); + } + return false; + } + + @Override + public String toString() { + return tenantId; + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetwork.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetwork.java new file mode 100644 index 00000000..256352f4 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetwork.java @@ -0,0 +1,130 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +/** + * Representation of the tenantNetwork. + */ +public interface TenantNetwork { + + /** + * Coarse classification of the state of the tenantNetwork. + */ + enum State { + /** + * Signifies that a tenantNetwork is currently active.This state means + * that this network is available. + */ + ACTIVE, + /** + * Signifies that a tenantNetwork is currently built. + */ + BUILD, + /** + * Signifies that a tenantNetwork is currently unavailable. + */ + DOWN, + /** + * Signifies that a tenantNetwork is currently error. + */ + ERROR + } + + /** + * Coarse classification of the type of the tenantNetwork. + */ + enum Type { + /** + * Signifies that a tenantNetwork is local. + */ + LOCAL + } + + /** + * Returns the tenantNetwork identifier. + * + * @return tenantNetwork identifier + */ + TenantNetworkId id(); + + /** + * Returns the tenantNetwork name. + * + * @return tenantNetwork name + */ + String name(); + + /** + * Returns the administrative state of the tenantNetwork,which is up(true) + * or down(false). + * + * @return true or false + */ + boolean adminStateUp(); + + /** + * Returns the tenantNetwork state. + * + * @return tenant network state + */ + State state(); + + /** + * Indicates whether this tenantNetwork is shared across all tenants. By + * default,only administrative user can change this value. + * + * @return true or false + */ + boolean shared(); + + /** + * Returns the UUID of the tenant that will own the tenantNetwork. This + * tenant can be different from the tenant that makes the create + * tenantNetwork request. + * + * @return the tenant identifier + */ + TenantId tenantId(); + + /** + * Returns the routerExternal.Indicates whether this network is externally + * accessible. + * + * @return true or false + */ + boolean routerExternal(); + + /** + * Returns the tenantNetwork Type. + * + * @return tenantNetwork Type + */ + Type type(); + + /** + * Returns the tenantNetwork physical network. + * + * @return tenantNetwork physical network + */ + PhysicalNetwork physicalNetwork(); + + /** + * Returns the tenantNetwork segmentation id. + * + * @return tenantNetwork segmentation id + */ + SegmentationId segmentationId(); +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetworkId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetworkId.java new file mode 100644 index 00000000..fbb9e480 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/TenantNetworkId.java @@ -0,0 +1,76 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import java.util.Objects; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Immutable representation of a tenantNetwork identity. + */ +public final class TenantNetworkId { + + private final String networkId; + + // Public construction is prohibited + private TenantNetworkId(String networkId) { + this.networkId = networkId; + } + + /** + * Creates a TenantNetwork identifier. + * + * @param networkId tenantNetwork identify string + * @return the attached tenantNetwork identifier + */ + public static TenantNetworkId networkId(String networkId) { + checkNotNull(networkId, "Networkid cannot be null"); + return new TenantNetworkId(networkId); + } + + /** + * Returns tenantNetwork identifier. + * + * @return the tenantNetwork identifier + */ + public String networkId() { + return networkId; + } + + @Override + public int hashCode() { + return Objects.hash(networkId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof TenantNetworkId) { + final TenantNetworkId that = (TenantNetworkId) obj; + return this.getClass() == that.getClass() + && Objects.equals(this.networkId, that.networkId); + } + return false; + } + + @Override + public String toString() { + return networkId; + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPort.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPort.java new file mode 100644 index 00000000..d2d7c146 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPort.java @@ -0,0 +1,156 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import java.util.Collection; +import java.util.Set; + +import org.onlab.packet.MacAddress; +import org.onosproject.net.DeviceId; + +/** + * Representation of the VirtualPort. + */ +public interface VirtualPort { + /** + * Coarse classification of the type of the virtual port. + */ + enum State { + /** + * Signifies that a virtualPort is currently active,This state mean that + * this virtualPort is available. + */ + ACTIVE, + /** + * Signifies that a virtualPort is currently unavailable. + */ + DOWN; + } + + /** + * Returns the virtualPort identifier. + * + * @return virtualPort identifier + */ + VirtualPortId portId(); + + /** + * Returns the network identifier. + * + * @return tenantNetwork identifier + */ + TenantNetworkId networkId(); + + /** + * Returns the symbolic name for the virtualPort. + * + * @return virtualPort name + */ + String name(); + + /** + * Returns the administrative status of the port,which is up(true) or + * down(false). + * + * @return true if the administrative status of the port is up + */ + boolean adminStateUp(); + + /** + * Returns the state. + * + * @return state + */ + State state(); + + /** + * Returns the MAC address. + * + * @return MAC Address + */ + MacAddress macAddress(); + + /** + * Returns the port tenantId. + * + * @return port tenantId + */ + TenantId tenantId(); + + /** + * Returns the device identifier. + * + * @return deviceId + */ + DeviceId deviceId(); + + /** + * Returns the identifier of the entity that uses this port. + * + * @return deviceOwner + */ + String deviceOwner(); + + /** + * Returns the virtualPort allowedAddressPairs. + * + * @return virtualPort allowedAddressPairs + */ + Collection<AllowedAddressPair> allowedAddressPairs(); + + /** + * Returns set of IP addresses for the port, include the IP addresses and subnet + * identity. + * + * @return FixedIps Set of fixedIp + */ + Set<FixedIp> fixedIps(); + + /** + * Returns the virtualPort bindinghostId. + * + * @return virtualPort bindinghostId + */ + BindingHostId bindingHostId(); + + /** + * Returns the virtualPort bindingVnicType. + * + * @return virtualPort bindingVnicType + */ + String bindingVnicType(); + + /** + * Returns the virtualPort bindingVifType. + * + * @return virtualPort bindingVifType + */ + String bindingVifType(); + + /** + * Returns the virtualPort bindingvifDetail. + * + * @return virtualPort bindingvifDetail + */ + String bindingVifDetails(); + + /** + * Returns the security groups. + * + * @return port security groups + */ + Iterable<SecurityGroup> securityGroups(); +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPortId.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPortId.java new file mode 100644 index 00000000..3038bdff --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/VirtualPortId.java @@ -0,0 +1,70 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Objects; + +/** + * Immutable representation of a virtual port identifier. + */ +public final class VirtualPortId { + private final String portId; + // Public construction is prohibited + private VirtualPortId(String virtualPortId) { + checkNotNull(virtualPortId, "VirtualPortId cannot be null"); + this.portId = virtualPortId; + } + + public String portId() { + return portId; + } + + /** + * Creates a virtualPort id using the supplied portId. + * + * @param portId virtualport identifier + * @return VirtualPortId + */ + public static VirtualPortId portId(String portId) { + return new VirtualPortId(portId); + } + + @Override + public int hashCode() { + return Objects.hash(portId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof VirtualPortId) { + final VirtualPortId that = (VirtualPortId) obj; + return this.getClass() == that.getClass() + && Objects.equals(this.portId, that.portId); + } + return false; + } + + @Override + public String toString() { + return portId; + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkCreateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkCreateCommand.java new file mode 100644 index 00000000..bcfdacfa --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkCreateCommand.java @@ -0,0 +1,97 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.cli.network; + +import java.util.Set; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.apache.karaf.shell.commands.Option; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.vtnrsc.DefaultTenantNetwork; +import org.onosproject.vtnrsc.PhysicalNetwork; +import org.onosproject.vtnrsc.SegmentationId; +import org.onosproject.vtnrsc.TenantId; +import org.onosproject.vtnrsc.TenantNetwork; +import org.onosproject.vtnrsc.TenantNetworkId; +import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService; + +import com.google.common.collect.Sets; + +/** + * Supports for creating a TenantNetwork. + */ +@Command(scope = "onos", name = "tenantnetwork-create", + description = "Supports for creating a TenantNetwork") +public class TenantNetworkCreateCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "id", description = "TenantNetwork network id", required = true, + multiValued = false) + String id = null; + + @Argument(index = 1, name = "tenantID", description = "The tenant id of TenantNetwork", + required = true, multiValued = false) + String tenantID = null; + + @Argument(index = 2, name = "type", description = "The type of TenantNetwork", required = true, + multiValued = false) + String type = null; + + @Argument(index = 3, name = "segmentationID", description = "The segmentation id of TenantNetwork", + required = true, multiValued = false) + String segmentationID = ""; + + @Option(name = "-n", aliases = "--name", description = "TenantNetwork name", required = false, + multiValued = false) + String name = null; + + @Option(name = "-a", aliases = "--adminStateUp", description = "TenantNetwork adminStateUp is true or false", + required = false, multiValued = false) + boolean adminStateUp = false; + + @Option(name = "-s", aliases = "--state", description = "The state of TenantNetwork", + required = false, multiValued = false) + String state = null; + + @Option(name = "-d", aliases = "--shared", description = "TenantNetwork is shared or not", + required = false, multiValued = false) + boolean shared = false; + + @Option(name = "-r", aliases = "--routerExternal", + description = "TenantNetwork is routerExternal or not", required = false, + multiValued = false) + boolean routerExternal = false; + + @Option(name = "-p", aliases = "--physicalNetwork", description = "The physical network of Tenant", + required = false, multiValued = false) + String physicalNetwork = ""; + + @Override + protected void execute() { + TenantNetworkService service = get(TenantNetworkService.class); + TenantNetwork network = new DefaultTenantNetwork(TenantNetworkId.networkId(id), name, + adminStateUp, + TenantNetwork.State.valueOf(state), + shared, TenantId.tenantId(tenantID), + routerExternal, + TenantNetwork.Type.valueOf(type), + PhysicalNetwork.physicalNetwork(physicalNetwork), + SegmentationId.segmentationId(segmentationID)); + + Set<TenantNetwork> networksSet = Sets.newHashSet(network); + service.createNetworks(networksSet); + } +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkQueryCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkQueryCommand.java new file mode 100644 index 00000000..47ea83c2 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkQueryCommand.java @@ -0,0 +1,60 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.cli.network; + +import org.apache.karaf.shell.commands.Command; +import org.apache.karaf.shell.commands.Option; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.vtnrsc.TenantNetwork; +import org.onosproject.vtnrsc.TenantNetworkId; +import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService; + +/** + * Supports for querying TenantNetworks by network id. + */ +@Command(scope = "onos", name = "tenantnetworks", description = "Supports for querying" + + "tenantNetworks by networkid") +public class TenantNetworkQueryCommand extends AbstractShellCommand { + + @Option(name = "-i", aliases = "--id", description = "TenantNetwork id", required = false, + multiValued = false) + String id = null; + + private static final String FMT = "networkId=%s, networkName=%s, segmentationId=%s," + + "tenantId=%s, type=%s, adminStateUp=%s"; + + @Override + protected void execute() { + TenantNetworkService service = get(TenantNetworkService.class); + if (id != null) { + TenantNetwork network = service.getNetwork(TenantNetworkId.networkId(id)); + printNetwork(network); + } else { + Iterable<TenantNetwork> networks = service.getNetworks(); + for (TenantNetwork network : networks) { + printNetwork(network); + } + } + } + + private void printNetwork(TenantNetwork network) { + if (network == null) { + return; + } + print(FMT, network.id(), network.name(), network.segmentationId(), + network.tenantId(), network.type(), network.adminStateUp()); + } +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkRemoveCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkRemoveCommand.java new file mode 100644 index 00000000..0ea22853 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkRemoveCommand.java @@ -0,0 +1,45 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.cli.network; + +import java.util.Set; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.vtnrsc.TenantNetworkId; +import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService; + +import com.google.common.collect.Sets; + +/** + * Supports for removing a TenantNetwork by network id. + */ +@Command(scope = "onos", name = "tenantnetwork-remove", description = "Supports for removing" + + " a tenantNetwork by tenantNetworkid") +public class TenantNetworkRemoveCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "id", description = "TenantNetwork neutronNetwork Id", + required = true, multiValued = false) + String id = null; + + @Override + protected void execute() { + TenantNetworkService service = get(TenantNetworkService.class); + Set<TenantNetworkId> networkIds = Sets.newHashSet(TenantNetworkId.networkId(id)); + service.removeNetworks(networkIds); + } +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkUpdateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkUpdateCommand.java new file mode 100644 index 00000000..2a738f72 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/TenantNetworkUpdateCommand.java @@ -0,0 +1,99 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.cli.network; + +import java.util.Set; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.apache.karaf.shell.commands.Option; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.vtnrsc.DefaultTenantNetwork; +import org.onosproject.vtnrsc.PhysicalNetwork; +import org.onosproject.vtnrsc.SegmentationId; +import org.onosproject.vtnrsc.TenantId; +import org.onosproject.vtnrsc.TenantNetwork; +import org.onosproject.vtnrsc.TenantNetworkId; +import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService; + +import com.google.common.collect.Sets; + +/** + * Supports for updating a TenantNetwork. + */ +@Command(scope = "onos", name = "tenantnetwork-update", + description = "Supports for updating a TenantNetwork") +public class TenantNetworkUpdateCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "id", description = "TenantNetwork network id", required = true, + multiValued = false) + String id = null; + + @Argument(index = 1, name = "tenantID", description = "The tenant id of TenantNetwork", + required = true, multiValued = false) + String tenantID = null; + + @Argument(index = 2, name = "type", description = "The type of TenantNetwork", required = true, + multiValued = false) + String type = null; + + @Argument(index = 3, name = "segmentationID", description = "The segmentation id of TenantNetwork", + required = true, multiValued = false) + String segmentationID = ""; + + @Option(name = "-n", aliases = "--name", description = "TenantNetwork name", required = false, + multiValued = false) + String name = null; + + @Option(name = "-a", aliases = "--adminStateUp", description = "TenantNetwork adminStateUp is true or false", + required = false, multiValued = false) + boolean adminStateUp = false; + + @Option(name = "-s", aliases = "--state", description = "The state of TenantNetwork", + required = false, multiValued = false) + String state = null; + + @Option(name = "-d", aliases = "--shared", description = "TenantNetwork is shared or not", + required = false, multiValued = false) + boolean shared = false; + + @Option(name = "-r", aliases = "--routerExternal", + description = "TenantNetwork is routerExternal or not", required = false, + multiValued = false) + boolean routerExternal = false; + + @Option(name = "-p", aliases = "--physicalNetwork", description = "The physical network of Tenant", + required = false, multiValued = false) + String physicalNetwork = ""; + + @Override + protected void execute() { + TenantNetworkService service = get(TenantNetworkService.class); + TenantNetwork network = new DefaultTenantNetwork(TenantNetworkId.networkId(id), name, + adminStateUp, + TenantNetwork.State.valueOf(state), + shared, TenantId.tenantId(tenantID), + routerExternal, + TenantNetwork.Type.valueOf(type), + PhysicalNetwork.physicalNetwork(physicalNetwork), + SegmentationId.segmentationId(segmentationID)); + + Set<TenantNetwork> networksSet = Sets.newHashSet(); + networksSet.add(network); + service.updateNetworks(networksSet); + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/package-info.java new file mode 100644 index 00000000..1622c800 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/network/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Command line interface for tenant networks. + */ +package org.onosproject.vtnrsc.cli.network; diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetCreateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetCreateCommand.java new file mode 100644 index 00000000..56236408 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetCreateCommand.java @@ -0,0 +1,118 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.cli.subnet; + +import java.util.Set; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.apache.karaf.shell.commands.Option; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpAddress.Version; +import org.onlab.packet.IpPrefix; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.vtnrsc.AllocationPool; +import org.onosproject.vtnrsc.DefaultSubnet; +import org.onosproject.vtnrsc.HostRoute; +import org.onosproject.vtnrsc.Subnet; +import org.onosproject.vtnrsc.Subnet.Mode; +import org.onosproject.vtnrsc.SubnetId; +import org.onosproject.vtnrsc.TenantId; +import org.onosproject.vtnrsc.TenantNetworkId; +import org.onosproject.vtnrsc.subnet.SubnetService; + +import com.google.common.collect.Sets; + +/** + * Supports for creating a subnet. + */ +@Command(scope = "onos", name = "subnet-create", description = "Supports for creating a subnet") +public class SubnetCreateCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "id", description = "Subnet Id", required = true, + multiValued = false) + String id = null; + + @Argument(index = 1, name = "subnetName", description = "Subnet String name", required = true, + multiValued = false) + String subnetName = null; + + @Argument(index = 2, name = "networkId", description = "Subnet Network Id", required = true, + multiValued = false) + String networkId = null; + + @Argument(index = 3, name = "tenantId", description = "Subnet Tenant Id", required = true, + multiValued = false) + String tenantId = null; + + @Option(name = "-i", aliases = "--ipVersion", description = "Subnet Version ipVersion", + required = false, multiValued = false) + Version ipVersion = null; + + @Option(name = "-c", aliases = "--cidr", description = "Subnet IpPrefix cidr", + required = false, multiValued = false) + String cidr = "0.0.0.0/0"; + + @Option(name = "-g", aliases = "--gatewayIp", description = "Subnet IpAddress gatewayIp", + required = false, multiValued = false) + String gatewayIp = "0.0.0.0"; + + @Option(name = "-d", aliases = "--dhcpEnabled", description = "Subnet boolean dhcpEnabled", + required = false, multiValued = false) + boolean dhcpEnabled = false; + + @Option(name = "-s", aliases = "--shared", description = "Subnet boolean shared", + required = false, multiValued = false) + boolean shared = false; + + @Option(name = "-m", aliases = "--ipV6AddressMode", + description = "Subnet Mode ipV6AddressMode", required = false, multiValued = false) + String ipV6AddressMode = null; + + @Option(name = "-r", aliases = "--ipV6RaMode", description = "Subnet Mode ipV6RaMode", + required = false, multiValued = false) + String ipV6RaMode = null; + + @Option(name = "-h", aliases = "--hostRoutes", description = "Subnet jsonnode hostRoutes", + required = false, multiValued = false) + Set<HostRoute> hostRoutes = Sets.newHashSet(); + + @Option(name = "-a", aliases = "--allocationPools", + description = "Subnet jsonnode allocationPools", required = false, multiValued = false) + Set<AllocationPool> allocationPools = Sets.newHashSet(); + + @Override + protected void execute() { + SubnetService service = get(SubnetService.class); + if (id == null || networkId == null || tenantId == null) { + print(null, "id,networkId,tenantId can not be null"); + return; + } + Subnet subnet = new DefaultSubnet(SubnetId.subnetId(id), subnetName, + TenantNetworkId.networkId(networkId), + TenantId.tenantId(tenantId), ipVersion, + cidr == null ? null : IpPrefix.valueOf(cidr), + gatewayIp == null ? null : IpAddress.valueOf(gatewayIp), + dhcpEnabled, shared, hostRoutes, + ipV6AddressMode == null ? null : Mode.valueOf(ipV6AddressMode), + ipV6RaMode == null ? null : Mode.valueOf(ipV6RaMode), + allocationPools); + + Set<Subnet> subnetsSet = Sets.newHashSet(subnet); + service.createSubnets(subnetsSet); + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetQueryCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetQueryCommand.java new file mode 100644 index 00000000..f5a94f0f --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetQueryCommand.java @@ -0,0 +1,61 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.cli.subnet; + +import org.apache.karaf.shell.commands.Command; +import org.apache.karaf.shell.commands.Option; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.vtnrsc.Subnet; +import org.onosproject.vtnrsc.SubnetId; +import org.onosproject.vtnrsc.subnet.SubnetService; + +/** + * Supports for querying a subnet. + */ +@Command(scope = "onos", name = "subnets", description = "Supports for querying a subnet") +public class SubnetQueryCommand extends AbstractShellCommand { + + @Option(name = "-i", aliases = "--id", description = "Subnet id", required = false, + multiValued = false) + String id = null; + + private static final String FMT = "subnetId=%s, networkId=%s, subnetName=%s," + + "tenantId=%s, cidr=%s, dhcpEnabled=%s, gatewayIp=%s," + "ipVersion=%s"; + + @Override + protected void execute() { + SubnetService service = get(SubnetService.class); + if (id != null) { + Subnet subnet = service.getSubnet(SubnetId.subnetId(id)); + printSubnet(subnet); + } else { + Iterable<Subnet> subnets = service.getSubnets(); + if (subnets == null) { + return; + } + for (Subnet subnet : subnets) { + printSubnet(subnet); + } + } + } + + private void printSubnet(Subnet subnet) { + print(FMT, subnet.id(), subnet.networkId(), subnet.subnetName(), + subnet.tenantId(), subnet.cidr(), subnet.dhcpEnabled(), subnet + .gatewayIp(), subnet.ipVersion()); + + } +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetRemoveCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetRemoveCommand.java new file mode 100644 index 00000000..241af87e --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetRemoveCommand.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.cli.subnet; + +import java.util.Set; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.vtnrsc.SubnetId; +import org.onosproject.vtnrsc.subnet.SubnetService; + +import com.google.common.collect.Sets; + +/** + * Supports for removing a subnet. + */ +@Command(scope = "onos", name = "subnet-remove", description = "Supports for removing a subnet") +public class SubnetRemoveCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "id", description = "Subnet SubnetId Id", required = true, + multiValued = false) + String id = null; + + @Override + protected void execute() { + SubnetService service = get(SubnetService.class); + Set<SubnetId> subnetsSet = Sets.newHashSet(); + subnetsSet.add(SubnetId.subnetId(id)); + service.removeSubnets(subnetsSet); + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetUpdateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetUpdateCommand.java new file mode 100644 index 00000000..b0578a1e --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/SubnetUpdateCommand.java @@ -0,0 +1,118 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.cli.subnet; + +import java.util.Set; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.apache.karaf.shell.commands.Option; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpAddress.Version; +import org.onlab.packet.IpPrefix; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.vtnrsc.AllocationPool; +import org.onosproject.vtnrsc.DefaultSubnet; +import org.onosproject.vtnrsc.HostRoute; +import org.onosproject.vtnrsc.Subnet; +import org.onosproject.vtnrsc.Subnet.Mode; +import org.onosproject.vtnrsc.SubnetId; +import org.onosproject.vtnrsc.TenantId; +import org.onosproject.vtnrsc.TenantNetworkId; +import org.onosproject.vtnrsc.subnet.SubnetService; + +import com.google.common.collect.Sets; + +/** + * Supports for updating a subnet. + */ +@Command(scope = "onos", name = "subnet-update", description = "Supports for updating a subnet") +public class SubnetUpdateCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "id", description = "Subnet Id", required = true, + multiValued = false) + String id = null; + + @Argument(index = 1, name = "subnetName", description = "Subnet String name", required = true, + multiValued = false) + String subnetName = null; + + @Argument(index = 2, name = "networkId", description = "Subnet Network Id", required = true, + multiValued = false) + String networkId = null; + + @Argument(index = 3, name = "tenantId", description = "Subnet Tenant Id", required = true, + multiValued = false) + String tenantId = null; + + @Option(name = "-i", aliases = "--ipVersion", description = "Subnet Version ipVersion", + required = false, multiValued = false) + Version ipVersion = null; + + @Option(name = "-c", aliases = "--cidr", description = "Subnet IpPrefix cidr", required = false, + multiValued = false) + String cidr = "0.0.0.0/0"; + + @Option(name = "-g", aliases = "--gatewayIp", description = "Subnet IpAddress gatewayIp", + required = false, multiValued = false) + String gatewayIp = "0.0.0.0"; + + @Option(name = "-d", aliases = "--dhcpEnabled", description = "Subnet boolean dhcpEnabled", + required = false, multiValued = false) + boolean dhcpEnabled = false; + + @Option(name = "-s", aliases = "--shared", description = "Subnet boolean shared", required = false, + multiValued = false) + boolean shared = false; + + @Option(name = "-m", aliases = "--ipV6AddressMode", description = "Subnet Mode ipV6AddressMode", + required = false, multiValued = false) + String ipV6AddressMode = null; + + @Option(name = "-r", aliases = "--ipV6RaMode", description = "Subnet Mode ipV6RaMode", + required = false, multiValued = false) + String ipV6RaMode = null; + + @Option(name = "-h", aliases = "--hostRoutes", description = "Subnet jsonnode hostRoutes", + required = false, multiValued = false) + Set<HostRoute> hostRoutes = Sets.newHashSet(); + + @Option(name = "-a", aliases = "--allocationPools", + description = "Subnet jsonnode allocationPools", required = false, multiValued = false) + Set<AllocationPool> allocationPools = Sets.newHashSet(); + + @Override + protected void execute() { + SubnetService service = get(SubnetService.class); + if (id == null || networkId == null || tenantId == null) { + print(null, "id,networkId,tenantId can not be null"); + return; + } + Subnet subnet = new DefaultSubnet(SubnetId.subnetId(id), subnetName, + TenantNetworkId.networkId(networkId), + TenantId.tenantId(tenantId), ipVersion, + cidr == null ? null : IpPrefix.valueOf(cidr), + gatewayIp == null ? null : IpAddress.valueOf(gatewayIp), + dhcpEnabled, shared, hostRoutes, + ipV6AddressMode == null ? null : Mode.valueOf(ipV6AddressMode), + ipV6RaMode == null ? null : Mode.valueOf(ipV6RaMode), + allocationPools); + Set<Subnet> subnetsSet = Sets.newHashSet(); + subnetsSet.add(subnet); + service.updateSubnets(subnetsSet); + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/package-info.java new file mode 100644 index 00000000..b3a2ff51 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/subnet/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Command line interface for subnets. + */ +package org.onosproject.vtnrsc.cli.subnet; diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortCreateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortCreateCommand.java new file mode 100644 index 00000000..4c555e33 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortCreateCommand.java @@ -0,0 +1,134 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.cli.virtualport; + +import java.util.Map; +import java.util.Set; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.apache.karaf.shell.commands.Option; +import org.onlab.packet.MacAddress; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.net.DeviceId; +import org.onosproject.vtnrsc.AllowedAddressPair; +import org.onosproject.vtnrsc.BindingHostId; +import org.onosproject.vtnrsc.DefaultVirtualPort; +import org.onosproject.vtnrsc.FixedIp; +import org.onosproject.vtnrsc.SecurityGroup; +import org.onosproject.vtnrsc.TenantId; +import org.onosproject.vtnrsc.TenantNetworkId; +import org.onosproject.vtnrsc.VirtualPort; +import org.onosproject.vtnrsc.VirtualPortId; +import org.onosproject.vtnrsc.virtualport.VirtualPortService; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +/** + * Supports for creating a virtualPort. + */ +@Command(scope = "onos", name = "virtualport-create", + description = "Supports for creating a virtualPort.") +public class VirtualPortCreateCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "id", description = "virtualPort id.", required = true, + multiValued = false) + String id = null; + + @Argument(index = 1, name = "networkId", description = "network id.", required = true, + multiValued = false) + String networkId = null; + + @Argument(index = 2, name = "name", description = "virtualPort name.", required = true, + multiValued = false) + String name = null; + + @Argument(index = 3, name = "tenantId", description = "tenant id.", required = true, + multiValued = false) + String tenantId = null; + + @Argument(index = 4, name = "deviceId", description = "device id.", required = true, + multiValued = false) + String deviceId = null; + + @Option(name = "-a", aliases = "--adminStateUp", + description = "administrative status of the virtualPort which is true or false.", + required = false, multiValued = false) + Boolean adminStateUp = false; + + @Option(name = "-s", aliases = "--state", description = "virtualPort state.", required = false, + multiValued = false) + String state = null; + + @Option(name = "-m", aliases = "--macAddress", description = "MAC address.", required = false, + multiValued = false) + String macAddress = ""; + + @Option(name = "-d", aliases = "--deviceOwner", description = "ID of the entity that uses this " + + "virtualPort.", required = false, multiValued = false) + String deviceOwner = null; + + @Option(name = "-f", aliases = "--fixedIp", + description = "The IP address for the port,include the IP address " + + "and subnet identity.", required = false, multiValued = false) + FixedIp fixedIp = null; + + @Option(name = "-i", aliases = "--bindingHostId", description = "virtualPort bindingHostId.", + required = false, multiValued = false) + String bindingHostId = null; + + @Option(name = "-t", aliases = "--bindingvnicType", description = "virtualPort bindingvnicType.", + required = false, multiValued = false) + String bindingvnicType = null; + + @Option(name = "-v", aliases = "--bindingvifType", description = "virtualPort bindingvifType.", + required = false, multiValued = false) + String bindingvifType = null; + + @Option(name = "-b", aliases = "--bindingvnicDetails", + description = "virtualPort bindingvnicDetails.", required = false, multiValued = false) + String bindingvnicDetails = null; + + @Option(name = "-l", aliases = "--allowedAddress", description = "virtual allowedAddressPair.", + required = false, multiValued = false) + Set<AllowedAddressPair> allowedAddressPairs = Sets.newHashSet(); + + @Option(name = "-e", aliases = "--securityGroups", description = "virtualPort securityGroups.", + required = false, multiValued = false) + Set<SecurityGroup> securityGroups = Sets.newHashSet(); + + @Override + protected void execute() { + Map<String, String> strMap = Maps.newHashMap(); + strMap.putIfAbsent("name", name); + strMap.putIfAbsent("deviceOwner", deviceOwner); + strMap.putIfAbsent("bindingvnicType", bindingvnicType); + strMap.putIfAbsent("bindingvifType", bindingvifType); + strMap.putIfAbsent("bindingvnicDetails", bindingvnicDetails); + VirtualPortService service = get(VirtualPortService.class); + VirtualPort virtualPort = new DefaultVirtualPort(VirtualPortId.portId(id), + TenantNetworkId.networkId(networkId), + false, strMap, VirtualPort.State.ACTIVE, + MacAddress.valueOf(macAddress), + TenantId.tenantId(tenantId), + DeviceId.deviceId(deviceId), Sets.newHashSet(fixedIp), + BindingHostId.bindingHostId(bindingHostId), + allowedAddressPairs, securityGroups); + Set<VirtualPort> virtualPorts = Sets.newHashSet(virtualPort); + service.createPorts(virtualPorts); + } +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortQueryCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortQueryCommand.java new file mode 100644 index 00000000..47126d1b --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortQueryCommand.java @@ -0,0 +1,94 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.cli.virtualport; + +import java.util.Collection; + +import org.apache.karaf.shell.commands.Command; +import org.apache.karaf.shell.commands.Option; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.net.DeviceId; +import org.onosproject.vtnrsc.TenantNetworkId; +import org.onosproject.vtnrsc.VirtualPort; +import org.onosproject.vtnrsc.VirtualPortId; +import org.onosproject.vtnrsc.virtualport.VirtualPortService; + +/** + * Supports for querying virtualPorts. + */ +@Command(scope = "onos", name = "virtualports", description = "Supports for querying virtualPorts.") +public class VirtualPortQueryCommand extends AbstractShellCommand { + + @Option(name = "-v", aliases = "--vPortId", description = "virtualPort ID.", required = false, + multiValued = false) + String vPortId; + + @Option(name = "-n", aliases = "--networkId", description = "network ID.", required = false, + multiValued = false) + String networkId; + + @Option(name = "-d", aliases = "--deviceId", description = "device ID.", required = false, + multiValued = false) + String deviceId; + + @Option(name = "-t", aliases = "--tenantId", description = "tenant ID.", required = false, + multiValued = false) + String tenantId; + + private static final String FMT = "virtualPortId=%s, networkId=%s, name=%s," + + " tenantId=%s, deviceId=%s, adminStateUp=%s, state=%s," + + " macAddress=%s, deviceOwner=%s, fixedIp=%s, bindingHostId=%s," + + " bindingvnicType=%s, bindingvifType=%s, bindingvnicDetails=%s," + + " allowedAddress=%s, securityGroups=%s"; + + @Override + protected void execute() { + VirtualPortService service = get(VirtualPortService.class); + if (vPortId != null && networkId == null && deviceId == null && tenantId == null) { + VirtualPort port = service.getPort(VirtualPortId.portId(vPortId)); + printPort(port); + } else if (vPortId == null && networkId != null && deviceId == null && tenantId == null) { + Collection<VirtualPort> ports = service.getPorts(TenantNetworkId.networkId(networkId)); + printPorts(ports); + } else if (vPortId == null && networkId == null && deviceId != null && tenantId == null) { + Collection<VirtualPort> ports = service.getPorts(DeviceId.deviceId(deviceId)); + printPorts(ports); + } else if (vPortId == null && networkId == null && deviceId == null && tenantId != null) { + Collection<VirtualPort> ports = service.getPorts(DeviceId.deviceId(tenantId)); + printPorts(ports); + } else if (vPortId == null && networkId == null && deviceId == null && tenantId == null) { + Collection<VirtualPort> ports = service.getPorts(); + printPorts(ports); + } else { + print("cannot input more than one parameter"); + } + + } + + private void printPorts(Collection<VirtualPort> ports) { + for (VirtualPort port : ports) { + printPort(port); + } + } + + private void printPort(VirtualPort port) { + print(FMT, port.portId(), port.networkId(), port.name(), port.tenantId(), port.deviceId(), + port.adminStateUp(), port.state(), port.macAddress(), port.deviceOwner(), port + .fixedIps(), port.bindingHostId(), port.bindingVnicType(), + port.bindingVifType(), port.bindingVifDetails(), port.allowedAddressPairs(), + port.securityGroups()); + } +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortRemoveCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortRemoveCommand.java new file mode 100644 index 00000000..1a3cb4f0 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortRemoveCommand.java @@ -0,0 +1,45 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.cli.virtualport; + +import java.util.Set; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.vtnrsc.VirtualPortId; +import org.onosproject.vtnrsc.virtualport.VirtualPortService; + +import com.google.common.collect.Sets; + +/** + * Supports for removing a virtualPort. + */ +@Command(scope = "onos", name = "virtualport-remove", + description = "Supports for removing a virtualPort.") +public class VirtualPortRemoveCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "id", description = "virtualPort id.", required = true, + multiValued = false) + String id = null; + + @Override + protected void execute() { + VirtualPortService service = get(VirtualPortService.class); + Set<VirtualPortId> virtualPorts = Sets.newHashSet(VirtualPortId.portId(id)); + service.removePorts(virtualPorts); + } +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortUpdateCommand.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortUpdateCommand.java new file mode 100644 index 00000000..6df4b23c --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/VirtualPortUpdateCommand.java @@ -0,0 +1,135 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.cli.virtualport; + +import java.util.Map; +import java.util.Set; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.apache.karaf.shell.commands.Option; +import org.onlab.packet.MacAddress; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.net.DeviceId; +import org.onosproject.vtnrsc.AllowedAddressPair; +import org.onosproject.vtnrsc.BindingHostId; +import org.onosproject.vtnrsc.DefaultVirtualPort; +import org.onosproject.vtnrsc.FixedIp; +import org.onosproject.vtnrsc.SecurityGroup; +import org.onosproject.vtnrsc.TenantId; +import org.onosproject.vtnrsc.TenantNetworkId; +import org.onosproject.vtnrsc.VirtualPort; +import org.onosproject.vtnrsc.VirtualPortId; +import org.onosproject.vtnrsc.virtualport.VirtualPortService; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +/** + * Supports for updating a virtualPort. + */ +@Command(scope = "onos", name = "virtualport-update", + description = "Supports for updating a virtualPort.") +public class VirtualPortUpdateCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "id", description = "virtualPort id.", required = true, + multiValued = false) + String id = null; + + @Argument(index = 1, name = "networkId", description = "network id.", required = true, + multiValued = false) + String networkId = null; + + @Argument(index = 2, name = "name", description = "virtualPort name.", required = true, + multiValued = false) + String name = null; + + @Argument(index = 3, name = "tenantId", description = "tenant id.", required = true, + multiValued = false) + String tenantId = null; + + @Argument(index = 4, name = "deviceId", description = "device id.", required = true, + multiValued = false) + String deviceId = null; + + @Option(name = "-a", aliases = "--adminStateUp", + description = "administrative status of the virtualPort which is true or false.", + required = false, multiValued = false) + Boolean adminStateUp = false; + + @Option(name = "-s", aliases = "--state", description = "virtualPort state.", required = false, + multiValued = false) + String state = null; + + @Option(name = "-m", aliases = "--macAddress", description = "MAC address.", required = false, + multiValued = false) + String macAddress = ""; + + @Option(name = "-d", aliases = "--deviceOwner", + description = "ID of the entity that uses this " + "virtualPort.", required = false, + multiValued = false) + String deviceOwner = null; + + @Option(name = "-f", aliases = "--fixedIp", + description = "The IP address for the port,include the IP address " + + "and subnet identity.", required = false, multiValued = false) + FixedIp fixedIp = null; + + @Option(name = "-i", aliases = "--bindingHostId", description = "virtualPort bindingHostId.", + required = false, multiValued = false) + String bindingHostId = ""; + + @Option(name = "-t", aliases = "--bindingvnicType", + description = "virtualPort bindingvnicType.", required = false, multiValued = false) + String bindingvnicType = null; + + @Option(name = "-v", aliases = "--bindingvifType", description = "virtualPort bindingvifType.", + required = false, multiValued = false) + String bindingvifType = null; + + @Option(name = "-b", aliases = "--bindingvnicDetails", + description = "virtualPort bindingvnicDetails.", required = false, multiValued = false) + String bindingvnicDetails = null; + + @Option(name = "-l", aliases = "--allowedAddress", description = "virtual allowedAddressPair.", + required = false, multiValued = false) + Set<AllowedAddressPair> allowedAddressPairs = Sets.newHashSet(); + + @Option(name = "-e", aliases = "--securityGroups", description = "virtualPort securityGroups.", + required = false, multiValued = false) + Set<SecurityGroup> securityGroups = Sets.newHashSet(); + + @Override + protected void execute() { + VirtualPortService service = get(VirtualPortService.class); + Map<String, String> strMap = Maps.newHashMap(); + strMap.putIfAbsent("name", name); + strMap.putIfAbsent("deviceOwner", deviceOwner); + strMap.putIfAbsent("bindingvnicType", bindingvnicType); + strMap.putIfAbsent("bindingvifType", bindingvifType); + strMap.putIfAbsent("bindingvnicDetails", bindingvnicDetails); + VirtualPort virtualPort = new DefaultVirtualPort(VirtualPortId.portId(id), + TenantNetworkId.networkId(networkId), + false, strMap, VirtualPort.State.ACTIVE, + MacAddress.valueOf(macAddress), + TenantId.tenantId(tenantId), + DeviceId.deviceId(deviceId), Sets.newHashSet(fixedIp), + BindingHostId.bindingHostId(bindingHostId), + allowedAddressPairs, securityGroups); + Set<VirtualPort> virtualPorts = Sets.newHashSet(virtualPort); + service.updatePorts(virtualPorts); + } +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/package-info.java new file mode 100644 index 00000000..fac214a1 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/cli/virtualport/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Command line interface for virtual ports. + */ +package org.onosproject.vtnrsc.cli.virtualport; diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/package-info.java new file mode 100644 index 00000000..b245fb14 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * VTN resources that used by virtual tenant network. + */ +package org.onosproject.vtnrsc; diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/SubnetService.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/SubnetService.java new file mode 100644 index 00000000..82eb9611 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/SubnetService.java @@ -0,0 +1,72 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.subnet; + +import org.onosproject.vtnrsc.Subnet; +import org.onosproject.vtnrsc.SubnetId; + + +/** + * Service for interacting with the inventory of subnets. + */ +public interface SubnetService { + /** + * Returns the subnet with the specified identifier. + * + * @param subnetId subnet identifier + * @return true or false + */ + boolean exists(SubnetId subnetId); + /** + * Returns a collection of the currently known subnets. + * + * @return iterable collection of subnets + */ + Iterable<Subnet> getSubnets(); + + /** + * Returns the subnet with the specified identifier. + * + * @param subnetId subnet identifier + * @return subnet or null if one with the given identifier is not known + */ + Subnet getSubnet(SubnetId subnetId); + /** + * Creates new subnets. + * + * @param subnets the iterable collection of subnets + * @return true if the identifier subnet has been created right + */ + boolean createSubnets(Iterable<Subnet> subnets); + + /** + * Updates existing subnets. + * + * @param subnets the iterable collection of subnets + * @return true if all subnets were updated successfully + */ + boolean updateSubnets(Iterable<Subnet> subnets); + + /** + * Administratively removes the specified subnets from the store. + * + * @param subnetIds the iterable collection of subnets identifier + * @return true if remove identifier subnets successfully + */ + boolean removeSubnets(Iterable<SubnetId> subnetIds); + + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/SubnetManager.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/SubnetManager.java new file mode 100644 index 00000000..890beb29 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/SubnetManager.java @@ -0,0 +1,183 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.subnet.impl; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.Service; +import org.onlab.packet.IpAddress; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.Serializer; +import org.onosproject.store.service.StorageService; +import org.onosproject.vtnrsc.AllocationPool; +import org.onosproject.vtnrsc.DefaultAllocationPool; +import org.onosproject.vtnrsc.DefaultHostRoute; +import org.onosproject.vtnrsc.DefaultSubnet; +import org.onosproject.vtnrsc.HostRoute; +import org.onosproject.vtnrsc.Subnet; +import org.onosproject.vtnrsc.SubnetId; +import org.onosproject.vtnrsc.TenantId; +import org.onosproject.vtnrsc.TenantNetworkId; +import org.onosproject.vtnrsc.subnet.SubnetService; +import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService; +import org.slf4j.Logger; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Provides implementation of the Subnet service. + */ +@Component(immediate = true) +@Service +public class SubnetManager implements SubnetService { + + private static final String SUBNET_ID_NULL = "Subnet ID cannot be null"; + private static final String SUBNET_NOT_NULL = "Subnet cannot be null"; + private static final String SUBNET = "vtn-subnet-store"; + private static final String VTNRSC_APP = "org.onosproject.vtnrsc"; + + + private final Logger log = getLogger(getClass()); + + protected Map<SubnetId, Subnet> subnetStore; + protected ApplicationId appId; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected StorageService storageService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected TenantNetworkService tenantNetworkService; + + @Activate + public void activate() { + + appId = coreService.registerApplication(VTNRSC_APP); + + subnetStore = storageService.<SubnetId, Subnet>consistentMapBuilder() + .withName(SUBNET) + .withApplicationId(appId) + .withPurgeOnUninstall() + .withSerializer(Serializer.using(Arrays.asList(KryoNamespaces.API), + Subnet.class, + SubnetId.class, + TenantNetworkId.class, + TenantId.class, + HostRoute.class, + DefaultHostRoute.class, + Subnet.Mode.class, + AllocationPool.class, + DefaultAllocationPool.class, + DefaultSubnet.class, + IpAddress.Version.class)) + .build().asJavaMap(); + + log.info("Started"); + } + + @Deactivate + public void deactivate() { + log.info("Stopped"); + } + + @Override + public Iterable<Subnet> getSubnets() { + return Collections.unmodifiableCollection(subnetStore.values()); + } + + @Override + public Subnet getSubnet(SubnetId subnetId) { + checkNotNull(subnetId, SUBNET_ID_NULL); + return subnetStore.get(subnetId); + } + + @Override + public boolean exists(SubnetId subnetId) { + checkNotNull(subnetId, SUBNET_ID_NULL); + return subnetStore.containsKey(subnetId); + } + + @Override + public boolean createSubnets(Iterable<Subnet> subnets) { + checkNotNull(subnets, SUBNET_NOT_NULL); + for (Subnet subnet : subnets) { + if (!tenantNetworkService.exists(subnet.networkId())) { + log.debug("The network identifier that the subnet {} belong to is not exist", + subnet.networkId().toString(), subnet.id().toString()); + return false; + } + subnetStore.put(subnet.id(), subnet); + if (!subnetStore.containsKey(subnet.id())) { + log.debug("The identified subnet whose identifier is {} create failed", + subnet.id().toString()); + return false; + } + } + return true; + } + + @Override + public boolean updateSubnets(Iterable<Subnet> subnets) { + checkNotNull(subnets, SUBNET_NOT_NULL); + if (subnets != null) { + for (Subnet subnet : subnets) { + if (!subnetStore.containsKey(subnet.id())) { + log.debug("The subnet is not exist whose identifier is {}", + subnet.id().toString()); + return false; + } + + subnetStore.put(subnet.id(), subnet); + + if (!subnet.equals(subnetStore.get(subnet.id()))) { + log.debug("The subnet is updated failed whose identifier is {}", + subnet.id().toString()); + return false; + } + } + } + return true; + } + + @Override + public boolean removeSubnets(Iterable<SubnetId> subnetIds) { + checkNotNull(subnetIds, SUBNET_ID_NULL); + if (subnetIds != null) { + for (SubnetId subnetId : subnetIds) { + subnetStore.remove(subnetId); + if (subnetStore.containsKey(subnetId)) { + log.debug("The subnet created is failed whose identifier is {}", + subnetId.toString()); + return false; + } + } + } + return true; + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/package-info.java new file mode 100644 index 00000000..79040d8d --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Provides implementation of the Subnet service. + */ +package org.onosproject.vtnrsc.subnet.impl; diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/package-info.java new file mode 100644 index 00000000..7b2bdb90 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/subnet/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Service for interacting with the inventory of subnets. + */ +package org.onosproject.vtnrsc.subnet; diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/TenantNetworkService.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/TenantNetworkService.java new file mode 100644 index 00000000..e246cc4e --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/TenantNetworkService.java @@ -0,0 +1,80 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.tenantnetwork; + +import org.onosproject.vtnrsc.TenantNetwork; +import org.onosproject.vtnrsc.TenantNetworkId; + +/** + * Service for interacting with the inventory of tenantNetwork. + */ +public interface TenantNetworkService { + + /** + * Returns if the tenantNetwork is existed. + * + * @param networkId tenantNetwork identifier + * @return true or false if one with the given identifier exists. + */ + boolean exists(TenantNetworkId networkId); + + /** + * Returns the number of tenantNetwork known to the system. + * + * @return number of tenantNetwork. + */ + int getNetworkCount(); + + /** + * Returns an iterable collection of the currently known tenantNetwork. + * + * @return collection of tenantNetwork. + */ + Iterable<TenantNetwork> getNetworks(); + + /** + * Returns the tenantNetwork with the identifier. + * + * @param networkId TenantNetwork identifier + * @return TenantNetwork or null if one with the given identifier is not + * known. + */ + TenantNetwork getNetwork(TenantNetworkId networkId); + + /** + * Creates tenantNetworks by networks. + * + * @param networks the collection of tenantNetworks + * @return true if all given identifiers created successfully. + */ + boolean createNetworks(Iterable<TenantNetwork> networks); + + /** + * Updates tenantNetworks by tenantNetworks. + * + * @param networks the collection of tenantNetworks + * @return true if all given identifiers updated successfully. + */ + boolean updateNetworks(Iterable<TenantNetwork> networks); + + /** + * Deletes tenantNetwork by tenantNetworkIds. + * + * @param networksIds the collection of tenantNetworkIds + * @return true if the specified tenantNetworks deleted successfully. + */ + boolean removeNetworks(Iterable<TenantNetworkId> networksIds); +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/TenantNetworkManager.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/TenantNetworkManager.java new file mode 100644 index 00000000..0dfc99e2 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/TenantNetworkManager.java @@ -0,0 +1,167 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.tenantnetwork.impl; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.Service; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.Serializer; +import org.onosproject.store.service.StorageService; +import org.onosproject.vtnrsc.DefaultTenantNetwork; +import org.onosproject.vtnrsc.PhysicalNetwork; +import org.onosproject.vtnrsc.SegmentationId; +import org.onosproject.vtnrsc.TenantId; +import org.onosproject.vtnrsc.TenantNetwork; +import org.onosproject.vtnrsc.TenantNetworkId; +import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService; +import org.slf4j.Logger; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Provides implementation of the tenantNetworkService. + */ +@Component(immediate = true) +@Service +public class TenantNetworkManager implements TenantNetworkService { + + private static final String NETWORK_ID_NULL = "Network ID cannot be null"; + private static final String NETWORK_NOT_NULL = "Network ID cannot be null"; + private static final String TENANTNETWORK = "vtn-tenant-network-store"; + private static final String VTNRSC_APP = "org.onosproject.vtnrsc"; + + protected Map<TenantNetworkId, TenantNetwork> networkIdAsKeyStore; + protected ApplicationId appId; + + private final Logger log = getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected StorageService storageService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + + @Activate + public void activate() { + + appId = coreService.registerApplication(VTNRSC_APP); + + networkIdAsKeyStore = storageService.<TenantNetworkId, TenantNetwork>consistentMapBuilder() + .withName(TENANTNETWORK) + .withApplicationId(appId) + .withPurgeOnUninstall() + .withSerializer(Serializer.using(Arrays.asList(KryoNamespaces.API), + TenantNetworkId.class, + DefaultTenantNetwork.class, + TenantNetwork.State.class, + TenantId.class, + TenantNetwork.Type.class, + PhysicalNetwork.class, + SegmentationId.class)) + .build().asJavaMap(); + + log.info("Started"); + } + + @Deactivate + public void deactivate() { + log.info("Stopped"); + } + + @Override + public boolean exists(TenantNetworkId networkId) { + checkNotNull(networkId, NETWORK_ID_NULL); + return networkIdAsKeyStore.containsKey(networkId); + } + + @Override + public int getNetworkCount() { + return networkIdAsKeyStore.size(); + } + + @Override + public Iterable<TenantNetwork> getNetworks() { + return Collections.unmodifiableCollection(networkIdAsKeyStore.values()); + } + + @Override + public TenantNetwork getNetwork(TenantNetworkId networkId) { + checkNotNull(networkId, NETWORK_ID_NULL); + return networkIdAsKeyStore.get(networkId); + } + + @Override + public boolean createNetworks(Iterable<TenantNetwork> networks) { + checkNotNull(networks, NETWORK_NOT_NULL); + for (TenantNetwork network : networks) { + networkIdAsKeyStore.put(network.id(), network); + if (!networkIdAsKeyStore.containsKey(network.id())) { + log.debug("The tenantNetwork is created failed which identifier was {}", network.id() + .toString()); + return false; + } + } + return true; + } + + @Override + public boolean updateNetworks(Iterable<TenantNetwork> networks) { + checkNotNull(networks, NETWORK_NOT_NULL); + for (TenantNetwork network : networks) { + if (!networkIdAsKeyStore.containsKey(network.id())) { + log.debug("The tenantNetwork is not exist whose identifier was {} ", + network.id().toString()); + return false; + } + + networkIdAsKeyStore.put(network.id(), network); + + if (!network.equals(networkIdAsKeyStore.get(network.id()))) { + log.debug("The tenantNetwork is updated failed whose identifier was {} ", + network.id().toString()); + return false; + } + + } + return true; + } + + @Override + public boolean removeNetworks(Iterable<TenantNetworkId> networkIds) { + checkNotNull(networkIds, NETWORK_NOT_NULL); + for (TenantNetworkId networkId : networkIds) { + networkIdAsKeyStore.remove(networkId); + if (networkIdAsKeyStore.containsKey(networkId)) { + log.debug("The tenantNetwork is removed failed whose identifier was {}", + networkId.toString()); + return false; + } + } + return true; + } +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/package-info.java new file mode 100644 index 00000000..f381fda6 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Implementation of service for interacting with the inventory of tenant networks. + */ +package org.onosproject.vtnrsc.tenantnetwork.impl; diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/package-info.java new file mode 100644 index 00000000..1489c973 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tenantnetwork/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Service for interacting with the inventory of tenant networks. + */ +package org.onosproject.vtnrsc.tenantnetwork; diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/TunnelConfigService.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/TunnelConfigService.java new file mode 100644 index 00000000..6f3cf653 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/TunnelConfigService.java @@ -0,0 +1,72 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.tunnel; + +import org.onosproject.vtnrsc.Subnet; +import org.onosproject.vtnrsc.SubnetId; + + +/** + * Service for interacting with the inventory of subnets. + */ +public interface TunnelConfigService { + /** + * Returns the subnet with the specified identifier. + * + * @param subnetId subnet identifier + * @return true or false + */ + boolean exists(SubnetId subnetId); + /** + * Returns a collection of the currently known subnets. + * + * @return iterable collection of subnets + */ + Iterable<Subnet> getSubnets(); + + /** + * Returns the subnet with the specified identifier. + * + * @param subnetId subnet identifier + * @return subnet or null if one with the given identifier is not known + */ + Subnet getSubnet(SubnetId subnetId); + /** + * Creates new subnets. + * + * @param subnets the iterable collection of subnets + * @return true if the identifier subnet has been created right + */ + boolean createSubnets(Iterable<Subnet> subnets); + + /** + * Updates existing subnets. + * + * @param subnets the iterable collection of subnets + * @return true if all subnets were updated successfully + */ + boolean updateSubnets(Iterable<Subnet> subnets); + + /** + * Administratively removes the specified subnets from the store. + * + * @param subnetIds the iterable collection of subnets identifier + * @return true if remove identifier subnets successfully + */ + boolean removeSubnets(Iterable<SubnetId> subnetIds); + + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/package-info.java new file mode 100644 index 00000000..3a84e6e3 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/tunnel/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Service for interacting with the inventory of subnets. + */ +package org.onosproject.vtnrsc.tunnel; diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java new file mode 100644 index 00000000..05ebccf9 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java @@ -0,0 +1,100 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.virtualport; + +import java.util.Collection; + +import org.onosproject.net.DeviceId; +import org.onosproject.vtnrsc.TenantId; +import org.onosproject.vtnrsc.TenantNetworkId; +import org.onosproject.vtnrsc.VirtualPort; +import org.onosproject.vtnrsc.VirtualPortId; + +/** + * Service for interacting with the inventory of virtualPort. + */ +public interface VirtualPortService { + /** + * Returns if the virtualPort is existed. + * + * @param virtualPortId virtualPort identifier + * @return true or false if one with the given identifier is not existed. + */ + boolean exists(VirtualPortId virtualPortId); + + /** + * Returns the virtualPort with the identifier. + * + * @param virtualPortId virtualPort ID + * @return VirtualPort or null if one with the given ID is not know. + */ + VirtualPort getPort(VirtualPortId virtualPortId); + + /** + * Returns the collection of the currently known virtualPort. + * @return collection of VirtualPort. + */ + Collection<VirtualPort> getPorts(); + + /** + * Returns the collection of the virtualPorts associated with the networkId. + * + * @param networkId the network identifer + * @return collection of virtualPort. + */ + Collection<VirtualPort> getPorts(TenantNetworkId networkId); + + /** + * Returns the collection of the virtualPorts associated with the tenantId. + * + * @param tenantId the tenant identifier + * @return collection of virtualPorts. + */ + Collection<VirtualPort> getPorts(TenantId tenantId); + + /** + * Returns the collection of the virtualPorts associated with the deviceId. + * + * @param deviceId the device identifier + * @return collection of virtualPort. + */ + Collection<VirtualPort> getPorts(DeviceId deviceId); + + /** + * Creates virtualPorts by virtualPorts. + * + * @param virtualPorts the iterable collection of virtualPorts + * @return true if all given identifiers created successfully. + */ + boolean createPorts(Iterable<VirtualPort> virtualPorts); + + /** + * Updates virtualPorts by virtualPorts. + * + * @param virtualPorts the iterable collection of virtualPorts + * @return true if all given identifiers updated successfully. + */ + boolean updatePorts(Iterable<VirtualPort> virtualPorts); + + /** + * Deletes virtualPortIds by virtualPortIds. + * + * @param virtualPortIds the iterable collection of virtualPort identifiers + * @return true or false if one with the given identifier to delete is + * successfully. + */ + boolean removePorts(Iterable<VirtualPortId> virtualPortIds); +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java new file mode 100644 index 00000000..926809c9 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java @@ -0,0 +1,208 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.virtualport.impl; + +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.Service; +import org.onlab.packet.IpAddress; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.net.DeviceId; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.Serializer; +import org.onosproject.store.service.StorageService; +import org.onosproject.vtnrsc.AllowedAddressPair; +import org.onosproject.vtnrsc.BindingHostId; +import org.onosproject.vtnrsc.DefaultVirtualPort; +import org.onosproject.vtnrsc.FixedIp; +import org.onosproject.vtnrsc.SecurityGroup; +import org.onosproject.vtnrsc.SubnetId; +import org.onosproject.vtnrsc.TenantId; +import org.onosproject.vtnrsc.TenantNetworkId; +import org.onosproject.vtnrsc.VirtualPort; +import org.onosproject.vtnrsc.VirtualPortId; +import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService; +import org.onosproject.vtnrsc.virtualport.VirtualPortService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides implementation of the VirtualPort APIs. + */ +@Component(immediate = true) +@Service +public class VirtualPortManager implements VirtualPortService { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private static final String VIRTUALPORT = "vtn-virtual-port"; + private static final String VTNRSC_APP = "org.onosproject.vtnrsc"; + + private static final String VIRTUALPORT_ID_NULL = "VirtualPort ID cannot be null"; + private static final String VIRTUALPORT_NOT_NULL = "VirtualPort cannot be null"; + private static final String TENANTID_NOT_NULL = "TenantId cannot be null"; + private static final String NETWORKID_NOT_NULL = "NetworkId cannot be null"; + private static final String DEVICEID_NOT_NULL = "DeviceId cannot be null"; + + protected Map<VirtualPortId, VirtualPort> vPortStore; + protected ApplicationId appId; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected StorageService storageService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected TenantNetworkService networkService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + @Activate + public void activate() { + + appId = coreService.registerApplication(VTNRSC_APP); + + vPortStore = storageService.<VirtualPortId, VirtualPort>consistentMapBuilder() + .withName(VIRTUALPORT) + .withApplicationId(appId) + .withPurgeOnUninstall() + .withSerializer(Serializer.using(Arrays.asList(KryoNamespaces.API), + VirtualPortId.class, + TenantNetworkId.class, + VirtualPort.State.class, + TenantId.class, + AllowedAddressPair.class, + FixedIp.class, + BindingHostId.class, + SecurityGroup.class, + SubnetId.class, + IpAddress.class, + DefaultVirtualPort.class)) + .build().asJavaMap(); + log.info("Started"); + } + + @Deactivate + public void deactivate() { + vPortStore.clear(); + log.info("Stoppped"); + } + + @Override + public boolean exists(VirtualPortId vPortId) { + checkNotNull(vPortId, VIRTUALPORT_ID_NULL); + return vPortStore.containsKey(vPortId); + } + + @Override + public VirtualPort getPort(VirtualPortId vPortId) { + checkNotNull(vPortId, VIRTUALPORT_ID_NULL); + return vPortStore.get(vPortId); + } + + @Override + public Collection<VirtualPort> getPorts() { + return Collections.unmodifiableCollection(vPortStore.values()); + } + + @Override + public Collection<VirtualPort> getPorts(TenantNetworkId networkId) { + checkNotNull(networkId, NETWORKID_NOT_NULL); + return vPortStore.values().stream().filter(d -> d.networkId().equals(networkId)) + .collect(Collectors.toList()); + } + + @Override + public Collection<VirtualPort> getPorts(TenantId tenantId) { + checkNotNull(tenantId, TENANTID_NOT_NULL); + return vPortStore.values().stream().filter(d -> d.tenantId().equals(tenantId)) + .collect(Collectors.toList()); + } + + @Override + public Collection<VirtualPort> getPorts(DeviceId deviceId) { + checkNotNull(deviceId, DEVICEID_NOT_NULL); + return vPortStore.values().stream().filter(d -> d.deviceId().equals(deviceId)) + .collect(Collectors.toList()); + } + + @Override + public boolean createPorts(Iterable<VirtualPort> vPorts) { + checkNotNull(vPorts, VIRTUALPORT_NOT_NULL); + for (VirtualPort vPort : vPorts) { + log.debug("vPortId is {} ", vPort.portId().toString()); + vPortStore.put(vPort.portId(), vPort); + if (!vPortStore.containsKey(vPort.portId())) { + log.debug("The virtualPort is created failed whose identifier is {} ", + vPort.portId().toString()); + return false; + } + } + return true; + } + + @Override + public boolean updatePorts(Iterable<VirtualPort> vPorts) { + checkNotNull(vPorts, VIRTUALPORT_NOT_NULL); + if (vPorts != null) { + for (VirtualPort vPort : vPorts) { + vPortStore.put(vPort.portId(), vPort); + if (!vPortStore.containsKey(vPort.portId())) { + log.debug("The virtualPort is not exist whose identifier is {}", + vPort.portId().toString()); + return false; + } + + vPortStore.put(vPort.portId(), vPort); + + if (!vPort.equals(vPortStore.get(vPort.portId()))) { + log.debug("The virtualPort is updated failed whose identifier is {}", + vPort.portId().toString()); + return false; + } + } + } + return true; + } + + @Override + public boolean removePorts(Iterable<VirtualPortId> vPortIds) { + checkNotNull(vPortIds, VIRTUALPORT_ID_NULL); + if (vPortIds != null) { + for (VirtualPortId vPortId : vPortIds) { + vPortStore.remove(vPortId); + if (vPortStore.containsKey(vPortId)) { + log.debug("The virtualPort is removed failed whose identifier is {}", + vPortId.toString()); + return false; + } + } + } + return true; + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/package-info.java new file mode 100644 index 00000000..24eb0d3f --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Implementation of service for interacting with the inventory of virtual ports. + */ +package org.onosproject.vtnrsc.virtualport.impl; diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/package-info.java new file mode 100644 index 00000000..06a01a04 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Service for interacting with the inventory of virtual ports. + */ +package org.onosproject.vtnrsc.virtualport; diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllocationPoolsCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllocationPoolsCodec.java new file mode 100644 index 00000000..57c97c1c --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllocationPoolsCodec.java @@ -0,0 +1,40 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.web; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.onosproject.codec.CodecContext; +import org.onosproject.codec.JsonCodec; +import org.onosproject.vtnrsc.AllocationPool; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Subnet AllocationPool codec. + */ +public final class AllocationPoolsCodec extends JsonCodec<AllocationPool> { + + @Override + public ObjectNode encode(AllocationPool alocPool, CodecContext context) { + checkNotNull(alocPool, "AllocationPools cannot be null"); + ObjectNode result = context.mapper().createObjectNode() + .put("start", alocPool.startIp().toString()) + .put("end", alocPool.endIp().toString()); + return result; + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllowedAddressPairCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllowedAddressPairCodec.java new file mode 100644 index 00000000..7960808f --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/AllowedAddressPairCodec.java @@ -0,0 +1,40 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.web; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.onosproject.codec.CodecContext; +import org.onosproject.codec.JsonCodec; +import org.onosproject.vtnrsc.AllowedAddressPair; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * VirtualPort AllowedAddressPair codec. + */ +public final class AllowedAddressPairCodec extends JsonCodec<AllowedAddressPair> { + + @Override + public ObjectNode encode(AllowedAddressPair alocAddPair, CodecContext context) { + checkNotNull(alocAddPair, "AllowedAddressPair cannot be null"); + ObjectNode result = context.mapper().createObjectNode() + .put("ip_address", alocAddPair.ip().toString()) + .put("mac_address", alocAddPair.mac().toString()); + return result; + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/FixedIpCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/FixedIpCodec.java new file mode 100644 index 00000000..96c9bb4e --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/FixedIpCodec.java @@ -0,0 +1,40 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.web; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.onosproject.codec.CodecContext; +import org.onosproject.codec.JsonCodec; +import org.onosproject.vtnrsc.FixedIp; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * VirtualPort FixedIp codec. + */ +public final class FixedIpCodec extends JsonCodec<FixedIp> { + + @Override + public ObjectNode encode(FixedIp fixIp, CodecContext context) { + checkNotNull(fixIp, "FixedIp cannot be null"); + ObjectNode result = context.mapper().createObjectNode() + .put("subnet_id", fixIp.subnetId().toString()) + .put("ip_address", fixIp.ip().toString()); + return result; + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/HostRoutesCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/HostRoutesCodec.java new file mode 100644 index 00000000..69ca6b3f --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/HostRoutesCodec.java @@ -0,0 +1,40 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.web; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.onosproject.codec.CodecContext; +import org.onosproject.codec.JsonCodec; +import org.onosproject.vtnrsc.HostRoute; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Subnet HostRoute codec. + */ +public final class HostRoutesCodec extends JsonCodec<HostRoute> { + + @Override + public ObjectNode encode(HostRoute hostRoute, CodecContext context) { + checkNotNull(hostRoute, "HostRoute cannot be null"); + ObjectNode result = context.mapper().createObjectNode() + .put("nexthop", hostRoute.nexthop().toString()) + .put("destination", hostRoute.destination().toString()); + return result; + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SecurityGroupCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SecurityGroupCodec.java new file mode 100644 index 00000000..c2ded196 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SecurityGroupCodec.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.web; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.onosproject.codec.CodecContext; +import org.onosproject.codec.JsonCodec; +import org.onosproject.vtnrsc.SecurityGroup; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Virtualport SecurityGroup codec. + */ +public final class SecurityGroupCodec extends JsonCodec<SecurityGroup> { + + @Override + public ObjectNode encode(SecurityGroup securGroup, CodecContext context) { + checkNotNull(securGroup, "SecurityGroup cannot be null"); + ObjectNode result = context.mapper().createObjectNode() + .put("security_group", securGroup.securityGroup()); + return result; + } + +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SubnetCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SubnetCodec.java new file mode 100644 index 00000000..122b75a9 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/SubnetCodec.java @@ -0,0 +1,53 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.web; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.onosproject.codec.CodecContext; +import org.onosproject.codec.JsonCodec; +import org.onosproject.vtnrsc.Subnet; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Subnet JSON codec. + */ +public final class SubnetCodec extends JsonCodec<Subnet> { + @Override + public ObjectNode encode(Subnet subnet, CodecContext context) { + checkNotNull(subnet, "Subnet cannot be null"); + ObjectNode result = context.mapper().createObjectNode() + .put("id", subnet.id().toString()) + .put("gateway_ip", subnet.gatewayIp().toString()) + .put("network_id", subnet.networkId().toString()) + .put("name", subnet.subnetName()) + .put("ip_version", subnet.ipVersion().toString()) + .put("cidr", subnet.cidr().toString()) + .put("shared", subnet.shared()) + .put("enabled_dchp", subnet.dhcpEnabled()) + .put("tenant_id", subnet.tenantId().toString()) + .put("ipv6_address_mode", subnet.ipV6AddressMode() == null ? null + : subnet.ipV6AddressMode().toString()) + .put("ipv6_ra_mode", subnet.ipV6RaMode() == null ? null + : subnet.ipV6RaMode().toString()); + result.set("allocation_pools", new AllocationPoolsCodec().encode(subnet + .allocationPools(), context)); + result.set("host_routes", + new HostRoutesCodec().encode(subnet.hostRoutes(), context)); + return result; + } +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/TenantNetworkCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/TenantNetworkCodec.java new file mode 100644 index 00000000..48ba3b97 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/TenantNetworkCodec.java @@ -0,0 +1,47 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.web; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.onosproject.codec.CodecContext; +import org.onosproject.codec.JsonCodec; +import org.onosproject.vtnrsc.TenantNetwork; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * TenantNetwork JSON codec. + */ +public final class TenantNetworkCodec extends JsonCodec<TenantNetwork> { + + @Override + public ObjectNode encode(TenantNetwork network, CodecContext context) { + checkNotNull(network, "Network cannot be null"); + ObjectNode result = context.mapper().createObjectNode() + .put("id", network.id().toString()) + .put("name", network.name()) + .put("admin_state_up", network.adminStateUp()) + .put("status", "" + network.state()) + .put("shared", network.shared()) + .put("tenant_id", network.tenantId().toString()) + .put("router:external", network.routerExternal()) + .put("provider:network_type", "" + network.type()) + .put("provider:physical_network", network.physicalNetwork().toString()) + .put("provider:segmentation_id", network.segmentationId().toString()); + return result; + } +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/VirtualPortCodec.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/VirtualPortCodec.java new file mode 100644 index 00000000..e57d56bc --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/VirtualPortCodec.java @@ -0,0 +1,57 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnrsc.web; + +import static com.google.common.base.Preconditions.checkNotNull; + +import org.onosproject.codec.CodecContext; +import org.onosproject.codec.JsonCodec; +import org.onosproject.vtnrsc.VirtualPort; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * VirtualPort JSON codec. + */ +public final class VirtualPortCodec extends JsonCodec<VirtualPort> { + @Override + public ObjectNode encode(VirtualPort vPort, CodecContext context) { + checkNotNull(vPort, "VPort cannot be null"); + ObjectNode result = context + .mapper() + .createObjectNode() + .put("id", vPort.portId().toString()) + .put("network_id", vPort.networkId().toString()) + .put("admin_state_up", vPort.adminStateUp()) + .put("name", vPort.name()) + .put("status", vPort.state().toString()) + .put("mac_address", vPort.macAddress().toString()) + .put("tenant_id", vPort.tenantId().toString()) + .put("device_id", vPort.deviceId().toString()) + .put("device_owner", vPort.deviceOwner()) + .put("binding:vnic_type", vPort.bindingVnicType()) + .put("binding:Vif_type", vPort.bindingVifType()) + .put("binding:host_id", vPort.bindingHostId().toString()) + .put("binding:vif_details", vPort.bindingVifDetails()); + result.set("allowed_address_pairs", new AllowedAddressPairCodec().encode( + vPort.allowedAddressPairs(), context)); + result.set("fixed_ips", new FixedIpCodec().encode( + vPort.fixedIps(), context)); + result.set("security_groups", new SecurityGroupCodec().encode( + vPort.securityGroups(), context)); + return result; + } +} diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/package-info.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/package-info.java new file mode 100644 index 00000000..34636a9f --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/web/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Codecs for virtual tenant objects. + */ +package org.onosproject.vtnrsc.web; diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/vtn/vtnrsc/src/main/resources/OSGI-INF/blueprint/shell-config.xml new file mode 100644 index 00000000..c6a9c81b --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -0,0 +1,56 @@ +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + + <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0"> + <command> + <action class="org.onosproject.vtnrsc.cli.network.TenantNetworkCreateCommand"/> + </command> + <command> + <action class="org.onosproject.vtnrsc.cli.network.TenantNetworkQueryCommand"/> + </command> + <command> + <action class="org.onosproject.vtnrsc.cli.network.TenantNetworkRemoveCommand"/> + </command> + <command> + <action class="org.onosproject.vtnrsc.cli.network.TenantNetworkUpdateCommand"/> + </command> + <command> + <action class="org.onosproject.vtnrsc.cli.subnet.SubnetCreateCommand"/> + </command> + <command> + <action class="org.onosproject.vtnrsc.cli.subnet.SubnetQueryCommand"/> + </command> + <command> + <action class="org.onosproject.vtnrsc.cli.subnet.SubnetRemoveCommand"/> + </command> + <command> + <action class="org.onosproject.vtnrsc.cli.subnet.SubnetUpdateCommand"/> + </command> + <command> + <action class="org.onosproject.vtnrsc.cli.virtualport.VirtualPortCreateCommand"/> + </command> + <command> + <action class="org.onosproject.vtnrsc.cli.virtualport.VirtualPortQueryCommand"/> + </command> + <command> + <action class="org.onosproject.vtnrsc.cli.virtualport.VirtualPortRemoveCommand"/> + </command> + <command> + <action class="org.onosproject.vtnrsc.cli.virtualport.VirtualPortUpdateCommand"/> + </command> + </command-bundle> +</blueprint> diff --git a/framework/src/onos/apps/vtn/vtnweb/pom.xml b/framework/src/onos/apps/vtn/vtnweb/pom.xml new file mode 100644 index 00000000..bcb71d9f --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnweb/pom.xml @@ -0,0 +1,87 @@ +<?xml version="1.0"?> +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-app-vtn</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + + <artifactId>onos-app-vtn-web</artifactId> + <packaging>bundle</packaging> + <properties> + <web.context>/onos/vtn</web.context> + </properties> + + <dependencies> + <dependency> + <groupId>javax.ws.rs</groupId> + <artifactId>jsr311-api</artifactId> + <version>1.1.1</version> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-app-vtn-rsc</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <_wab>src/main/webapp/</_wab> + <Bundle-SymbolicName> + ${project.groupId}.${project.artifactId} + </Bundle-SymbolicName> + <Import-Package> + org.slf4j, + org.osgi.framework, + javax.ws.rs, + javax.ws.rs.core, + com.sun.jersey.api.core, + com.sun.jersey.spi.container.servlet, + com.sun.jersey.server.impl.container.servlet, + com.fasterxml.jackson.databind, + com.fasterxml.jackson.databind.node, + com.fasterxml.jackson.core, + org.apache.karaf.shell.commands, + org.apache.commons.lang.math.*, + com.google.common.*, + org.onlab.packet.*, + org.onlab.rest.*, + org.onosproject.*, + org.onlab.util.*, + org.jboss.netty.util.* + </Import-Package> + <Web-ContextPath>${web.context}</Web-ContextPath> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + +</project>
\ No newline at end of file diff --git a/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/SubnetWebResource.java b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/SubnetWebResource.java new file mode 100644 index 00000000..deb9ca37 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/SubnetWebResource.java @@ -0,0 +1,379 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnweb.resources; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; +import static javax.ws.rs.core.Response.Status.NOT_FOUND; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +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.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpAddress.Version; +import org.onlab.packet.IpPrefix; +import org.onlab.util.ItemNotFoundException; +import org.onosproject.rest.AbstractWebResource; +import org.onosproject.vtnrsc.AllocationPool; +import org.onosproject.vtnrsc.DefaultAllocationPool; +import org.onosproject.vtnrsc.DefaultHostRoute; +import org.onosproject.vtnrsc.DefaultSubnet; +import org.onosproject.vtnrsc.HostRoute; +import org.onosproject.vtnrsc.Subnet; +import org.onosproject.vtnrsc.SubnetId; +import org.onosproject.vtnrsc.TenantId; +import org.onosproject.vtnrsc.TenantNetworkId; +import org.onosproject.vtnrsc.Subnet.Mode; +import org.onosproject.vtnrsc.subnet.SubnetService; +import org.onosproject.vtnrsc.web.SubnetCodec; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +@Path("subnets") +public class SubnetWebResource extends AbstractWebResource { + private final Logger log = LoggerFactory.getLogger(SubnetWebResource.class); + public static final String SUBNET_NOT_CREATE = "Subnets is failed to create!"; + public static final String SUBNET_NOT_FOUND = "Subnets is not found"; + public static final String JSON_NOT_NULL = "JsonNode can not be null"; + + @GET + @Produces(MediaType.APPLICATION_JSON) + public Response listSubnets() { + Iterable<Subnet> subnets = get(SubnetService.class).getSubnets(); + ObjectNode result = new ObjectMapper().createObjectNode(); + result.set("subnets", new SubnetCodec().encode(subnets, this)); + return ok(result.toString()).build(); + } + + @GET + @Path("{subnetUUID}") + @Produces(MediaType.APPLICATION_JSON) + public Response getSubnet(@PathParam("subnetUUID") String id) { + + if (!get(SubnetService.class).exists(SubnetId.subnetId(id))) { + return Response.status(NOT_FOUND) + .entity(SUBNET_NOT_FOUND).build(); + } + Subnet sub = nullIsNotFound(get(SubnetService.class) + .getSubnet(SubnetId.subnetId(id)), + SUBNET_NOT_FOUND); + + ObjectNode result = new ObjectMapper().createObjectNode(); + result.set("subnet", new SubnetCodec().encode(sub, this)); + return ok(result.toString()).build(); + } + + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + public Response createSubnet(final InputStream input) { + + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode subnode = mapper.readTree(input); + Iterable<Subnet> subnets = createOrUpdateByInputStream(subnode); + Boolean result = nullIsNotFound((get(SubnetService.class) + .createSubnets(subnets)), + SUBNET_NOT_CREATE); + + if (!result) { + return Response.status(INTERNAL_SERVER_ERROR) + .entity(SUBNET_NOT_CREATE).build(); + } + return Response.status(202).entity(result.toString()).build(); + } catch (Exception e) { + return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString()) + .build(); + } + } + + @PUT + @Path("{subnetUUID}") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + public Response updateSubnet(@PathParam("id") String id, + final InputStream input) { + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode subnode = mapper.readTree(input); + Iterable<Subnet> subnets = createOrUpdateByInputStream(subnode); + Boolean result = nullIsNotFound(get(SubnetService.class) + .updateSubnets(subnets), SUBNET_NOT_FOUND); + if (!result) { + return Response.status(INTERNAL_SERVER_ERROR) + .entity(SUBNET_NOT_FOUND).build(); + } + return Response.status(203).entity(result.toString()).build(); + } catch (Exception e) { + return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString()) + .build(); + } + } + + @Path("{subnetUUID}") + @DELETE + public Response deleteSingleSubnet(@PathParam("subnetUUID") String id) + throws IOException { + try { + SubnetId subId = SubnetId.subnetId(id); + Set<SubnetId> subIds = new HashSet<>(); + subIds.add(subId); + get(SubnetService.class).removeSubnets(subIds); + return Response.status(201).entity("SUCCESS").build(); + } catch (Exception e) { + return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString()) + .build(); + } + } + + private Iterable<Subnet> createOrUpdateByInputStream(JsonNode subnode) { + checkNotNull(subnode, JSON_NOT_NULL); + Iterable<Subnet> subnets = null; + JsonNode subnetNodes = subnode.get("subnets"); + if (subnetNodes == null) { + subnetNodes = subnode.get("subnet"); + } + log.debug("subnetNodes is {}", subnetNodes.toString()); + if (subnetNodes.isArray()) { + subnets = changeJsonToSubs(subnetNodes); + } else { + subnets = changeJsonToSub(subnetNodes); + } + return subnets; + } + + /** + * Returns a collection of subnets from subnetNodes. + * + * @param subnetNodes the subnet json node + * @return subnets a collection of subnets + */ + public Iterable<Subnet> changeJsonToSubs(JsonNode subnetNodes) { + checkNotNull(subnetNodes, JSON_NOT_NULL); + Map<SubnetId, Subnet> subMap = new HashMap<>(); + for (JsonNode subnetNode : subnetNodes) { + if (!subnetNode.hasNonNull("id")) { + return null; + } + SubnetId id = SubnetId.subnetId(subnetNode.get("id").asText()); + String subnetName = subnetNode.get("name").asText(); + TenantId tenantId = TenantId + .tenantId(subnetNode.get("tenant_id").asText()); + TenantNetworkId networkId = TenantNetworkId + .networkId(subnetNode.get("network_id").asText()); + String version = subnetNode.get("ip_version").asText(); + Version ipVersion; + switch (version) { + case "4": + ipVersion = Version.INET; + break; + case "6": + ipVersion = Version.INET; + break; + default: + throw new IllegalArgumentException("ipVersion should be 4 or 6."); + } + IpPrefix cidr = IpPrefix.valueOf(subnetNode.get("cidr").asText()); + IpAddress gatewayIp = IpAddress + .valueOf(subnetNode.get("gateway_ip").asText()); + Boolean dhcpEnabled = subnetNode.get("enable_dhcp").asBoolean(); + Boolean shared = subnetNode.get("shared").asBoolean(); + JsonNode hostRoutes = subnetNode.get("host_routes"); + Iterable<HostRoute> hostRoutesIt = jsonNodeToHostRoutes(hostRoutes); + JsonNode allocationPools = subnetNode.get("allocation_pools"); + Iterable<AllocationPool> allocationPoolsIt = jsonNodeToAllocationPools(allocationPools); + Mode ipV6AddressMode = Mode + .valueOf(subnetNode.get("ipv6_address_mode").asText()); + Mode ipV6RaMode = Mode + .valueOf(subnetNode.get("ipv6_ra_mode").asText()); + Subnet subnet = new DefaultSubnet(id, subnetName, networkId, + tenantId, ipVersion, cidr, + gatewayIp, dhcpEnabled, shared, + Sets.newHashSet(hostRoutesIt), ipV6AddressMode, + ipV6RaMode, Sets.newHashSet(allocationPoolsIt)); + subMap.put(id, subnet); + } + return Collections.unmodifiableCollection(subMap.values()); + } + + /** + * Returns a collection of subnets from subnetNodes. + * + * @param subnetNodes the subnet json node + * @return subnets a collection of subnets + */ + public Iterable<Subnet> changeJsonToSub(JsonNode subnetNodes) { + checkNotNull(subnetNodes, JSON_NOT_NULL); + checkArgument(subnetNodes.get("enable_dhcp").isBoolean(), "enable_dhcp should be boolean"); + checkArgument(subnetNodes.get("shared").isBoolean(), "shared should be boolean"); + Map<SubnetId, Subnet> subMap = new HashMap<>(); + if (!subnetNodes.hasNonNull("id")) { + return null; + } + SubnetId id = SubnetId.subnetId(subnetNodes.get("id").asText()); + String subnetName = subnetNodes.get("name").asText(); + TenantId tenantId = TenantId + .tenantId(subnetNodes.get("tenant_id").asText()); + TenantNetworkId networkId = TenantNetworkId + .networkId(subnetNodes.get("network_id").asText()); + String version = subnetNodes.get("ip_version").asText(); + Version ipVersion; + switch (version) { + case "4": + ipVersion = Version.INET; + break; + case "6": + ipVersion = Version.INET; + break; + default: + throw new IllegalArgumentException("ipVersion should be 4 or 6."); + } + + IpPrefix cidr = IpPrefix.valueOf(subnetNodes.get("cidr").asText()); + IpAddress gatewayIp = IpAddress + .valueOf(subnetNodes.get("gateway_ip").asText()); + Boolean dhcpEnabled = subnetNodes.get("enable_dhcp").asBoolean(); + Boolean shared = subnetNodes.get("shared").asBoolean(); + JsonNode hostRoutes = subnetNodes.get("host_routes"); + Iterable<HostRoute> hostRoutesIt = jsonNodeToHostRoutes(hostRoutes); + JsonNode allocationPools = subnetNodes.get("allocation_pools"); + Iterable<AllocationPool> allocationPoolsIt = jsonNodeToAllocationPools(allocationPools); + + Mode ipV6AddressMode = getMode(subnetNodes.get("ipv6_address_mode") + .asText()); + Mode ipV6RaMode = getMode(subnetNodes.get("ipv6_ra_mode").asText()); + + Subnet subnet = new DefaultSubnet(id, subnetName, networkId, tenantId, + ipVersion, cidr, gatewayIp, + dhcpEnabled, shared, Sets.newHashSet(hostRoutesIt), + ipV6AddressMode, ipV6RaMode, + Sets.newHashSet(allocationPoolsIt)); + subMap.put(id, subnet); + return Collections.unmodifiableCollection(subMap.values()); + } + + /** + * Gets ipv6_address_mode or ipv6_ra_mode type. + * + * @param mode the String value in JsonNode + * @return ipV6Mode Mode of the ipV6Mode + */ + private Mode getMode(String mode) { + Mode ipV6Mode; + if (mode == null) { + return null; + } + switch (mode) { + case "dhcpv6-stateful": + ipV6Mode = Mode.DHCPV6_STATEFUL; + break; + case "dhcpv6-stateless": + ipV6Mode = Mode.DHCPV6_STATELESS; + break; + case "slaac": + ipV6Mode = Mode.SLAAC; + break; + default: + ipV6Mode = null; + } + return ipV6Mode; + } + + /** + * Changes JsonNode alocPools to a collection of the alocPools. + * + * @param allocationPools the allocationPools JsonNode + * @return a collection of allocationPools + */ + public Iterable<AllocationPool> jsonNodeToAllocationPools(JsonNode allocationPools) { + checkNotNull(allocationPools, JSON_NOT_NULL); + ConcurrentMap<Integer, AllocationPool> alocplMaps = Maps + .newConcurrentMap(); + Integer i = 0; + for (JsonNode node : allocationPools) { + IpAddress startIp = IpAddress.valueOf(node.get("start").asText()); + IpAddress endIp = IpAddress.valueOf(node.get("end").asText()); + AllocationPool alocPls = new DefaultAllocationPool(startIp, endIp); + alocplMaps.putIfAbsent(i, alocPls); + i++; + } + return Collections.unmodifiableCollection(alocplMaps.values()); + } + + /** + * Changes hostRoutes JsonNode to a collection of the hostRoutes. + * + * @param hostRoutes the hostRoutes json node + * @return a collection of hostRoutes + */ + public Iterable<HostRoute> jsonNodeToHostRoutes(JsonNode hostRoutes) { + checkNotNull(hostRoutes, JSON_NOT_NULL); + ConcurrentMap<Integer, HostRoute> hostRouteMaps = Maps + .newConcurrentMap(); + Integer i = 0; + for (JsonNode node : hostRoutes) { + IpAddress nexthop = IpAddress.valueOf(node.get("nexthop").asText()); + IpPrefix destination = IpPrefix.valueOf(node.get("destination") + .asText()); + HostRoute hostRoute = new DefaultHostRoute(nexthop, destination); + hostRouteMaps.putIfAbsent(i, hostRoute); + i++; + } + return Collections.unmodifiableCollection(hostRouteMaps.values()); + } + + /** + * Returns the specified item if that items is null; otherwise throws not + * found exception. + * + * @param item item to check + * @param <T> item type + * @param message not found message + * @return item if not null + * @throws org.onlab.util.ItemNotFoundException if item is null + */ + protected <T> T nullIsNotFound(T item, String message) { + if (item == null) { + throw new ItemNotFoundException(message); + } + return item; + } + +} diff --git a/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/TenantNetworkWebResource.java b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/TenantNetworkWebResource.java new file mode 100644 index 00000000..0b877822 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/TenantNetworkWebResource.java @@ -0,0 +1,373 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnweb.resources; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkArgument; +import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; +import static javax.ws.rs.core.Response.Status.OK; +import static javax.ws.rs.core.Response.Status.NOT_FOUND; + +import java.io.InputStream; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +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.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.onlab.util.ItemNotFoundException; +import org.onosproject.rest.AbstractWebResource; +import org.onosproject.vtnrsc.DefaultTenantNetwork; +import org.onosproject.vtnrsc.PhysicalNetwork; +import org.onosproject.vtnrsc.SegmentationId; +import org.onosproject.vtnrsc.TenantId; +import org.onosproject.vtnrsc.TenantNetwork; +import org.onosproject.vtnrsc.TenantNetworkId; +import org.onosproject.vtnrsc.TenantNetwork.State; +import org.onosproject.vtnrsc.TenantNetwork.Type; +import org.onosproject.vtnrsc.tenantnetwork.TenantNetworkService; +import org.onosproject.vtnrsc.web.TenantNetworkCodec; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Maps; + +/** + * REST resource for interacting with the inventory of networks. + */ +@Path("networks") +public class TenantNetworkWebResource extends AbstractWebResource { + public static final String NETWORK_NOT_FOUND = "Network is not found"; + public static final String NETWORK_ID_EXIST = "Network id is existed"; + public static final String NETWORK_ID_NOT_EXIST = "Network id is not existed"; + public static final String CREATE_NETWORK = "create network"; + public static final String UPDATE_NETWORK = "update network"; + public static final String DELETE_NETWORK = "delete network"; + public static final String JSON_NOT_NULL = "JsonNode can not be null"; + + protected static final Logger log = LoggerFactory + .getLogger(TenantNetworkWebResource.class); + private final ConcurrentMap<TenantNetworkId, TenantNetwork> networksMap = Maps + .newConcurrentMap(); + + @GET + @Produces({ MediaType.APPLICATION_JSON }) + public Response getNetworks(@QueryParam("id") String queryId, + @QueryParam("name") String queryName, + @QueryParam("admin_state_up") String queryadminStateUp, + @QueryParam("status") String querystate, + @QueryParam("shared") String queryshared, + @QueryParam("tenant_id") String querytenantId, + @QueryParam("router:external") String routerExternal, + @QueryParam("provider:network_type") String type, + @QueryParam("provider:physical_network") String physicalNetwork, + @QueryParam("provider:segmentation_id") String segmentationId) { + Iterable<TenantNetwork> networks = get(TenantNetworkService.class) + .getNetworks(); + Iterator<TenantNetwork> networkors = networks.iterator(); + while (networkors.hasNext()) { + TenantNetwork network = networkors.next(); + if ((queryId == null || queryId.equals(network.id().toString())) + && (queryName == null || queryName.equals(network.name())) + && (queryadminStateUp == null || queryadminStateUp + .equals(network.adminStateUp())) + && (querystate == null || querystate.equals(network.state() + .toString())) + && (queryshared == null || queryshared.equals(network + .shared())) + && (querytenantId == null || querytenantId.equals(network + .tenantId().toString())) + && (routerExternal == null || routerExternal.equals(network + .routerExternal())) + && (type == null || type.equals(network.type())) + && (physicalNetwork == null || physicalNetwork + .equals(network.physicalNetwork())) + && (segmentationId == null || segmentationId.equals(network + .segmentationId()))) { + networksMap.putIfAbsent(network.id(), network); + } + } + networks = Collections.unmodifiableCollection(networksMap.values()); + ObjectNode result = new ObjectMapper().createObjectNode(); + result.set("networks", new TenantNetworkCodec().encode(networks, this)); + + return ok(result.toString()).build(); + } + + private State isState(String state) { + if (state.equals("ACTIVE")) { + return TenantNetwork.State.ACTIVE; + } else if (state.equals("BUILD")) { + return TenantNetwork.State.BUILD; + } else if (state.equals("DOWN")) { + return TenantNetwork.State.DOWN; + } else if (state.equals("ERROR")) { + return TenantNetwork.State.ERROR; + } else { + return null; + } + } + + private Type isType(String type) { + if (type.equals("LOCAL")) { + return TenantNetwork.Type.LOCAL; + } else { + return null; + } + } + + @GET + @Path("{id}") + @Produces({ MediaType.APPLICATION_JSON }) + public Response getNetwork(@PathParam("id") String id) { + + if (!get(TenantNetworkService.class).exists(TenantNetworkId + .networkId(id))) { + return Response.status(NOT_FOUND) + .entity(NETWORK_NOT_FOUND).build(); + } + TenantNetwork network = nullIsNotFound(get(TenantNetworkService.class) + .getNetwork(TenantNetworkId.networkId(id)), NETWORK_NOT_FOUND); + ObjectNode result = new ObjectMapper().createObjectNode(); + result.set("network", new TenantNetworkCodec().encode(network, this)); + + return ok(result.toString()).build(); + + } + + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + public Response createNetworks(InputStream input) { + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode cfg = mapper.readTree(input); + JsonNode nodes = null; + Iterable<TenantNetwork> networks = null; + if (cfg.get("network") != null) { + nodes = cfg.get("network"); + if (nodes.isArray()) { + networks = changeJson2objs(nodes); + } else { + networks = changeJson2obj(CREATE_NETWORK, null, nodes); + } + } else if (cfg.get("networks") != null) { + nodes = cfg.get("networks"); + networks = changeJson2objs(nodes); + } + Boolean issuccess = nullIsNotFound((get(TenantNetworkService.class) + .createNetworks(networks)), + NETWORK_NOT_FOUND); + + if (!issuccess) { + return Response.status(INTERNAL_SERVER_ERROR) + .entity(NETWORK_ID_EXIST).build(); + } + return Response.status(OK).entity(issuccess.toString()).build(); + } catch (Exception e) { + log.error("Creates tenantNetwork exception {}.", e.toString()); + return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString()) + .build(); + } + } + + @PUT + @Path("{id}") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + public Response updateNetworks(@PathParam("id") String id, InputStream input) { + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode cfg = mapper.readTree(input); + JsonNode nodes = null; + Iterable<TenantNetwork> networks = null; + if (cfg.get("network") != null) { + nodes = cfg.get("network"); + if (nodes.isArray()) { + networks = changeJson2objs(nodes); + } else { + networks = changeJson2obj(UPDATE_NETWORK, + TenantNetworkId.networkId(id), + nodes); + } + } else if (cfg.get("networks") != null) { + nodes = cfg.get("networks"); + networks = changeJson2objs(nodes); + } + Boolean issuccess = nullIsNotFound((get(TenantNetworkService.class) + .updateNetworks(networks)), + NETWORK_NOT_FOUND); + if (!issuccess) { + return Response.status(INTERNAL_SERVER_ERROR) + .entity(NETWORK_ID_NOT_EXIST).build(); + } + return Response.status(OK).entity(issuccess.toString()).build(); + } catch (Exception e) { + log.error("Updates tenantNetwork failed because of exception {}.", + e.toString()); + return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString()) + .build(); + } + } + + @DELETE + @Path("{id}") + public Response deleteNetworks(@PathParam("id") String id) { + log.debug("Deletes network by identifier {}.", id); + Set<TenantNetworkId> networkSet = new HashSet<>(); + networkSet.add(TenantNetworkId.networkId(id)); + Boolean issuccess = nullIsNotFound(get(TenantNetworkService.class) + .removeNetworks(networkSet), NETWORK_NOT_FOUND); + if (!issuccess) { + log.debug("Network identifier {} is not existed", id); + return Response.status(INTERNAL_SERVER_ERROR) + .entity(NETWORK_ID_NOT_EXIST).build(); + } + return Response.status(OK).entity(issuccess.toString()).build(); + } + + /** + * Returns a collection of tenantNetworks. + * + * @param flag the flag + * @param networkId network identifier + * @param node the network json node + * @return a collection of tenantNetworks + */ + public Iterable<TenantNetwork> changeJson2obj(String flag, + TenantNetworkId networkId, + JsonNode node) { + checkNotNull(node, JSON_NOT_NULL); + TenantNetwork network = null; + ConcurrentMap<TenantNetworkId, TenantNetwork> networksMap = Maps + .newConcurrentMap(); + if (node != null) { + checkArgument(node.get("admin_state_up").isBoolean(), "admin_state_up should be boolean"); + checkArgument(node.get("shared").isBoolean(), "shared should be boolean"); + checkArgument(node.get("router:external").isBoolean(), "router:external should be boolean"); + String name = node.get("name").asText(); + boolean adminStateUp = node.get("admin_state_up").asBoolean(); + String state = node.get("status").asText(); + boolean shared = node.get("shared").asBoolean(); + String tenantId = node.get("tenant_id").asText(); + boolean routerExternal = node.get("router:external").asBoolean(); + String type = node.get("provider:network_type").asText(); + String physicalNetwork = node.get("provider:physical_network") + .asText(); + String segmentationId = node.get("provider:segmentation_id") + .asText(); + TenantNetworkId id = null; + if (flag == CREATE_NETWORK) { + id = TenantNetworkId.networkId(node.get("id").asText()); + } else if (flag == UPDATE_NETWORK) { + id = networkId; + } + network = new DefaultTenantNetwork( + id, + name, + adminStateUp, + isState(state), + shared, + TenantId.tenantId(tenantId), + routerExternal, + isType(type), + PhysicalNetwork + .physicalNetwork(physicalNetwork), + SegmentationId + .segmentationId(segmentationId)); + networksMap.putIfAbsent(id, network); + } + return Collections.unmodifiableCollection(networksMap.values()); + } + + /** + * Returns a collection of tenantNetworks. + * + * @param nodes the network jsonnodes + * @return a collection of tenantNetworks + */ + public Iterable<TenantNetwork> changeJson2objs(JsonNode nodes) { + checkNotNull(nodes, JSON_NOT_NULL); + TenantNetwork network = null; + ConcurrentMap<TenantNetworkId, TenantNetwork> networksMap = Maps + .newConcurrentMap(); + if (nodes != null) { + for (JsonNode node : nodes) { + String id = node.get("id").asText(); + String name = node.get("name").asText(); + boolean adminStateUp = node.get("admin_state_up").asBoolean(); + String state = node.get("status").asText(); + boolean shared = node.get("shared").asBoolean(); + String tenantId = node.get("tenant_id").asText(); + boolean routerExternal = node.get("router:external") + .asBoolean(); + String type = node.get("provider:network_type").asText(); + String physicalNetwork = node.get("provider:physical_network") + .asText(); + String segmentationId = node.get("provider:segmentation_id") + .asText(); + network = new DefaultTenantNetwork( + TenantNetworkId + .networkId(id), + name, + adminStateUp, + isState(state), + shared, + TenantId.tenantId(tenantId), + routerExternal, + isType(type), + PhysicalNetwork + .physicalNetwork(physicalNetwork), + SegmentationId + .segmentationId(segmentationId)); + networksMap.putIfAbsent(TenantNetworkId.networkId(id), network); + } + } + return Collections.unmodifiableCollection(networksMap.values()); + } + + /** + * Returns the specified item if that items is null; otherwise throws not + * found exception. + * + * @param item item to check + * @param <T> item type + * @param message not found message + * @return item if not null + * @throws org.onlab.util.ItemNotFoundException if item is null + */ + protected <T> T nullIsNotFound(T item, String message) { + if (item == null) { + throw new ItemNotFoundException(message); + } + return item; + } +} diff --git a/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/VirtualPortWebResource.java b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/VirtualPortWebResource.java new file mode 100644 index 00000000..03d3a653 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/VirtualPortWebResource.java @@ -0,0 +1,412 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.vtnweb.resources; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; +import static javax.ws.rs.core.Response.Status.OK; +import static javax.ws.rs.core.Response.Status.NOT_FOUND; + +import java.io.InputStream; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +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.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; +import org.onlab.util.ItemNotFoundException; +import org.onosproject.net.DeviceId; +import org.onosproject.rest.AbstractWebResource; +import org.onosproject.vtnrsc.AllowedAddressPair; +import org.onosproject.vtnrsc.BindingHostId; +import org.onosproject.vtnrsc.DefaultVirtualPort; +import org.onosproject.vtnrsc.FixedIp; +import org.onosproject.vtnrsc.SecurityGroup; +import org.onosproject.vtnrsc.SubnetId; +import org.onosproject.vtnrsc.TenantId; +import org.onosproject.vtnrsc.TenantNetworkId; +import org.onosproject.vtnrsc.VirtualPort; +import org.onosproject.vtnrsc.VirtualPort.State; +import org.onosproject.vtnrsc.VirtualPortId; +import org.onosproject.vtnrsc.virtualport.VirtualPortService; +import org.onosproject.vtnrsc.web.VirtualPortCodec; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +/** + * REST resource for interacting with the inventory of infrastructure + * virtualPort. + */ +@Path("ports") +public class VirtualPortWebResource extends AbstractWebResource { + public static final String VPORT_NOT_FOUND = "VirtualPort is not found"; + public static final String VPORT_ID_EXIST = "VirtualPort id is exist"; + public static final String VPORT_ID_NOT_EXIST = "VirtualPort id is not exist"; + public static final String JSON_NOT_NULL = "JsonNode can not be null"; + protected static final Logger log = LoggerFactory + .getLogger(VirtualPortService.class); + + @GET + @Produces({ MediaType.APPLICATION_JSON }) + public Response getPorts() { + Iterable<VirtualPort> virtualPorts = get(VirtualPortService.class) + .getPorts(); + ObjectNode result = new ObjectMapper().createObjectNode(); + result.set("ports", new VirtualPortCodec().encode(virtualPorts, this)); + return ok(result.toString()).build(); + } + + @GET + @Path("{id}") + @Produces({ MediaType.APPLICATION_JSON }) + public Response getportsById(@PathParam("id") String id) { + + if (!get(VirtualPortService.class).exists(VirtualPortId.portId(id))) { + return Response.status(NOT_FOUND) + .entity(VPORT_NOT_FOUND).build(); + } + VirtualPort virtualPort = nullIsNotFound(get(VirtualPortService.class) + .getPort(VirtualPortId.portId(id)), VPORT_NOT_FOUND); + ObjectNode result = new ObjectMapper().createObjectNode(); + result.set("port", new VirtualPortCodec().encode(virtualPort, this)); + return ok(result.toString()).build(); + } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response createPorts(InputStream input) { + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode cfg = mapper.readTree(input); + Iterable<VirtualPort> vPorts = createOrUpdateByInputStream(cfg); + Boolean issuccess = nullIsNotFound(get(VirtualPortService.class) + .createPorts(vPorts), VPORT_NOT_FOUND); + if (!issuccess) { + return Response.status(INTERNAL_SERVER_ERROR) + .entity(VPORT_ID_NOT_EXIST).build(); + } + return Response.status(OK).entity(issuccess.toString()).build(); + } catch (Exception e) { + log.error("Creates VirtualPort failed because of exception {}", + e.toString()); + return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString()) + .build(); + } + } + + @Path("{portUUID}") + @DELETE + public Response deletePorts(@PathParam("portUUID") String id) { + Set<VirtualPortId> vPortIds = new HashSet<>(); + try { + if (id != null) { + vPortIds.add(VirtualPortId.portId(id)); + } + Boolean issuccess = nullIsNotFound(get(VirtualPortService.class) + .removePorts(vPortIds), VPORT_NOT_FOUND); + if (!issuccess) { + return Response.status(INTERNAL_SERVER_ERROR) + .entity(VPORT_ID_NOT_EXIST).build(); + } + return Response.status(OK).entity(issuccess.toString()).build(); + } catch (Exception e) { + log.error("Deletes VirtualPort failed because of exception {}", + e.toString()); + return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString()) + .build(); + } + } + + @PUT + @Path("{id}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response updatePorts(@PathParam("id") String id, InputStream input) { + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode cfg = mapper.readTree(input); + Iterable<VirtualPort> vPorts = createOrUpdateByInputStream(cfg); + Boolean issuccess = nullIsNotFound(get(VirtualPortService.class) + .updatePorts(vPorts), VPORT_NOT_FOUND); + if (!issuccess) { + return Response.status(INTERNAL_SERVER_ERROR) + .entity(VPORT_ID_NOT_EXIST).build(); + } + return Response.status(OK).entity(issuccess.toString()).build(); + } catch (Exception e) { + log.error("Updates failed because of exception {}", e.toString()); + return Response.status(INTERNAL_SERVER_ERROR).entity(e.toString()) + .build(); + } + } + + /** + * Returns a Object of the currently known infrastructure virtualPort. + * + * @param vPortNode the virtualPort json node + * @return a collection of virtualPorts + */ + public Iterable<VirtualPort> createOrUpdateByInputStream(JsonNode vPortNode) { + checkNotNull(vPortNode, JSON_NOT_NULL); + JsonNode vPortNodes = vPortNode.get("ports"); + if (vPortNodes == null) { + vPortNodes = vPortNode.get("port"); + } + if (vPortNodes.isArray()) { + return changeJsonToPorts(vPortNodes); + } else { + return changeJsonToPort(vPortNodes); + } + } + + /** + * Returns the iterable collection of virtualports from subnetNodes. + * + * @param vPortNodes the virtualPort json node + * @return virtualPorts a collection of virtualPorts + */ + public Iterable<VirtualPort> changeJsonToPorts(JsonNode vPortNodes) { + checkNotNull(vPortNodes, JSON_NOT_NULL); + Map<VirtualPortId, VirtualPort> portMap = new HashMap<>(); + Map<String, String> strMap = new HashMap<>(); + for (JsonNode vPortnode : vPortNodes) { + VirtualPortId id = VirtualPortId.portId(vPortnode.get("id") + .asText()); + String name = vPortnode.get("name").asText(); + TenantId tenantId = TenantId.tenantId(vPortnode.get("tenant_id") + .asText()); + TenantNetworkId networkId = TenantNetworkId.networkId(vPortnode + .get("network_id").asText()); + checkArgument(vPortnode.get("admin_state_up").isBoolean(), "admin_state_up should be boolean"); + Boolean adminStateUp = vPortnode.get("admin_state_up").asBoolean(); + String state = vPortnode.get("status").asText(); + MacAddress macAddress = MacAddress.valueOf(vPortnode + .get("mac_address").asText()); + DeviceId deviceId = DeviceId.deviceId(vPortnode.get("device_id") + .asText()); + String deviceOwner = vPortnode.get("device_owner").asText(); + JsonNode fixedIpNodes = vPortNodes.get("fixed_ips"); + Set<FixedIp> fixedIps = new HashSet<>(); + for (JsonNode fixedIpNode : fixedIpNodes) { + FixedIp fixedIp = jsonNodeToFixedIps(fixedIpNode); + fixedIps.add(fixedIp); + } + + BindingHostId bindingHostId = BindingHostId + .bindingHostId(vPortnode.get("binding:host_id").asText()); + String bindingVnicType = vPortnode.get("binding:vnic_type") + .asText(); + String bindingVifType = vPortnode.get("binding:vif_type").asText(); + String bindingVifDetails = vPortnode.get("binding:vif_details") + .asText(); + JsonNode allowedAddressPairJsonNode = vPortnode + .get("allowed_address_pairs"); + Collection<AllowedAddressPair> allowedAddressPairs = + jsonNodeToAllowedAddressPair(allowedAddressPairJsonNode); + JsonNode securityGroupNode = vPortnode.get("security_groups"); + Collection<SecurityGroup> securityGroups = jsonNodeToSecurityGroup(securityGroupNode); + strMap.put("name", name); + strMap.put("deviceOwner", deviceOwner); + strMap.put("bindingVnicType", bindingVnicType); + strMap.put("bindingVifType", bindingVifType); + strMap.put("bindingVifDetails", bindingVifDetails); + VirtualPort vPort = new DefaultVirtualPort(id, networkId, + adminStateUp, strMap, + isState(state), + macAddress, tenantId, + deviceId, fixedIps, + bindingHostId, + Sets.newHashSet(allowedAddressPairs), + Sets.newHashSet(securityGroups)); + portMap.put(id, vPort); + } + return Collections.unmodifiableCollection(portMap.values()); + } + + /** + * Returns a collection of virtualPorts from subnetNodes. + * + * @param vPortNodes the virtualPort json node + * @return virtualPorts a collection of virtualPorts + */ + public Iterable<VirtualPort> changeJsonToPort(JsonNode vPortNodes) { + checkNotNull(vPortNodes, JSON_NOT_NULL); + Map<VirtualPortId, VirtualPort> vportMap = new HashMap<>(); + Map<String, String> strMap = new HashMap<>(); + VirtualPortId id = VirtualPortId.portId(vPortNodes.get("id").asText()); + String name = vPortNodes.get("name").asText(); + TenantId tenantId = TenantId.tenantId(vPortNodes.get("tenant_id") + .asText()); + TenantNetworkId networkId = TenantNetworkId.networkId(vPortNodes + .get("network_id").asText()); + Boolean adminStateUp = vPortNodes.get("admin_state_up").asBoolean(); + String state = vPortNodes.get("status").asText(); + MacAddress macAddress = MacAddress.valueOf(vPortNodes + .get("mac_address").asText()); + DeviceId deviceId = DeviceId.deviceId(vPortNodes.get("device_id") + .asText()); + String deviceOwner = vPortNodes.get("device_owner").asText(); + JsonNode fixedIpNodes = vPortNodes.get("fixed_ips"); + Set<FixedIp> fixedIps = new HashSet<>(); + for (JsonNode fixedIpNode : fixedIpNodes) { + FixedIp fixedIp = jsonNodeToFixedIps(fixedIpNode); + fixedIps.add(fixedIp); + } + + BindingHostId bindingHostId = BindingHostId + .bindingHostId(vPortNodes.get("binding:host_id").asText()); + String bindingVnicType = vPortNodes.get("binding:vnic_type").asText(); + String bindingVifType = vPortNodes.get("binding:vif_type").asText(); + String bindingVifDetails = vPortNodes.get("binding:vif_details") + .asText(); + JsonNode allowedAddressPairJsonNode = vPortNodes + .get("allowed_address_pairs"); + Collection<AllowedAddressPair> allowedAddressPairs = + jsonNodeToAllowedAddressPair(allowedAddressPairJsonNode); + JsonNode securityGroupNode = vPortNodes.get("security_groups"); + Collection<SecurityGroup> securityGroups = jsonNodeToSecurityGroup(securityGroupNode); + strMap.put("name", name); + strMap.put("deviceOwner", deviceOwner); + strMap.put("bindingVnicType", bindingVnicType); + strMap.put("bindingVifType", bindingVifType); + strMap.put("bindingVifDetails", bindingVifDetails); + VirtualPort vPort = new DefaultVirtualPort(id, networkId, adminStateUp, + strMap, isState(state), + macAddress, tenantId, + deviceId, fixedIps, + bindingHostId, + Sets.newHashSet(allowedAddressPairs), + Sets.newHashSet(securityGroups)); + vportMap.put(id, vPort); + + return Collections.unmodifiableCollection(vportMap.values()); + } + + /** + * Returns a Object of the currently known infrastructure virtualPort. + * + * @param allowedAddressPairs the allowedAddressPairs json node + * @return a collection of allowedAddressPair + */ + public Collection<AllowedAddressPair> jsonNodeToAllowedAddressPair(JsonNode allowedAddressPairs) { + checkNotNull(allowedAddressPairs, JSON_NOT_NULL); + ConcurrentMap<Integer, AllowedAddressPair> allowMaps = Maps + .newConcurrentMap(); + int i = 0; + for (JsonNode node : allowedAddressPairs) { + IpAddress ip = IpAddress.valueOf(node.get("ip_address").asText()); + MacAddress mac = MacAddress.valueOf(node.get("mac_address") + .asText()); + AllowedAddressPair allows = AllowedAddressPair + .allowedAddressPair(ip, mac); + allowMaps.put(i, allows); + i++; + } + log.debug("The jsonNode of allowedAddressPairallow is {}" + + allowedAddressPairs.toString()); + return Collections.unmodifiableCollection(allowMaps.values()); + } + + /** + * Returns a collection of virtualPorts. + * + * @param securityGroups the virtualPort jsonnode + * @return a collection of securityGroups + */ + public Collection<SecurityGroup> jsonNodeToSecurityGroup(JsonNode securityGroups) { + checkNotNull(securityGroups, JSON_NOT_NULL); + ConcurrentMap<Integer, SecurityGroup> securMaps = Maps + .newConcurrentMap(); + int i = 0; + for (JsonNode node : securityGroups) { + SecurityGroup securityGroup = SecurityGroup + .securityGroup(node.asText()); + securMaps.put(i, securityGroup); + i++; + } + return Collections.unmodifiableCollection(securMaps.values()); + } + + /** + * Returns a collection of fixedIps. + * + * @param fixedIpNode the fixedIp jsonnode + * @return a collection of SecurityGroup + */ + public FixedIp jsonNodeToFixedIps(JsonNode fixedIpNode) { + SubnetId subnetId = SubnetId.subnetId(fixedIpNode.get("subnet_id") + .asText()); + IpAddress ipAddress = IpAddress.valueOf(fixedIpNode.get("ip_address") + .asText()); + FixedIp fixedIps = FixedIp.fixedIp(subnetId, ipAddress); + return fixedIps; + } + + /** + * Returns VirtualPort State. + * + * @param state the virtualport state + * @return the virtualPort state + */ + private State isState(String state) { + if (state.equals("ACTIVE")) { + return VirtualPort.State.ACTIVE; + } else { + return VirtualPort.State.DOWN; + } + + } + + /** + * Returns the specified item if that items is null; otherwise throws not + * found exception. + * + * @param item item to check + * @param <T> item type + * @param message not found message + * @return item if not null + * @throws org.onlab.util.ItemNotFoundException if item is null + */ + protected <T> T nullIsNotFound(T item, String message) { + if (item == null) { + throw new ItemNotFoundException(message); + } + return item; + } +} diff --git a/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/package-info.java b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/package-info.java new file mode 100644 index 00000000..c81fc3d8 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnweb/src/main/java/org/onosproject/vtnweb/resources/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * VTN web that used rest to creat vtn resources. + */ +package org.onosproject.vtnweb.resources; diff --git a/framework/src/onos/apps/vtn/vtnweb/src/main/webapp/WEB-INF/web.xml b/framework/src/onos/apps/vtn/vtnweb/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..4cc12455 --- /dev/null +++ b/framework/src/onos/apps/vtn/vtnweb/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" + 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"> + <display-name>VTNRSC REST API v1.0</display-name> + + <servlet> + <servlet-name>JAX-RS Service</servlet-name> + <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class> + <init-param> + <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name> + <param-value>com.sun.jersey.api.core.ClassNamesResourceConfig</param-value> + </init-param> + <init-param> + <param-name>com.sun.jersey.config.property.classnames</param-name> + <param-value> + org.onosproject.vtnweb.resources.TenantNetworkWebResource, + org.onosproject.vtnweb.resources.SubnetWebResource, + org.onosproject.vtnweb.resources.VirtualPortWebResource + </param-value> + </init-param> + <load-on-startup>1</load-on-startup> + </servlet> + + <servlet-mapping> + <servlet-name>JAX-RS Service</servlet-name> + <url-pattern>/*</url-pattern> + </servlet-mapping> +</web-app> diff --git a/framework/src/onos/bgp/api/pom.xml b/framework/src/onos/bgp/api/pom.xml new file mode 100755 index 00000000..6fa1cc7b --- /dev/null +++ b/framework/src/onos/bgp/api/pom.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-bgp</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>onos-bgp-api</artifactId> + <packaging>bundle</packaging> + + <description>ONOS BGP controller subsystem API</description> + + <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-bgpio</artifactId> + </dependency> + <dependency> + <groupId>io.netty</groupId> + <artifactId>netty</artifactId> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-misc</artifactId> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>2.3</version> + <configuration> + <artifactSet> + <excludes> + <exclude>io.netty:netty</exclude> + <exclude>com.google.guava:guava</exclude> + <exclude>org.slf4j:slfj-api</exclude> + <exclude>ch.qos.logback:logback-core</exclude> + <exclude>ch.qos.logback:logback-classic</exclude> + <exclude>com.google.code.findbugs:annotations</exclude> + </excludes> + </artifactSet> + </configuration> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <configuration> + <instructions> + <Export-Package> + org.onosproject.bgp.*,org.onosproject.bgpio.*,org.onosproject.bgp.controller + </Export-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPCfg.java b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPCfg.java new file mode 100755 index 00000000..46165d87 --- /dev/null +++ b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPCfg.java @@ -0,0 +1,297 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgp.controller; + +import java.util.TreeMap; + +/** + * Abstraction of an BGP configuration. Manages the BGP configuration from CLI to the BGP controller. + */ +public interface BGPCfg { + + enum State { + /** + * Signifies that its just created. + */ + INIT, + + /** + * Signifies that only IP Address is configured. + */ + IP_CONFIGURED, + + /** + * Signifies that only Autonomous System is configured. + */ + AS_CONFIGURED, + + /** + * Signifies that both IP and Autonomous System is configured. + */ + IP_AS_CONFIGURED + } + + /** + * Returns the status of the configuration based on this state certain operations like connection is handled. + * + * @return State of the configuration + */ + State getState(); + + /** + * To set the current state of the configuration. + * + * @param state Configuration State enum + */ + void setState(State state); + + /** + * Get the status of the link state support for this BGP speaker. + * + * @return true if the link state is supported else false + */ + boolean getLsCapability(); + + /** + * Set the link state support to this BGP speaker. + * + * @param lscapability true value if link state is supported else false + */ + void setLsCapability(boolean lscapability); + + /** + * Get the status of the 32 bit AS support for this BGP speaker. + * + * @return true if the 32 bit AS number is supported else false + */ + boolean getLargeASCapability(); + + /** + * Set the 32 bit AS support capability to this BGP speaker. + * + * @param largeAs true value if the 32 bit AS is supported else false + */ + void setLargeASCapability(boolean largeAs); + + /** + * Set the AS number to which this BGP speaker belongs. + * + * @param localAs 16 or 32 bit AS number, length is dependent on the capability + */ + void setAsNumber(int localAs); + + /** + * Get the AS number to which this BGP speaker belongs. + * + * @return 16 or 32 bit AS number, length is dependent on the capability + */ + int getAsNumber(); + + /** + * Get the connection retry count number. + * + * @return connection retry count if there is a connection error + */ + int getMaxConnRetryCount(); + + /** + * Set the connection retry count. + * + * @param retryCount number of times to try to connect if there is any error + */ + void setMaxConnRetryCout(int retryCount); + + /** + * Get the connection retry time in seconds. + * + * @return connection retry time in seconds + */ + int getMaxConnRetryTime(); + + /** + * Set the connection retry time in seconds. + * + * @param retryTime connection retry times in seconds + */ + void setMaxConnRetryTime(int retryTime); + + /** + * Set the keep alive timer for the connection. + * + * @param holdTime connection hold timer in seconds + */ + void setHoldTime(short holdTime); + + /** + * Returns the connection hold timer in seconds. + * + * @return connection hold timer in seconds + */ + short getHoldTime(); + + /** + * Returns the maximum number of session supported. + * + * @return maximum number of session supported + */ + int getMaxSession(); + + /** + * Set the maximum number of sessions to support. + * + * @param maxsession maximum number of session + */ + void setMaxSession(int maxsession); + + /** + * Returns the Router ID of this BGP speaker. + * + * @return IP address in string format + */ + String getRouterId(); + + /** + * Set the Router ID of this BGP speaker. + * + * @param routerid IP address in string format + */ + void setRouterId(String routerid); + + /** + * Add the BGP peer IP address and the AS number to which it belongs. + * + * @param routerid IP address in string format + * @param remoteAs AS number to which it belongs + * + * @return true if added successfully else false + */ + boolean addPeer(String routerid, int remoteAs); + + /** + * Add the BGP peer IP address and the keep alive time. + * + * @param routerid IP address in string format + * @param holdTime keep alive time for the connection + * + * @return true if added successfully else false + */ + boolean addPeer(String routerid, short holdTime); + + /** + * Add the BGP peer IP address, the AS number to which it belongs and keep alive time. + * + * @param routerid IP address in string format + * @param remoteAs AS number to which it belongs + * @param holdTime keep alive time for the connection + * + * @return true if added successfully else false + */ + boolean addPeer(String routerid, int remoteAs, short holdTime); + + /** + * Remove the BGP peer with this IP address. + * + * @param routerid router IP address + * + * @return true if removed successfully else false + */ + boolean removePeer(String routerid); + + /** + * Connect to BGP peer with this IP address. + * + * @param routerid router IP address + * + * @return true of the configuration is found and able to connect else false + */ + boolean connectPeer(String routerid); + + /** + * Disconnect this BGP peer with this IP address. + * + * @param routerid router IP address in string format + * + * @return true if the configuration is found and able to disconnect else false + */ + boolean disconnectPeer(String routerid); + + /** + * Returns the peer tree information. + * + * @return return the tree map with IP as key and BGPPeerCfg as object + */ + TreeMap<String, BGPPeerCfg> displayPeers(); + + /** + * Return the BGP Peer information with this matching IP. + * + * @param routerid router IP address in string format + * + * @return BGPPeerCfg object + */ + BGPPeerCfg displayPeers(String routerid); + + /** + * Check if this BGP peer is configured. + * + * @param routerid router IP address in string format + * + * @return true if configured exists else false + */ + boolean isPeerConfigured(String routerid); + + /** + * Check if this BGP speaker is having connection with the peer. + * + * @param routerid router IP address in string format + * + * @return true if the connection exists else false + */ + boolean isPeerConnected(String routerid); + + /** + * Return the peer tree map. + * + * @return return the tree map with IP as key and BGPPeerCfg as object + */ + TreeMap<String, BGPPeerCfg> getPeerTree(); + + /** + * Set the current connection state information. + * + * @param routerid router IP address in string format + * @param state state information + */ + void setPeerConnState(String routerid, BGPPeerCfg.State state); + + /** + * Check if the peer can be connected or not. + * + * @param routerid router IP address in string format + * + * @return true if the peer can be connected else false + */ + boolean isPeerConnectable(String routerid); + + /** + * Get the current peer connection state information. + * + * @param routerid router IP address in string format + * + * @return state information + */ + BGPPeerCfg.State getPeerConnState(String routerid); +} diff --git a/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPController.java b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPController.java new file mode 100755 index 00000000..6d758122 --- /dev/null +++ b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPController.java @@ -0,0 +1,49 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgp.controller; + +import org.onosproject.bgpio.protocol.BGPMessage; + +/** + * Abstraction of an BGP controller. Serves as a one stop shop for obtaining BGP devices and (un)register listeners + * on bgp events + */ +public interface BGPController { + + /** + * Send a message to a particular bgp peer. + * + * @param bgpId the id of the peer to send message. + * @param msg the message to send + */ + void writeMsg(BGPId bgpId, BGPMessage msg); + + /** + * Process a message and notify the appropriate listeners. + * + * @param bgpId id of the peer the message arrived on + * @param msg the message to process. + */ + void processBGPPacket(BGPId bgpId, BGPMessage msg); + + /** + * Get the BGPConfig class to the caller. + * + * @return configuration object + */ + BGPCfg getConfig(); +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPId.java b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPId.java new file mode 100755 index 00000000..636e72f3 --- /dev/null +++ b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPId.java @@ -0,0 +1,121 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgp.controller; + +import org.onlab.packet.IpAddress; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Objects; + +import static com.google.common.base.Preconditions.checkArgument; + +/** + * The class representing a network peer bgp ip. + * This class is immutable. + */ +public final class BGPId { + + private static final String SCHEME = "bgp"; + private static final long UNKNOWN = 0; + private final IpAddress ipAddress; + + /** + * Private constructor. + */ + private BGPId(IpAddress ipAddress) { + this.ipAddress = ipAddress; + } + + /** + * Create a BGPId from ip address. + * + * @param ipAddress IP address + * @return object of BGPId + */ + public static BGPId bgpId(IpAddress ipAddress) { + return new BGPId(ipAddress); + } + + /** + * Returns the ip address. + * + * @return ipAddress + */ + public IpAddress ipAddress() { + return ipAddress; + } + + /** + * Convert the BGPId value to a ':' separated hexadecimal string. + * + * @return the BGPId value as a ':' separated hexadecimal string. + */ + @Override + public String toString() { + return ipAddress.toString(); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof BGPId)) { + return false; + } + + BGPId otherBGPid = (BGPId) other; + return Objects.equals(ipAddress, otherBGPid.ipAddress); + } + + @Override + public int hashCode() { + return Objects.hash(ipAddress); + } + + /** + * Returns BGPId created from the given device URI. + * + * @param uri device URI + * @return object of BGPId + */ + public static BGPId bgpId(URI uri) { + checkArgument(uri.getScheme().equals(SCHEME), "Unsupported URI scheme"); + return new BGPId(IpAddress.valueOf(uri.getSchemeSpecificPart())); + } + + /** + * Produces device URI from the given DPID. + * + * @param bgpId device bgpId + * @return device URI + */ + public static URI uri(BGPId bgpId) { + return uri(bgpId.ipAddress()); + } + + /** + * Produces device URI from the given DPID long. + * + * @param ipAddress device ip address + * @return device URI + */ + public static URI uri(IpAddress ipAddress) { + try { + return new URI(SCHEME, ipAddress.toString(), null); + } catch (URISyntaxException e) { + return null; + } + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPPacketStats.java b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPPacketStats.java new file mode 100755 index 00000000..95f83a2d --- /dev/null +++ b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPPacketStats.java @@ -0,0 +1,52 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgp.controller; + +/** + * A representation of a packet context which allows any provider to view a packet in event, but may block the response + * to the event if blocked has been called. This packet context can be used to react to the packet in event with a + * packet out. + */ +public interface BGPPacketStats { + /** + * Returns the count for no of packets sent out. + * + * @return int value of no of packets sent + */ + int outPacketCount(); + + /** + * Returns the count for no of packets received. + * + * @return int value of no of packets sent + */ + int inPacketCount(); + + /** + * Returns the count for no of wrong packets received. + * + * @return int value of no of wrong packets received + */ + int wrongPacketCount(); + + /** + * Returns the time. + * + * @return the time + */ + long getTime(); +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPPeerCfg.java b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPPeerCfg.java new file mode 100755 index 00000000..87ec031f --- /dev/null +++ b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/BGPPeerCfg.java @@ -0,0 +1,166 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgp.controller; + +/** + * BGP Peer configuration information. + */ +public interface BGPPeerCfg { + + enum State { + + /** + * Signifies that peer connection is idle. + */ + IDLE, + + /** + * Signifies that connection is initiated. + */ + CONNECT, + + /** + * Signifies that state is active and connection can be established. + */ + ACTIVE, + + /** + * Signifies that open is sent and anticipating reply. + */ + OPENSENT, + + /** + * Signifies that peer sent the open message as reply. + */ + OPENCONFIRM, + + /** + * Signifies that all the negotiation is successful and ready to exchange other messages. + */ + ESTABLISHED, + + /** + * Signifies that invalid state. + */ + INVALID + } + + /** + * Returns the connection State information of the peer. + * + * @return + * enum state is returned + */ + State getState(); + + /** + * Set the connection state information of the peer. + * + * @param state + * enum state + */ + void setState(State state); + + /** + * Returns the connection is initiated from us or not. + * + * @return + * true if the connection is initiated by this peer, false if it has been received. + */ + boolean getSelfInnitConnection(); + + /** + * Set the connection is initiated from us or not. + * + * @param selfInit + * true if the connection is initiated by this peer, false if it has been received. + */ + void setSelfInnitConnection(boolean selfInit); + + /** + * Returns the AS number to which this peer belongs. + * + * @return + * AS number + */ + int getAsNumber(); + + /** + * Set the AS number to which this peer belongs. + * + * @param asNumber + * AS number + */ + void setAsNumber(int asNumber); + + /** + * Get the keep alive timer value configured. + * + * @return + * keep alive timer value in seconds + */ + short getHoldtime(); + + /** + * Set the keep alive timer value. + * + * @param holdTime + * keep alive timer value in seconds + */ + void setHoldtime(short holdTime); + + /** + * Return the connection type eBGP or iBGP. + * + * @return + * true if iBGP, false if it is eBGP + */ + boolean getIsIBgp(); + + /** + * Set the connection type eBGP or iBGP. + * + * @param isIBgp + * true if iBGP, false if it is eBGP + */ + void setIsIBgp(boolean isIBgp); + + /** + * Return the peer router IP address. + * + * @return + * IP address in string format + */ + String getPeerRouterId(); + + /** + * Set the peer router IP address. + * + * @param peerId + * IP address in string format + */ + void setPeerRouterId(String peerId); + + /** + * Set the peer router IP address and AS number. + * + * @param peerId + * IP address in string format + * @param asNumber + * AS number + */ + void setPeerRouterId(String peerId, int asNumber); +} diff --git a/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/package-info.java b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/package-info.java new file mode 100755 index 00000000..4dd775b8 --- /dev/null +++ b/framework/src/onos/bgp/api/src/main/java/org/onosproject/bgp/controller/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * BGP controller API. + */ +package org.onosproject.bgp.controller; diff --git a/framework/src/onos/bgp/bgpio/pom.xml b/framework/src/onos/bgp/bgpio/pom.xml new file mode 100755 index 00000000..5d67f18c --- /dev/null +++ b/framework/src/onos/bgp/bgpio/pom.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-bgp</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>onos-bgpio</artifactId> + <packaging>bundle</packaging> + + <description>ONOS BGPio Protocol subsystem</description> + + <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-osgi</artifactId> + </dependency> + + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-annotations</artifactId> + </dependency> + + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + </dependency> + <dependency> + <groupId>org.apache.karaf.shell</groupId> + <artifactId>org.apache.karaf.shell.console</artifactId> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + </plugin> + </plugins> + </build> + +</project> diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/exceptions/BGPParseException.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/exceptions/BGPParseException.java new file mode 100755 index 00000000..62427a44 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/exceptions/BGPParseException.java @@ -0,0 +1,106 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.exceptions; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Custom Exception for BGP IO. + */ +public class BGPParseException extends Exception { + + private static final long serialVersionUID = 1L; + private byte errorCode; + private byte errorSubCode; + private ChannelBuffer data; + + /** + * Default constructor to create a new exception. + */ + public BGPParseException() { + super(); + } + + /** + * Constructor to create exception from message and cause. + * + * @param message the detail of exception in string + * @param cause underlying cause of the error + */ + public BGPParseException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Constructor to create exception from message. + * + * @param message the detail of exception in string + */ + public BGPParseException(final String message) { + super(message); + } + + /** + * Constructor to create exception from cause. + * + * @param cause underlying cause of the error + */ + public BGPParseException(final Throwable cause) { + super(cause); + } + + /** + * Constructor to create exception from error code and error subcode. + * + * @param errorCode error code of BGP message + * @param errorSubCode error subcode of BGP message + * @param data error data of BGP message + */ + public BGPParseException(final byte errorCode, final byte errorSubCode, final ChannelBuffer data) { + super(); + this.errorCode = errorCode; + this.errorSubCode = errorSubCode; + this.data = data; + } + + /** + * Returns errorcode for this exception. + * + * @return errorcode for this exception + */ + public byte getErrorCode() { + return this.errorCode; + } + + /** + * Returns error Subcode for this exception. + * + * @return error Subcode for this exception + */ + public byte getErrorSubCode() { + return this.errorSubCode; + } + + /** + * Returns error data for this exception. + * + * @return error data for this exception + */ + public ChannelBuffer getData() { + return this.data; + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/exceptions/package-info.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/exceptions/package-info.java new file mode 100755 index 00000000..78b28072 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/exceptions/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * BGP custom exceptions. + */ +package org.onosproject.bgpio.exceptions; diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPKeepaliveMsg.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPKeepaliveMsg.java new file mode 100644 index 00000000..c8aef36e --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPKeepaliveMsg.java @@ -0,0 +1,58 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgpio.protocol; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onosproject.bgpio.types.BGPHeader; + +/** + * Abstraction of an entity providing BGP Keepalive Message. + */ +public interface BGPKeepaliveMsg extends BGPMessage { + + @Override + BGPVersion getVersion(); + + @Override + BGPType getType(); + + @Override + void writeTo(ChannelBuffer channelBuffer); + + @Override + BGPHeader getHeader(); + + /** + * Builder interface with get and set functions to build Keepalive message. + */ + interface Builder extends BGPMessage.Builder { + + @Override + BGPKeepaliveMsg build(); + + @Override + BGPVersion getVersion(); + + @Override + BGPType getType(); + + @Override + Builder setHeader(BGPHeader bgpMsgHeader); + + @Override + BGPHeader getHeader(); + } +} diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessage.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessage.java new file mode 100644 index 00000000..a5d8154f --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessage.java @@ -0,0 +1,92 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.protocol; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onosproject.bgpio.exceptions.BGPParseException; +import org.onosproject.bgpio.types.BGPHeader; + +/** + * Abstraction of an entity providing BGP Messages. + */ +public interface BGPMessage extends Writeable { + /** + * Returns BGP Header of BGP Message. + * + * @return BGP Header of BGP Message + */ + BGPHeader getHeader(); + + /** + * Returns version of BGP Message. + * + * @return version of BGP Message + */ + BGPVersion getVersion(); + + /** + * Returns BGP Type of BGP Message. + * + * @return BGP Type of BGP Message + */ + BGPType getType(); + + @Override + void writeTo(ChannelBuffer cb) throws BGPParseException; + + /** + * Builder interface with get and set functions to build BGP Message. + */ + interface Builder { + /** + * Builds BGP Message. + * + * @return BGP Message + * @throws BGPParseException while building bgp message + */ + BGPMessage build() throws BGPParseException; + + /** + * Returns BGP Version of BGP Message. + * + * @return BGP Version of BGP Message + */ + BGPVersion getVersion(); + + /** + * Returns BGP Type of BGP Message. + * + * @return BGP Type of BGP Message + */ + BGPType getType(); + + /** + * Returns BGP Header of BGP Message. + * + * @return BGP Header of BGP Message + */ + BGPHeader getHeader(); + + /** + * Sets BgpHeader and return its builder. + * + * @param bgpMsgHeader BGP Message Header + * @return builder by setting BGP message header + */ + Builder setHeader(BGPHeader bgpMsgHeader); + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessageReader.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessageReader.java new file mode 100755 index 00000000..18b8f58d --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessageReader.java @@ -0,0 +1,36 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgpio.protocol; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onosproject.bgpio.exceptions.BGPParseException; +import org.onosproject.bgpio.types.BGPHeader; + +/** + * Abstraction of an entity providing BGP Message Reader. + */ +public interface BGPMessageReader<T> { + + /** + * Reads the Objects in the BGP Message and Returns BGP Message. + * + * @param cb Channel Buffer + * @param bgpHeader BGP message header + * @return BGP Message + * @throws BGPParseException while parsing BGP message. + */ + T readFrom(ChannelBuffer cb, BGPHeader bgpHeader) throws BGPParseException; +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessageWriter.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessageWriter.java new file mode 100644 index 00000000..11f161c4 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPMessageWriter.java @@ -0,0 +1,36 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.protocol; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onosproject.bgpio.exceptions.BGPParseException; + +/** + * Abstraction of an entity providing BGP Message Writer. + */ +public interface BGPMessageWriter<T> { + + /** + * Writes the Objects of the BGP Message into Channel Buffer. + * + * @param cb Channel Buffer + * @param message BGP Message + * @throws BGPParseException + * While writing message + */ + void write(ChannelBuffer cb, T message) throws BGPParseException; +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPOpenMsg.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPOpenMsg.java new file mode 100644 index 00000000..c41e5eb6 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPOpenMsg.java @@ -0,0 +1,151 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.protocol; + +import java.util.LinkedList; + +import org.onosproject.bgpio.exceptions.BGPParseException; +import org.onosproject.bgpio.types.BGPHeader; +import org.onosproject.bgpio.types.BGPValueType; + +/** + * Abstraction of an entity providing BGP Open Message. + */ +public interface BGPOpenMsg extends BGPMessage { + + @Override + BGPHeader getHeader(); + + @Override + BGPVersion getVersion(); + + @Override + BGPType getType(); + + /** + * Returns hold time of Open Message. + * + * @return hold time of Open Message + */ + short getHoldTime(); + + /** + * Returns AS Number of Open Message. + * + * @return AS Number of Open Message + */ + short getAsNumber(); + + /** + * Returns BGP Identifier of Open Message. + * + * @return BGP Identifier of Open Message + */ + int getBgpId(); + + /** + * Returns capabilities of Open Message. + * + * @return capabilities of Open Message + */ + LinkedList<BGPValueType> getCapabilityTlv(); + + /** + * Builder interface with get and set functions to build Open message. + */ + interface Builder extends BGPMessage.Builder { + + @Override + BGPOpenMsg build() throws BGPParseException; + + @Override + BGPHeader getHeader(); + + @Override + BGPVersion getVersion(); + + @Override + BGPType getType(); + + /** + * Returns hold time of Open Message. + * + * @return hold time of Open Message + */ + short getHoldTime(); + + /** + * Sets hold time in Open Message and return its builder. + * + * @param holdtime + * hold timer value in open message + * @return builder by setting hold time + */ + Builder setHoldTime(short holdtime); + + /** + * Returns as number of Open Message. + * + * @return as number of Open Message + */ + short getAsNumber(); + + /** + * Sets AS number in Open Message and return its builder. + * + * @param asNumber + * as number in open message + * @return builder by setting asNumber + */ + Builder setAsNumber(short asNumber); + + /** + * Returns BGP Identifier of Open Message. + * + * @return BGP Identifier of Open Message + */ + int getBgpId(); + + /** + * Sets BGP Identifier in Open Message and return its builder. + * + * @param bgpId + * BGP Identifier in open message + * @return builder by setting BGP Identifier + */ + Builder setBgpId(int bgpId); + + /** + * Returns capabilities of Open Message. + * + * @return capabilities of Open Message + */ + LinkedList<BGPValueType> getCapabilityTlv(); + + /** + * Sets capabilities in Open Message and return its builder. + * + * @param capabilityTlv + * capabilities in open message + * @return builder by setting capabilities + */ + Builder setCapabilityTlv(LinkedList<BGPValueType> capabilityTlv); + + @Override + Builder setHeader(BGPHeader bgpMsgHeader); + } +} diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPType.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPType.java new file mode 100755 index 00000000..d3349156 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPType.java @@ -0,0 +1,45 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.protocol; + +/** + * Enum to Provide the Different types of BGP messages. + */ +public enum BGPType { + + NONE(0), OPEN(1), UPDATE(2), NOTIFICATION(3), KEEP_ALIVE(4); + + int value; + + /** + * Assign value with the value val as the types of BGP message. + * + * @param val type of BGP message + */ + BGPType(int val) { + value = val; + } + + /** + * Returns value as type of BGP message. + * + * @return value type of BGP message + */ + public byte getType() { + return (byte) value; + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPVersion.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPVersion.java new file mode 100755 index 00000000..97bc7dce --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/BGPVersion.java @@ -0,0 +1,45 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.protocol; + +/** + * Enum to provide BGP Message Version. + */ +public enum BGPVersion { + + BGP_4(4); + + public final int packetVersion; + + /** + * Assign BGP PacketVersion with specified packetVersion. + * + * @param packetVersion version of BGP + */ + BGPVersion(final int packetVersion) { + this.packetVersion = packetVersion; + } + + /** + * Returns Packet version of BGP Message. + * + * @return packetVersion + */ + public int getPacketVersion() { + return packetVersion; + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/IGPRouterID.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/IGPRouterID.java new file mode 100644 index 00000000..377d12b7 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/IGPRouterID.java @@ -0,0 +1,23 @@ +/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.bgpio.protocol;
+
+/**
+ * Provides Abstraction of IGP RouterID TLV.
+ */
+public interface IGPRouterID {
+}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/Writeable.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/Writeable.java new file mode 100755 index 00000000..72df7f32 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/Writeable.java @@ -0,0 +1,35 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.protocol; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onosproject.bgpio.exceptions.BGPParseException; + +/** + * Abstraction of an entity providing functionality to write byte streams of + * Messages to channel buffer. + */ +public interface Writeable { + + /** + * Writes byte streams of messages to channel buffer. + * + * @param cb channelBuffer + * @throws BGPParseException when error occurs while writing BGP message to channel buffer + */ + void writeTo(ChannelBuffer cb) throws BGPParseException; +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/BGPPrefixLSIdentifier.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/BGPPrefixLSIdentifier.java new file mode 100644 index 00000000..4fef47ff --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/BGPPrefixLSIdentifier.java @@ -0,0 +1,228 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.protocol.link_state; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Objects; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onosproject.bgpio.exceptions.BGPParseException; +import org.onosproject.bgpio.types.BGPErrorType; +import org.onosproject.bgpio.types.BGPValueType; +import org.onosproject.bgpio.types.IPReachabilityInformationTlv; +import org.onosproject.bgpio.types.OSPFRouteTypeTlv; +import org.onosproject.bgpio.types.attr.BgpAttrNodeMultiTopologyId; +import org.onosproject.bgpio.util.UnSupportedAttribute; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +/** + * Provides Implementation of Local node descriptors and prefix descriptors. + */ +public class BGPPrefixLSIdentifier { + + protected static final Logger log = LoggerFactory.getLogger(BGPPrefixLSIdentifier.class); + public static final int TYPE_AND_LEN = 4; + private NodeDescriptors localNodeDescriptors; + private LinkedList<BGPValueType> prefixDescriptor; + + /** + * Resets parameters. + */ + public BGPPrefixLSIdentifier() { + this.localNodeDescriptors = null; + this.prefixDescriptor = null; + } + + /** + * Constructor to initialize parameters. + * + * @param localNodeDescriptors Local node descriptors + * @param prefixDescriptor Prefix Descriptors + */ + public BGPPrefixLSIdentifier(NodeDescriptors localNodeDescriptors, LinkedList<BGPValueType> prefixDescriptor) { + this.localNodeDescriptors = localNodeDescriptors; + this.prefixDescriptor = prefixDescriptor; + } + + /** + * Reads the channel buffer and parses Prefix Identifier. + * + * @param cb ChannelBuffer + * @param protocolId protocol ID + * @return object of this class + * @throws BGPParseException while parsing Prefix Identifier + */ + public static BGPPrefixLSIdentifier parsePrefixIdendifier(ChannelBuffer cb, byte protocolId) + throws BGPParseException { + //Parse Local Node descriptor + NodeDescriptors localNodeDescriptors = new NodeDescriptors(); + localNodeDescriptors = parseLocalNodeDescriptors(cb, protocolId); + + //Parse Prefix descriptor + LinkedList<BGPValueType> prefixDescriptor = new LinkedList<>(); + prefixDescriptor = parsePrefixDescriptors(cb); + return new BGPPrefixLSIdentifier(localNodeDescriptors, prefixDescriptor); + } + + /** + * Parse local node descriptors. + * + * @param cb ChannelBuffer + * @param protocolId protocol identifier + * @return LocalNodeDescriptors + * @throws BGPParseException while parsing local node descriptors + */ + public static NodeDescriptors parseLocalNodeDescriptors(ChannelBuffer cb, byte protocolId) + throws BGPParseException { + ChannelBuffer tempBuf = cb; + short type = cb.readShort(); + short length = cb.readShort(); + if (cb.readableBytes() < length) { + //length + 4 implies data contains type, length and value + throw new BGPParseException(BGPErrorType.UPDATE_MESSAGE_ERROR, BGPErrorType.OPTIONAL_ATTRIBUTE_ERROR, + tempBuf.readBytes(cb.readableBytes() + TYPE_AND_LEN)); + } + NodeDescriptors localNodeDescriptors = new NodeDescriptors(); + ChannelBuffer tempCb = cb.readBytes(length); + + if (type == NodeDescriptors.LOCAL_NODE_DES_TYPE) { + localNodeDescriptors = NodeDescriptors.read(tempCb, length, type, protocolId); + } else { + throw new BGPParseException(BGPErrorType.UPDATE_MESSAGE_ERROR, + BGPErrorType.MALFORMED_ATTRIBUTE_LIST, null); + } + return localNodeDescriptors; + } + + /** + * Parse list of prefix descriptors. + * + * @param cb ChannelBuffer + * @return list of prefix descriptors + * @throws BGPParseException while parsing list of prefix descriptors + */ + public static LinkedList<BGPValueType> parsePrefixDescriptors(ChannelBuffer cb) throws BGPParseException { + LinkedList<BGPValueType> prefixDescriptor = new LinkedList<>(); + BGPValueType tlv = null; + boolean isIpReachInfo = false; + ChannelBuffer tempCb; + int count = 0; + + while (cb.readableBytes() > 0) { + ChannelBuffer tempBuf = cb; + short type = cb.readShort(); + short length = cb.readShort(); + if (cb.readableBytes() < length) { + //length + 4 implies data contains type, length and value + throw new BGPParseException(BGPErrorType.UPDATE_MESSAGE_ERROR, BGPErrorType.OPTIONAL_ATTRIBUTE_ERROR, + tempBuf.readBytes(cb.readableBytes() + TYPE_AND_LEN)); + } + tempCb = cb.readBytes(length); + switch (type) { + case OSPFRouteTypeTlv.TYPE: + tlv = OSPFRouteTypeTlv.read(tempCb); + break; + case IPReachabilityInformationTlv.TYPE: + tlv = IPReachabilityInformationTlv.read(tempCb, length); + isIpReachInfo = true; + break; + case BgpAttrNodeMultiTopologyId.ATTRNODE_MULTITOPOLOGY: + tlv = BgpAttrNodeMultiTopologyId.read(tempCb); + count = count + 1; + if (count > 1) { + //length + 4 implies data contains type, length and value + throw new BGPParseException(BGPErrorType.UPDATE_MESSAGE_ERROR, + BGPErrorType.OPTIONAL_ATTRIBUTE_ERROR, tempBuf.readBytes(length + TYPE_AND_LEN)); + } + break; + default: + UnSupportedAttribute.skipBytes(tempCb, length); + } + prefixDescriptor.add(tlv); + } + + if (!isIpReachInfo) { + throw new BGPParseException(BGPErrorType.UPDATE_MESSAGE_ERROR, BGPErrorType.OPTIONAL_ATTRIBUTE_ERROR, + null); + } + return prefixDescriptor; + } + + /** + * Returns local node descriptors. + * + * @return local node descriptors + */ + public NodeDescriptors getLocalNodeDescriptors() { + return this.localNodeDescriptors; + } + + /** + * Returns Prefix descriptors. + * + * @return Prefix descriptors + */ + public LinkedList<BGPValueType> getPrefixdescriptor() { + return this.prefixDescriptor; + } + + @Override + public int hashCode() { + return Objects.hash(prefixDescriptor.hashCode(), localNodeDescriptors); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof BGPPrefixLSIdentifier) { + int countObjSubTlv = 0; + int countOtherSubTlv = 0; + boolean isCommonSubTlv = true; + BGPPrefixLSIdentifier other = (BGPPrefixLSIdentifier) obj; + + Iterator<BGPValueType> objListIterator = other.prefixDescriptor.iterator(); + countOtherSubTlv = other.prefixDescriptor.size(); + countObjSubTlv = prefixDescriptor.size(); + if (countObjSubTlv != countOtherSubTlv) { + return false; + } else { + while (objListIterator.hasNext() && isCommonSubTlv) { + BGPValueType subTlv = objListIterator.next(); + isCommonSubTlv = Objects.equals(prefixDescriptor.contains(subTlv), + other.prefixDescriptor.contains(subTlv)); + } + return isCommonSubTlv && Objects.equals(this.localNodeDescriptors, other.localNodeDescriptors); + } + } + return false; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("localNodeDescriptors", localNodeDescriptors) + .add("prefixDescriptor", prefixDescriptor) + .toString(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/NodeDescriptors.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/NodeDescriptors.java new file mode 100644 index 00000000..a03b2bae --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/NodeDescriptors.java @@ -0,0 +1,225 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.protocol.link_state; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Objects; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onosproject.bgpio.exceptions.BGPParseException; +import org.onosproject.bgpio.types.AreaIDTlv; +import org.onosproject.bgpio.types.AutonomousSystemTlv; +import org.onosproject.bgpio.types.BGPErrorType; +import org.onosproject.bgpio.types.BGPLSIdentifierTlv; +import org.onosproject.bgpio.types.BGPValueType; +import org.onosproject.bgpio.types.IsIsNonPseudonode; +import org.onosproject.bgpio.types.IsIsPseudonode; +import org.onosproject.bgpio.types.OSPFNonPseudonode; +import org.onosproject.bgpio.types.OSPFPseudonode; +import org.onosproject.bgpio.util.UnSupportedAttribute; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +/** + * Provides Local and Remote NodeDescriptors which contains Node Descriptor Sub-TLVs. + */ +public class NodeDescriptors { + + /* + *Reference :draft-ietf-idr-ls-distribution-11 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + // Node Descriptor Sub-TLVs (variable) // + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Figure : Local or Remote Node Descriptors TLV format + */ + + protected static final Logger log = LoggerFactory.getLogger(NodeDescriptors.class); + + public static final short LOCAL_NODE_DES_TYPE = 256; + public static final short REMOTE_NODE_DES_TYPE = 257; + public static final short IGP_ROUTERID_TYPE = 515; + public static final short IS_IS_LEVEL_1_PROTOCOL_ID = 1; + public static final short IS_IS_LEVEL_2_PROTOCOL_ID = 2; + public static final short OSPF_V2_PROTOCOL_ID = 3; + public static final short OSPF_V3_PROTOCOL_ID = 6; + public static final int TYPE_AND_LEN = 4; + public static final int ISISNONPSEUDONODE_LEN = 6; + public static final int ISISPSEUDONODE_LEN = 7; + public static final int OSPFNONPSEUDONODE_LEN = 4; + public static final int OSPFPSEUDONODE_LEN = 8; + private LinkedList<BGPValueType> subTlvs; + private short deslength; + private short desType; + + /** + * Resets parameters. + */ + public NodeDescriptors() { + this.subTlvs = null; + this.deslength = 0; + this.desType = 0; + } + + /** + * Constructor to initialize parameters. + * + * @param subTlvs list of subTlvs + * @param deslength Descriptors length + * @param desType local node descriptor or remote node descriptor type + */ + public NodeDescriptors(LinkedList<BGPValueType> subTlvs, short deslength, short desType) { + this.subTlvs = subTlvs; + this.deslength = deslength; + this.desType = desType; + } + + /** + * Returns list of subTlvs. + * + * @return subTlvs list of subTlvs + */ + public LinkedList<BGPValueType> getSubTlvs() { + return subTlvs; + } + + @Override + public int hashCode() { + return Objects.hash(subTlvs.hashCode()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof NodeDescriptors) { + int countObjSubTlv = 0; + int countOtherSubTlv = 0; + boolean isCommonSubTlv = true; + NodeDescriptors other = (NodeDescriptors) obj; + Iterator<BGPValueType> objListIterator = other.subTlvs.iterator(); + countOtherSubTlv = other.subTlvs.size(); + countObjSubTlv = subTlvs.size(); + if (countObjSubTlv != countOtherSubTlv) { + return false; + } else { + while (objListIterator.hasNext() && isCommonSubTlv) { + BGPValueType subTlv = objListIterator.next(); + isCommonSubTlv = Objects.equals(subTlvs.contains(subTlv), other.subTlvs.contains(subTlv)); + } + return isCommonSubTlv; + } + } + return false; + } + + /** + * Reads node descriptors Sub-TLVs. + * + * @param cb ChannelBuffer + * @param desLength node descriptor length + * @param desType local node descriptor or remote node descriptor type + * @param protocolId protocol ID + * @return object of NodeDescriptors + * @throws BGPParseException while parsing node descriptors + */ + public static NodeDescriptors read(ChannelBuffer cb, short desLength, short desType, byte protocolId) + throws BGPParseException { + LinkedList<BGPValueType> subTlvs; + subTlvs = new LinkedList<>(); + BGPValueType tlv = null; + + while (cb.readableBytes() > 0) { + ChannelBuffer tempBuf = cb; + short type = cb.readShort(); + short length = cb.readShort(); + if (cb.readableBytes() < length) { + throw new BGPParseException(BGPErrorType.UPDATE_MESSAGE_ERROR, BGPErrorType.OPTIONAL_ATTRIBUTE_ERROR, + tempBuf.readBytes(cb.readableBytes() + TYPE_AND_LEN)); + } + ChannelBuffer tempCb = cb.readBytes(length); + switch (type) { + case AutonomousSystemTlv.TYPE: + tlv = AutonomousSystemTlv.read(tempCb); + break; + case BGPLSIdentifierTlv.TYPE: + tlv = BGPLSIdentifierTlv.read(tempCb); + break; + case AreaIDTlv.TYPE: + tlv = AreaIDTlv.read(tempCb); + break; + case IGP_ROUTERID_TYPE: + if (protocolId == IS_IS_LEVEL_1_PROTOCOL_ID || protocolId == IS_IS_LEVEL_2_PROTOCOL_ID) { + if (length == ISISNONPSEUDONODE_LEN) { + tlv = IsIsNonPseudonode.read(tempCb); + } else if (length == ISISPSEUDONODE_LEN) { + tlv = IsIsPseudonode.read(tempCb); + } + } else if (protocolId == OSPF_V2_PROTOCOL_ID || protocolId == OSPF_V3_PROTOCOL_ID) { + if (length == OSPFNONPSEUDONODE_LEN) { + tlv = OSPFNonPseudonode.read(tempCb); + } else if (length == OSPFPSEUDONODE_LEN) { + tlv = OSPFPseudonode.read(tempCb); + } + } + break; + default: + UnSupportedAttribute.skipBytes(tempCb, length); + } + subTlvs.add(tlv); + } + return new NodeDescriptors(subTlvs, desLength, desType); + } + + /** + * Returns node descriptors length. + * + * @return node descriptors length + */ + public short getLength() { + return this.deslength; + } + + /** + * Returns node descriptors type. + * + * @return node descriptors type + */ + public short getType() { + return this.desType; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("desType", desType) + .add("deslength", deslength) + .add("subTlvs", subTlvs) + .toString(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/package-info.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/package-info.java new file mode 100755 index 00000000..d2a2ccf3 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/link_state/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * BGP Protocol specific link state details. + */ +package org.onosproject.bgpio.protocol.link_state;
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/package-info.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/package-info.java new file mode 100755 index 00000000..723b31b1 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * BGP Protocol specific components. + */ +package org.onosproject.bgpio.protocol; diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPKeepaliveMsgVer4.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPKeepaliveMsgVer4.java new file mode 100644 index 00000000..a6668b3a --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPKeepaliveMsgVer4.java @@ -0,0 +1,172 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgpio.protocol.ver4; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onosproject.bgpio.exceptions.BGPParseException; +import org.onosproject.bgpio.protocol.BGPKeepaliveMsg; +import org.onosproject.bgpio.protocol.BGPMessageReader; +import org.onosproject.bgpio.protocol.BGPMessageWriter; +import org.onosproject.bgpio.types.BGPHeader; +import org.onosproject.bgpio.protocol.BGPType; +import org.onosproject.bgpio.protocol.BGPVersion; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +/** + * Provides BGP keep alive message. + */ +class BGPKeepaliveMsgVer4 implements BGPKeepaliveMsg { + + /* + <Keepalive Message>::= <Common Header> + A KEEPALIVE message consists of only the message header and has a + length of 19 octets. + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + + + | | + + + + | Marker | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length | Type | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + REFERENCE : RFC 4271 + */ + + protected static final Logger log = LoggerFactory + .getLogger(BGPKeepaliveMsgVer4.class); + + private BGPHeader bgpMsgHeader; + public static final byte PACKET_VERSION = 4; + public static final int PACKET_MINIMUM_LENGTH = 19; + public static final int MARKER_LENGTH = 16; + public static final BGPType MSG_TYPE = BGPType.KEEP_ALIVE; + public static byte[] marker = new byte[] {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}; + + public static final BGPKeepaliveMsgVer4.Reader READER = new Reader(); + + /** + * Reader class for reading BGP keepalive message from channel buffer. + */ + static class Reader implements BGPMessageReader<BGPKeepaliveMsg> { + + @Override + public BGPKeepaliveMsg readFrom(ChannelBuffer cb, BGPHeader bgpHeader) + throws BGPParseException { + + /* bgpHeader is not required in case of keepalive message and + Header is already read and no other fields except header in keepalive message.*/ + return new BGPKeepaliveMsgVer4(); + } + } + + /** + * Default constructor. + */ + BGPKeepaliveMsgVer4() { + } + + /** + * Builder class for BGP keepalive message. + */ + static class Builder implements BGPKeepaliveMsg.Builder { + BGPHeader bgpMsgHeader; + + @Override + public BGPVersion getVersion() { + return BGPVersion.BGP_4; + } + + @Override + public BGPType getType() { + return BGPType.KEEP_ALIVE; + } + + @Override + public BGPHeader getHeader() { + return this.bgpMsgHeader; + } + + @Override + public Builder setHeader(BGPHeader bgpMsgHeader) { + this.bgpMsgHeader = bgpMsgHeader; + return this; + } + + @Override + public BGPKeepaliveMsg build() { + return new BGPKeepaliveMsgVer4(); + } + } + + @Override + public void writeTo(ChannelBuffer cb) { + WRITER.write(cb, this); + } + + static final Writer WRITER = new Writer(); + + /** + * Writer class for writing the BGP keepalive message to channel buffer. + */ + static class Writer implements BGPMessageWriter<BGPKeepaliveMsgVer4> { + + @Override + public void write(ChannelBuffer cb, BGPKeepaliveMsgVer4 message) { + + // write marker + cb.writeBytes(marker, 0, MARKER_LENGTH); + + // write length of header + cb.writeShort(PACKET_MINIMUM_LENGTH); + + // write the type of message + cb.writeByte(MSG_TYPE.getType()); + } + } + + @Override + public BGPVersion getVersion() { + return BGPVersion.BGP_4; + } + + @Override + public BGPType getType() { + return MSG_TYPE; + } + + @Override + public BGPHeader getHeader() { + return this.bgpMsgHeader; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()).toString(); + } +} diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPOpenMsgVer4.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPOpenMsgVer4.java new file mode 100644 index 00000000..1348ecfd --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/BGPOpenMsgVer4.java @@ -0,0 +1,468 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.protocol.ver4; + +import java.util.LinkedList; +import java.util.ListIterator; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onosproject.bgpio.exceptions.BGPParseException; +import org.onosproject.bgpio.protocol.BGPMessageReader; +import org.onosproject.bgpio.protocol.BGPMessageWriter; +import org.onosproject.bgpio.protocol.BGPOpenMsg; +import org.onosproject.bgpio.protocol.BGPType; +import org.onosproject.bgpio.protocol.BGPVersion; +import org.onosproject.bgpio.types.BGPErrorType; +import org.onosproject.bgpio.types.BGPHeader; +import org.onosproject.bgpio.types.BGPValueType; +import org.onosproject.bgpio.util.Validation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +/** + * Provides BGP open message. + */ +public class BGPOpenMsgVer4 implements BGPOpenMsg { + + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+ + | Version | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | My Autonomous System | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Hold Time | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | BGP Identifier | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Opt Parm Len | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Optional Parameters (variable) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + OPEN Message Format + REFERENCE : RFC 4271 + */ + + protected static final Logger log = LoggerFactory.getLogger(BGPOpenMsgVer4.class); + + public static final byte PACKET_VERSION = 4; + public static final int OPEN_MSG_MINIMUM_LENGTH = 10; + public static final int MSG_HEADER_LENGTH = 19; + public static final int MARKER_LENGTH = 16; + public static final int DEFAULT_HOLD_TIME = 120; + public static final int OPT_PARA_TYPE_CAPABILITY = 2; + public static final BGPType MSG_TYPE = BGPType.OPEN; + public static final byte[] MARKER = new byte[]{(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}; + public static final BGPHeader DEFAULT_OPEN_HEADER = new BGPHeader(MARKER, + (short) OPEN_MSG_MINIMUM_LENGTH, (byte) 0X01); + private BGPHeader bgpMsgHeader; + private byte version; + private short asNumber; + private short holdTime; + private int bgpId; + private LinkedList<BGPValueType> capabilityTlv; + + public static final BGPOpenMsgVer4.Reader READER = new Reader(); + + /** + * reset variables. + */ + public BGPOpenMsgVer4() { + this.bgpMsgHeader = null; + this.version = 0; + this.holdTime = 0; + this.asNumber = 0; + this.bgpId = 0; + this.capabilityTlv = null; + } + + /** + * Constructor to initialize all variables of BGP Open message. + * + * @param bgpMsgHeader + * BGP Header in open message + * @param version + * BGP version in open message + * @param holdTime + * hold time in open message + * @param asNumber + * AS number in open message + * @param bgpId + * BGP identifier in open message + * @param capabilityTlv + * capabilities in open message + */ + public BGPOpenMsgVer4(BGPHeader bgpMsgHeader, byte version, short asNumber, short holdTime, + int bgpId, LinkedList<BGPValueType> capabilityTlv) { + this.bgpMsgHeader = bgpMsgHeader; + this.version = version; + this.asNumber = asNumber; + this.holdTime = holdTime; + this.bgpId = bgpId; + this.capabilityTlv = capabilityTlv; + } + + @Override + public BGPHeader getHeader() { + return this.bgpMsgHeader; + } + + @Override + public BGPVersion getVersion() { + return BGPVersion.BGP_4; + } + + @Override + public BGPType getType() { + return MSG_TYPE; + } + + @Override + public short getHoldTime() { + return this.holdTime; + } + + @Override + public short getAsNumber() { + return this.asNumber; + } + + @Override + public int getBgpId() { + return this.bgpId; + } + + @Override + public LinkedList<BGPValueType> getCapabilityTlv() { + return this.capabilityTlv; + } + + /** + * Reader class for reading BGP open message from channel buffer. + */ + public static class Reader implements BGPMessageReader<BGPOpenMsg> { + + @Override + public BGPOpenMsg readFrom(ChannelBuffer cb, BGPHeader bgpHeader) throws BGPParseException { + + byte version; + short holdTime; + short asNumber; + int bgpId; + byte optParaLen = 0; + byte optParaType; + byte capParaLen = 0; + LinkedList<BGPValueType> capabilityTlv = new LinkedList<>(); + + if (cb.readableBytes() < OPEN_MSG_MINIMUM_LENGTH) { + log.error("[readFrom] Invalid length: Packet size is less than the minimum length "); + Validation.validateLen(BGPErrorType.OPEN_MESSAGE_ERROR, BGPErrorType.BAD_MESSAGE_LENGTH, + cb.readableBytes()); + } + + // Read version + version = cb.readByte(); + if (version != PACKET_VERSION) { + log.error("[readFrom] Invalid version: " + version); + throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR, + BGPErrorType.UNSUPPORTED_VERSION_NUMBER, null); + } + + // Read AS number + asNumber = cb.readShort(); + + // Read Hold timer + holdTime = cb.readShort(); + + // Read BGP Identifier + bgpId = cb.readInt(); + + // Read optional parameter length + optParaLen = cb.readByte(); + + // Read Capabilities if optional parameter length is greater than 0 + if (optParaLen != 0) { + // Read optional parameter type + optParaType = cb.readByte(); + + // Read optional parameter length + capParaLen = cb.readByte(); + + if (cb.readableBytes() < capParaLen) { + throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR, (byte) 0, null); + } + + ChannelBuffer capaCb = cb.readBytes(capParaLen); + + // Parse capabilities only if optional parameter type is 2 + if ((optParaType == OPT_PARA_TYPE_CAPABILITY) && (capParaLen != 0)) { + capabilityTlv = parseCapabilityTlv(capaCb); + } else { + throw new BGPParseException(BGPErrorType.OPEN_MESSAGE_ERROR, + BGPErrorType.UNSUPPORTED_OPTIONAL_PARAMETER, null); + } + } + return new BGPOpenMsgVer4(bgpHeader, version, asNumber, holdTime, bgpId, capabilityTlv); + } + } + + /** + * Parsing capabilities. + * + * @param cb of type channel buffer + * @return capabilityTlv of open message + * @throws BGPParseException while parsing capabilities + */ + protected static LinkedList<BGPValueType> parseCapabilityTlv(ChannelBuffer cb) throws BGPParseException { + + LinkedList<BGPValueType> capabilityTlv = new LinkedList<>(); + + // TODO: Capability parsing + return capabilityTlv; + } + + /** + * Builder class for BGP open message. + */ + static class Builder implements BGPOpenMsg.Builder { + + private boolean isHeaderSet = false; + private BGPHeader bgpMsgHeader; + private boolean isHoldTimeSet = false; + private short holdTime; + private boolean isAsNumSet = false; + private short asNumber; + private boolean isBgpIdSet = false; + private int bgpId; + + LinkedList<BGPValueType> capabilityTlv = new LinkedList<>(); + + @Override + public BGPOpenMsg build() throws BGPParseException { + BGPHeader bgpMsgHeader = this.isHeaderSet ? this.bgpMsgHeader : DEFAULT_OPEN_HEADER; + short holdTime = this.isHoldTimeSet ? this.holdTime : DEFAULT_HOLD_TIME; + + if (!this.isAsNumSet) { + throw new BGPParseException("BGP AS number is not set (mandatory)"); + } + + if (!this.isBgpIdSet) { + throw new BGPParseException("BGPID is not set (mandatory)"); + } + + // TODO: capabilities build + + return new BGPOpenMsgVer4(bgpMsgHeader, PACKET_VERSION, this.asNumber, holdTime, this.bgpId, + this.capabilityTlv); + } + + @Override + public BGPHeader getHeader() { + return this.bgpMsgHeader; + } + + @Override + public Builder setHeader(BGPHeader bgpMsgHeader) { + this.bgpMsgHeader = bgpMsgHeader; + return this; + } + + @Override + public BGPVersion getVersion() { + return BGPVersion.BGP_4; + } + + @Override + public BGPType getType() { + return MSG_TYPE; + } + + @Override + public short getHoldTime() { + return this.holdTime; + } + + @Override + public short getAsNumber() { + return this.asNumber; + } + + @Override + public int getBgpId() { + return this.bgpId; + } + + @Override + public LinkedList<BGPValueType> getCapabilityTlv() { + return this.capabilityTlv; + } + + @Override + public Builder setHoldTime(short holdTime) { + this.holdTime = holdTime; + this.isHoldTimeSet = true; + return this; + } + + @Override + public Builder setAsNumber(short asNumber) { + this.asNumber = asNumber; + this.isAsNumSet = true; + return this; + } + + @Override + public Builder setBgpId(int bgpId) { + this.bgpId = bgpId; + this.isBgpIdSet = true; + return this; + } + + @Override + public Builder setCapabilityTlv(LinkedList<BGPValueType> capabilityTlv) { + this.capabilityTlv = capabilityTlv; + return this; + } + } + + @Override + public void writeTo(ChannelBuffer cb) { + try { + WRITER.write(cb, this); + } catch (BGPParseException e) { + log.debug("[writeTo] Error: " + e.toString()); + } + } + + public static final Writer WRITER = new Writer(); + + /** + * Writer class for writing BGP open message to channel buffer. + */ + public static class Writer implements BGPMessageWriter<BGPOpenMsgVer4> { + + @Override + public void write(ChannelBuffer cb, BGPOpenMsgVer4 message) throws BGPParseException { + + int optParaLen = 0; + + int startIndex = cb.writerIndex(); + + // write common header and get msg length index + int msgLenIndex = message.bgpMsgHeader.write(cb); + + if (msgLenIndex <= 0) { + throw new BGPParseException("Unable to write message header."); + } + + // write version in 1-octet + cb.writeByte(message.version); + + // TODO : Write AS number based on capabilities + cb.writeShort(message.asNumber); + + // write HoldTime in next 2-octet + cb.writeShort(message.holdTime); + + // write BGP Identifier in next 4-octet + cb.writeInt(message.bgpId); + + // store the index of Optional parameter length + int optParaLenIndex = cb.writerIndex(); + + // set optional parameter length as 0 + cb.writeByte(0); + + // Pack capability TLV + optParaLen = message.packCapabilityTlv(cb, message); + + if (optParaLen != 0) { + // Update optional parameter length + cb.setByte(optParaLenIndex, (byte) (optParaLen + 2)); //+2 for optional parameter type. + } + + // write OPEN Object Length + int length = cb.writerIndex() - startIndex; + cb.setShort(msgLenIndex, (short) length); + message.bgpMsgHeader.setLength((short) length); + } + } + + /** + * returns length of capability tlvs. + * + * @param cb of type channel buffer + * @param message of type BGPOpenMsgVer4 + * @return capParaLen of open message + */ + protected int packCapabilityTlv(ChannelBuffer cb, BGPOpenMsgVer4 message) { + int startIndex = cb.writerIndex(); + int capParaLen = 0; + int capParaLenIndex = 0; + + LinkedList<BGPValueType> capabilityTlv = message.capabilityTlv; + ListIterator<BGPValueType> listIterator = capabilityTlv.listIterator(); + + if (listIterator.hasNext()) { + // Set optional parameter type as 2 + cb.writeByte(OPT_PARA_TYPE_CAPABILITY); + + // Store the index of capability parameter length and update length at the end + capParaLenIndex = cb.writerIndex(); + + // Set capability parameter length as 0 + cb.writeByte(0); + + // Update the startIndex to know the length of capability tlv + startIndex = cb.writerIndex(); + } + + while (listIterator.hasNext()) { + BGPValueType tlv = listIterator.next(); + if (tlv == null) { + log.debug("Warning: tlv is null from CapabilityTlv list"); + continue; + } + tlv.write(cb); + } + + capParaLen = cb.writerIndex() - startIndex; + + if (capParaLen != 0) { + // Update capability parameter length + cb.setByte(capParaLenIndex, (byte) capParaLen); + } + return capParaLen; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("bgpMsgHeader", bgpMsgHeader) + .add("version", version) + .add("holdTime", holdTime) + .add("asNumber", asNumber) + .add("bgpId", bgpId) + .add("capabilityTlv", capabilityTlv) + .toString(); + } +} diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/package-info.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/package-info.java new file mode 100755 index 00000000..fb8c67c0 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/protocol/ver4/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * BGP Protocol specific details of version 4. + */ +package org.onosproject.bgpio.protocol.ver4;
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/AreaIDTlv.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/AreaIDTlv.java new file mode 100644 index 00000000..52bae466 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/AreaIDTlv.java @@ -0,0 +1,126 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgpio.types; + +import java.util.Objects; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +/** + * Provides AreaID Tlv which contains opaque value (32 Bit Area-ID). + */ +public class AreaIDTlv implements BGPValueType { + + /* Reference :draft-ietf-idr-ls-distribution-11 + * 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type= 514 | Length=4 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | opaque value (32 Bit Area-ID) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + protected static final Logger log = LoggerFactory.getLogger(AreaIDTlv.class); + + public static final short TYPE = 514; + public static final short LENGTH = 4; + + private final int areaID; + + /** + * Constructor to initialize areaID. + * + * @param areaID of BGP AreaID Tlv + */ + public AreaIDTlv(int areaID) { + this.areaID = areaID; + } + + /** + * Returns object of this class with specified areaID. + * + * @param areaID opaque value of area id + * @return object of AreaIDTlv + */ + public static AreaIDTlv of(final int areaID) { + return new AreaIDTlv(areaID); + } + + /** + * Returns opaque value of area id. + * + * @return opaque value of area id + */ + public int getAreaID() { + return areaID; + } + + @Override + public int hashCode() { + return Objects.hash(areaID); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof AreaIDTlv) { + AreaIDTlv other = (AreaIDTlv) obj; + return Objects.equals(areaID, other.areaID); + } + return false; + } + + @Override + public int write(ChannelBuffer c) { + int iLenStartIndex = c.writerIndex(); + c.writeShort(TYPE); + c.writeShort(LENGTH); + c.writeInt(areaID); + return c.writerIndex() - iLenStartIndex; + } + + /** + * Reads the channel buffer and returns object of AreaIDTlv. + * + * @param cb ChannelBuffer + * @return object of AreaIDTlv + */ + public static AreaIDTlv read(ChannelBuffer cb) { + return AreaIDTlv.of(cb.readInt()); + } + + @Override + public short getType() { + return TYPE; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("Type", TYPE) + .add("Length", LENGTH) + .add("Value", areaID) + .toString(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/AutonomousSystemTlv.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/AutonomousSystemTlv.java new file mode 100644 index 00000000..5d8a9193 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/AutonomousSystemTlv.java @@ -0,0 +1,126 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgpio.types; + +import java.util.Objects; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +/** + * Provides Autonomous System Tlv which contains opaque value (32 Bit AS Number). + */ +public class AutonomousSystemTlv implements BGPValueType { + + /* Reference :draft-ietf-idr-ls-distribution-11 + * 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type= 512 | Length=4 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | opaque value (32 Bit AS Number) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + protected static final Logger log = LoggerFactory.getLogger(AutonomousSystemTlv.class); + + public static final short TYPE = 512; + public static final short LENGTH = 4; + + private final int asNum; + + /** + * Constructor to initialize asNum. + * + * @param asNum 32 Bit AS Number + */ + public AutonomousSystemTlv(int asNum) { + this.asNum = asNum; + } + + /** + * Returns object of this class with specified asNum. + * + * @param asNum 32 Bit AS Number + * @return object of AutonomousSystemTlv + */ + public static AutonomousSystemTlv of(final int asNum) { + return new AutonomousSystemTlv(asNum); + } + + /** + * Returns opaque value of AS Number. + * + * @return opaque value of AS Number + */ + public int getAsNum() { + return asNum; + } + + @Override + public int hashCode() { + return Objects.hash(asNum); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof AutonomousSystemTlv) { + AutonomousSystemTlv other = (AutonomousSystemTlv) obj; + return Objects.equals(asNum, other.asNum); + } + return false; + } + + @Override + public int write(ChannelBuffer c) { + int iLenStartIndex = c.writerIndex(); + c.writeShort(TYPE); + c.writeShort(LENGTH); + c.writeInt(asNum); + return c.writerIndex() - iLenStartIndex; + } + + /** + * Reads the channel buffer and returns object of AutonomousSystemTlv. + * + * @param c ChannelBuffer + * @return object of AutonomousSystemTlv + */ + public static AutonomousSystemTlv read(ChannelBuffer c) { + return AutonomousSystemTlv.of(c.readInt()); + } + + @Override + public short getType() { + return TYPE; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("Type", TYPE) + .add("Length", LENGTH) + .add("asNum", asNum) + .toString(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPErrorType.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPErrorType.java new file mode 100644 index 00000000..f643ae00 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPErrorType.java @@ -0,0 +1,57 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.types; + +/** + * BgpErrorType class defines all errorCodes and error Subcodes required for Notification message. + */ +public final class BGPErrorType { + private BGPErrorType() { + } + + //Error Codes + public static final byte MESSAGE_HEADER_ERROR = 1; + public static final byte OPEN_MESSAGE_ERROR = 2; + public static final byte UPDATE_MESSAGE_ERROR = 3; + public static final byte HOLD_TIMER_EXPIRED = 4; + public static final byte FINITE_STATE_MACHINE_ERROR = 4; + public static final byte CEASE = 5; + + //Message Header Error subcodes + public static final byte CONNECTION_NOT_SYNCHRONIZED = 1; + public static final byte BAD_MESSAGE_LENGTH = 2; + public static final byte BAD_MESSAGE_TYPE = 3; + + //OPEN Message Error subcodes + public static final byte UNSUPPORTED_VERSION_NUMBER = 1; + public static final byte BAD_PEER_AS = 2; + public static final byte BAD_BGP_IDENTIFIER = 3; + public static final byte UNSUPPORTED_OPTIONAL_PARAMETER = 4; + public static final byte UNACCEPTABLE_HOLD_TIME = 5; + + //UPDATE Message Error subcodes + public static final byte MALFORMED_ATTRIBUTE_LIST = 1; + public static final byte UNRECOGNIZED_WELLKNOWN_ATTRIBUTE = 2; + public static final byte MISSING_WELLKNOWN_ATTRIBUTE = 3; + public static final byte ATTRIBUTE_FLAGS_ERROR = 4; + public static final byte ATTRIBUTE_LENGTH_ERROR = 5; + public static final byte INVALID_ORIGIN_ATTRIBUTE = 6; + public static final byte INVALID_NEXTHOP_ATTRIBUTE = 8; + public static final byte OPTIONAL_ATTRIBUTE_ERROR = 9; + public static final byte INVALID_NETWORK_FIELD = 10; + public static final byte MALFORMED_ASPATH = 11; +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPHeader.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPHeader.java new file mode 100755 index 00000000..6acda0d6 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPHeader.java @@ -0,0 +1,161 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgpio.types; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides BGP Message Header which is common for all the Messages. + */ + +public class BGPHeader { + + /* 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + + + + | | + + + + | Marker | + + + + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Length | Type | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + protected static final Logger log = LoggerFactory.getLogger(BGPHeader.class); + + public static final int MARKER_LENGTH = 16; + public static final short DEFAULT_HEADER_LENGTH = 19; + + private byte[] marker; + private byte type; + private short length; + + /** + * Reset fields. + */ + public BGPHeader() { + this.marker = null; + this.length = 0; + this.type = 0; + } + + /** + * Constructors to initialize parameters. + * + * @param marker field in BGP header + * @param length message length + * @param type message type + */ + public BGPHeader(byte[] marker, short length, byte type) { + this.marker = marker; + this.length = length; + this.type = type; + } + + /** + * Sets marker field. + * + * @param value marker field + */ + public void setMarker(byte[] value) { + this.marker = value; + } + + /** + * Sets message type. + * + * @param value message type + */ + public void setType(byte value) { + this.type = value; + } + + /** + * Sets message length. + * + * @param value message length + */ + public void setLength(short value) { + this.length = value; + } + + /** + * Returns message length. + * + * @return message length + */ + public short getLength() { + return this.length; + } + + /** + * Returns message marker. + * + * @return message marker + */ + public byte[] getMarker() { + return this.marker; + } + + /** + * Returns message type. + * + * @return message type + */ + public byte getType() { + return this.type; + } + + /** + * Writes Byte stream of BGP header to channel buffer. + * + * @param cb ChannelBuffer + * @return length index of message header + */ + public int write(ChannelBuffer cb) { + + cb.writeBytes(getMarker(), 0, MARKER_LENGTH); + + int headerLenIndex = cb.writerIndex(); + cb.writeShort((short) 0); + cb.writeByte(type); + + return headerLenIndex; + } + + /** + * Read from channel buffer and Returns BGP header. + * + * @param cb ChannelBuffer + * @return object of BGPHeader + */ + public static BGPHeader read(ChannelBuffer cb) { + + byte[] marker = new byte[MARKER_LENGTH]; + byte type; + short length; + cb.readBytes(marker, 0, MARKER_LENGTH); + length = cb.readShort(); + type = cb.readByte(); + return new BGPHeader(marker, length, type); + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPLSIdentifierTlv.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPLSIdentifierTlv.java new file mode 100644 index 00000000..f723d2ca --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPLSIdentifierTlv.java @@ -0,0 +1,127 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.types; + +import java.util.Objects; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +/** + * Provides BGPLSIdentifier Tlv which contains opaque value (32 Bit BGPLS-Identifier). + */ +public class BGPLSIdentifierTlv implements BGPValueType { + + /* Reference :draft-ietf-idr-ls-distribution-11 + * 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type= 513 | Length=4 | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | opaque value (32 Bit BGPLS-Identifier) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + protected static final Logger log = LoggerFactory.getLogger(BGPLSIdentifierTlv.class); + + public static final short TYPE = 513; + public static final short LENGTH = 4; + + private final int bgpLSIdentifier; + + /** + * Constructor to initialize bgpLSIdentifier. + * + * @param bgpLSIdentifier BgpLS-Identifier + */ + public BGPLSIdentifierTlv(int bgpLSIdentifier) { + this.bgpLSIdentifier = bgpLSIdentifier; + } + + /** + * Returns object of this class with specified rbgpLSIdentifier. + * + * @param bgpLSIdentifier BgpLS-Identifier + * @return BgpLS-Identifier + */ + public static BGPLSIdentifierTlv of(final int bgpLSIdentifier) { + return new BGPLSIdentifierTlv(bgpLSIdentifier); + } + + /** + * Returns opaque value of BgpLS-Identifier. + * + * @return opaque value of BgpLS-Identifier + */ + public int getBgpLSIdentifier() { + return bgpLSIdentifier; + } + + @Override + public int hashCode() { + return Objects.hash(bgpLSIdentifier); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof BGPLSIdentifierTlv) { + BGPLSIdentifierTlv other = (BGPLSIdentifierTlv) obj; + return Objects.equals(bgpLSIdentifier, other.bgpLSIdentifier); + } + return false; + } + + @Override + public int write(ChannelBuffer c) { + int iLenStartIndex = c.writerIndex(); + c.writeShort(TYPE); + c.writeShort(LENGTH); + c.writeInt(bgpLSIdentifier); + return c.writerIndex() - iLenStartIndex; + } + + /** + * Reads the channel buffer and parses BGPLS Identifier TLV. + * + * @param cb ChannelBuffer + * @return object of BGPLSIdentifierTlv + */ + public static BGPLSIdentifierTlv read(ChannelBuffer cb) { + return BGPLSIdentifierTlv.of(cb.readInt()); + } + + @Override + public short getType() { + return TYPE; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("Type", TYPE) + .add("Length", LENGTH) + .add("Value", bgpLSIdentifier) + .toString(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPValueType.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPValueType.java new file mode 100755 index 00000000..54a8b43c --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/BGPValueType.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.types; + +import org.jboss.netty.buffer.ChannelBuffer; + +/** + * Abstraction which Provides the BGP of TLV format. + */ +public interface BGPValueType { + /** + * Returns the Type of BGP Message. + * + * @return short value of type + */ + short getType(); + + /** + * Writes the byte Stream of BGP Message to channel buffer. + * + * @param cb channel buffer + * @return length written to channel buffer + */ + int write(ChannelBuffer cb); +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IPReachabilityInformationTlv.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IPReachabilityInformationTlv.java new file mode 100644 index 00000000..85fb748b --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IPReachabilityInformationTlv.java @@ -0,0 +1,156 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgpio.types; + +import java.util.Objects; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onlab.packet.IpPrefix; +import org.onosproject.bgpio.util.Validation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +/** + * Provides IP Reachability InformationTlv Tlv which contains IP Prefix. + */ +public class IPReachabilityInformationTlv implements BGPValueType { + + /* + * Reference :draft-ietf-idr-ls-distribution-11 + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Prefix Length | IP Prefix (variable) // + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Figure 14: IP Reachability Information TLV Format + */ + + protected static final Logger log = LoggerFactory.getLogger(IPReachabilityInformationTlv.class); + + public static final short TYPE = 265; + public static final int ONE_BYTE_LEN = 8; + private byte prefixLen; + private byte[] ipPrefix; + public short length; + + /** + * Constructor to initialize parameters. + * + * @param prefixLen length of IP Prefix + * @param ipPrefix IP Prefix + * @param length length of value field + */ + public IPReachabilityInformationTlv(byte prefixLen, byte[] ipPrefix, short length) { + this.ipPrefix = ipPrefix; + this.prefixLen = prefixLen; + this.length = length; + } + + /** + * Returns IP Prefix. + * + * @return IP Prefix + */ + public IpPrefix getPrefixValue() { + IpPrefix prefix = Validation.bytesToPrefix(ipPrefix, prefixLen); + return prefix; + } + + /** + * Returns IP Prefix length. + * + * @return IP Prefix length + */ + public byte getPrefixLen() { + return this.prefixLen; + } + + @Override + public int hashCode() { + return Objects.hash(ipPrefix, prefixLen); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof IPReachabilityInformationTlv) { + IPReachabilityInformationTlv other = (IPReachabilityInformationTlv) obj; + return Objects.equals(prefixLen, other.prefixLen) && Objects.equals(ipPrefix, other.ipPrefix); + } + return false; + } + + @Override + public int write(ChannelBuffer cb) { + int iLenStartIndex = cb.writerIndex(); + cb.writeShort(TYPE); + cb.writeShort(length); + cb.writeByte(prefixLen); + cb.writeBytes(ipPrefix); + return cb.writerIndex() - iLenStartIndex; + } + + /** + * Reads the channel buffer and returns object of IPReachabilityInformationTlv. + * + * @param cb ChannelBuffer + * @param length of value field + * @return object of IPReachabilityInformationTlv + */ + public static IPReachabilityInformationTlv read(ChannelBuffer cb, short length) { + byte preficLen = cb.readByte(); + byte[] prefix; + if (preficLen == 0) { + prefix = new byte[] {0}; + } else { + int len = preficLen / ONE_BYTE_LEN; + int reminder = preficLen % ONE_BYTE_LEN; + if (reminder > 0) { + len = len + 1; + } + prefix = new byte[len]; + cb.readBytes(prefix, 0, len); + } + return IPReachabilityInformationTlv.of(preficLen, prefix, length); + } + + public static IPReachabilityInformationTlv of(final byte preficLen, final byte[] prefix, final short length) { + return new IPReachabilityInformationTlv(preficLen, prefix, length); + } + @Override + public short getType() { + return TYPE; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("Type", TYPE) + .add("Length", length) + .add("Prefixlength", getPrefixLen()) + .add("Prefixvalue", getPrefixValue()) + .toString(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IsIsNonPseudonode.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IsIsNonPseudonode.java new file mode 100644 index 00000000..d5f3e7f3 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IsIsNonPseudonode.java @@ -0,0 +1,117 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgpio.types; + +import java.util.Objects; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onosproject.bgpio.protocol.IGPRouterID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +/** + * Provides Implementation of IsIsNonPseudonode Tlv. + */ +public class IsIsNonPseudonode implements IGPRouterID, BGPValueType { + protected static final Logger log = LoggerFactory.getLogger(IsIsNonPseudonode.class); + + public static final short TYPE = 515; + public static final short LENGTH = 6; + + private final byte[] isoNodeID; + + /** + * Constructor to initialize isoNodeID. + * + * @param isoNodeID ISO system-ID + */ + public IsIsNonPseudonode(byte[] isoNodeID) { + this.isoNodeID = isoNodeID; + } + + /** + * Returns object of this class with specified isoNodeID. + * + * @param isoNodeID ISO system-ID + * @return object of IsIsNonPseudonode + */ + public static IsIsNonPseudonode of(final byte[] isoNodeID) { + return new IsIsNonPseudonode(isoNodeID); + } + + /** + * Returns ISO NodeID. + * + * @return ISO NodeID + */ + public byte[] getISONodeID() { + return isoNodeID; + } + + @Override + public int hashCode() { + return Objects.hash(isoNodeID); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof IsIsNonPseudonode) { + IsIsNonPseudonode other = (IsIsNonPseudonode) obj; + return Objects.equals(isoNodeID, other.isoNodeID); + } + return false; + } + + @Override + public int write(ChannelBuffer c) { + int iLenStartIndex = c.writerIndex(); + c.writeShort(TYPE); + c.writeShort(LENGTH); + c.writeBytes(isoNodeID); + return c.writerIndex() - iLenStartIndex; + } + + /** + * Reads the channel buffer and returns object of IsIsNonPseudonode. + * + * @param cb ChannelBuffer + * @return object of IsIsNonPseudonode + */ + public static IsIsNonPseudonode read(ChannelBuffer cb) { + byte[] isoNodeID = new byte[LENGTH]; + cb.readBytes(isoNodeID, 0, LENGTH); + return IsIsNonPseudonode.of(isoNodeID); + } + + @Override + public short getType() { + return TYPE; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("Type", TYPE) + .add("Length", LENGTH) + .add("ISONodeID", isoNodeID) + .toString(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IsIsPseudonode.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IsIsPseudonode.java new file mode 100644 index 00000000..5c742d0f --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/IsIsPseudonode.java @@ -0,0 +1,133 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgpio.types; + +import java.util.Objects; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onosproject.bgpio.protocol.IGPRouterID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +/** + * Provides implementation of IsIsPseudonode Tlv. + */ +public class IsIsPseudonode implements IGPRouterID, BGPValueType { + protected static final Logger log = LoggerFactory.getLogger(IsIsPseudonode.class); + + public static final short TYPE = 515; + public static final short LENGTH = 7; + + private final byte[] isoNodeID; + private byte psnIdentifier; + + /** + * Constructor to initialize isoNodeID. + * + * @param isoNodeID ISO system-ID + * @param psnIdentifier PSN identifier + */ + public IsIsPseudonode(byte[] isoNodeID, byte psnIdentifier) { + this.isoNodeID = isoNodeID; + this.psnIdentifier = psnIdentifier; + } + + /** + * Returns object of this class with specified values. + * + * @param isoNodeID ISO system-ID + * @param psnIdentifier PSN identifier + * @return object of IsIsPseudonode + */ + public static IsIsPseudonode of(final byte[] isoNodeID, final byte psnIdentifier) { + return new IsIsPseudonode(isoNodeID, psnIdentifier); + } + + /** + * Returns ISO NodeID. + * + * @return ISO NodeID + */ + public byte[] getISONodeID() { + return isoNodeID; + } + + /** + * Returns PSN Identifier. + * + * @return PSN Identifier + */ + public byte getPSNIdentifier() { + return this.psnIdentifier; + } + + @Override + public int hashCode() { + return Objects.hash(isoNodeID, psnIdentifier); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof IsIsPseudonode) { + IsIsPseudonode other = (IsIsPseudonode) obj; + return Objects.equals(isoNodeID, other.isoNodeID) && Objects.equals(psnIdentifier, other.psnIdentifier); + } + return false; + } + + @Override + public int write(ChannelBuffer c) { + int iLenStartIndex = c.writerIndex(); + c.writeShort(TYPE); + c.writeShort(LENGTH); + c.writeBytes(isoNodeID); + c.writeByte(psnIdentifier); + return c.writerIndex() - iLenStartIndex; + } + + /** + * Reads the channel buffer and returns object of IsIsPseudonode. + * + * @param cb ChannelBuffer + * @return object of IsIsPseudonode + */ + public static IsIsPseudonode read(ChannelBuffer cb) { + byte[] isoNodeID = new byte[LENGTH - 1]; + cb.readBytes(isoNodeID, 0, LENGTH - 1); + byte psnIdentifier = cb.readByte(); + return IsIsPseudonode.of(isoNodeID, psnIdentifier); + } + + @Override + public short getType() { + return TYPE; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("Type", TYPE) + .add("Length", LENGTH) + .add("isoNodeID", isoNodeID) + .add("psnIdentifier", psnIdentifier) + .toString(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFNonPseudonode.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFNonPseudonode.java new file mode 100644 index 00000000..6d8282b7 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFNonPseudonode.java @@ -0,0 +1,118 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.types; + +import java.util.Objects; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onosproject.bgpio.protocol.IGPRouterID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +/** + * Provides implementation of OSPFNonPseudonode Tlv. + */ +public class OSPFNonPseudonode implements IGPRouterID, BGPValueType { + + protected static final Logger log = LoggerFactory.getLogger(OSPFNonPseudonode.class); + + public static final short TYPE = 515; + public static final short LENGTH = 4; + + private final int routerID; + + /** + * Constructor to initialize routerID. + * + * @param routerID routerID + */ + public OSPFNonPseudonode(int routerID) { + this.routerID = routerID; + } + + /** + * Returns object of this class with specified routerID. + * + * @param routerID routerID + * @return object of OSPFNonPseudonode + */ + public static OSPFNonPseudonode of(final int routerID) { + return new OSPFNonPseudonode(routerID); + } + + /** + * Returns RouterID. + * + * @return RouterID + */ + public int getrouterID() { + return this.routerID; + } + + @Override + public int hashCode() { + return Objects.hash(routerID); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof OSPFNonPseudonode) { + OSPFNonPseudonode other = (OSPFNonPseudonode) obj; + return Objects.equals(routerID, other.routerID); + } + return false; + } + + @Override + public int write(ChannelBuffer c) { + int iLenStartIndex = c.writerIndex(); + c.writeShort(TYPE); + c.writeShort(LENGTH); + c.writeInt(routerID); + return c.writerIndex() - iLenStartIndex; + } + + /** + * Reads the channel buffer and returns object of OSPFNonPseudonode. + * + * @param cb ChannelBuffer + * @return object of OSPFNonPseudonode + */ + public static OSPFNonPseudonode read(ChannelBuffer cb) { + return OSPFNonPseudonode.of(cb.readInt()); + } + + @Override + public short getType() { + return TYPE; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("Type", TYPE) + .add("Length", LENGTH) + .add("RouterID", routerID) + .toString(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFPseudonode.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFPseudonode.java new file mode 100644 index 00000000..82a39bd1 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFPseudonode.java @@ -0,0 +1,125 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgpio.types; + +import java.util.Objects; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onlab.packet.Ip4Address; +import org.onosproject.bgpio.protocol.IGPRouterID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +/** + * Provides implementation of OSPFPseudonode Tlv. + */ +public class OSPFPseudonode implements IGPRouterID, BGPValueType { + + protected static final Logger log = LoggerFactory.getLogger(OSPFPseudonode.class); + + public static final short TYPE = 515; + public static final short LENGTH = 8; + + private final int routerID; + private final Ip4Address drInterface; + + /** + * Constructor to initialize parameters. + * + * @param routerID routerID + * @param drInterface IPv4 address of the DR's interface + */ + public OSPFPseudonode(int routerID, Ip4Address drInterface) { + this.routerID = routerID; + this.drInterface = drInterface; + } + + /** + * Returns object of this class with specified values. + * + * @param routerID routerID + * @param drInterface IPv4 address of the DR's interface + * @return object of OSPFPseudonode + */ + public static OSPFPseudonode of(final int routerID, final Ip4Address drInterface) { + return new OSPFPseudonode(routerID, drInterface); + } + + /** + * Returns RouterID. + * + * @return RouterID + */ + public int getrouterID() { + return this.routerID; + } + + @Override + public int hashCode() { + return Objects.hash(routerID, drInterface); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof OSPFPseudonode) { + OSPFPseudonode other = (OSPFPseudonode) obj; + return Objects.equals(routerID, other.routerID) && Objects.equals(drInterface, other.drInterface); + } + return false; + } + + @Override + public int write(ChannelBuffer c) { + int iLenStartIndex = c.writerIndex(); + c.writeShort(TYPE); + c.writeShort(LENGTH); + c.writeInt(routerID); + c.writeInt(drInterface.toInt()); + return c.writerIndex() - iLenStartIndex; + } + + /** + * Reads the channel buffer and returns object of OSPFPseudonode. + * + * @param cb ChannelBuffer + * @return object of OSPFPseudonode + */ + public static OSPFPseudonode read(ChannelBuffer cb) { + int routerID = cb.readInt(); + Ip4Address drInterface = Ip4Address.valueOf(cb.readInt()); + return OSPFPseudonode.of(routerID, drInterface); + } + + @Override + public short getType() { + return TYPE; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("Type", TYPE) + .add("Length", LENGTH) + .add("RouterID", routerID) + .add("DRInterface", drInterface) + .toString(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFRouteTypeTlv.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFRouteTypeTlv.java new file mode 100644 index 00000000..20fffbfa --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/OSPFRouteTypeTlv.java @@ -0,0 +1,163 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgpio.types; + +import java.util.Objects; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onosproject.bgpio.exceptions.BGPParseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +/** + * Provides OSPF Route Type Tlv which contains route type. + */ +public class OSPFRouteTypeTlv implements BGPValueType { + + /* Reference :draft-ietf-idr-ls-distribution-11 + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Type | Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Route Type | + +-+-+-+-+-+-+-+-+ + + Figure : OSPF Route Type TLV Format + */ + + protected static final Logger log = LoggerFactory.getLogger(OSPFRouteTypeTlv.class); + + public static final short TYPE = 264; + public static final short LENGTH = 1; + public static final int INTRA_AREA_TYPE = 1; + public static final short INTER_AREA_TYPE = 2; + public static final short EXTERNAL_TYPE_1 = 3; + public static final short EXTERNAL_TYPE_2 = 4; + public static final short NSSA_TYPE_1 = 5; + public static final short NSSA_TYPE_2 = 6; + private final byte routeType; + + /** + * Enum for Route Type. + */ + public enum ROUTETYPE { + Intra_Area(1), Inter_Area(2), External_1(3), External_2(4), NSSA_1(5), NSSA_2(6); + int value; + ROUTETYPE(int val) { + value = val; + } + public byte getType() { + return (byte) value; + } + } + + /** + * Constructor to initialize routeType. + * + * @param routeType Route type + */ + public OSPFRouteTypeTlv(byte routeType) { + this.routeType = routeType; + } + + /** + * Returns object of this class with specified routeType. + * + * @param routeType Route type + * @return object of OSPFRouteTypeTlv + */ + public static OSPFRouteTypeTlv of(final byte routeType) { + return new OSPFRouteTypeTlv(routeType); + } + + /** + * Returns RouteType. + * + * @return RouteType + * @throws BGPParseException if routeType is not matched + */ + public ROUTETYPE getValue() throws BGPParseException { + switch (routeType) { + case INTRA_AREA_TYPE: + return ROUTETYPE.Intra_Area; + case INTER_AREA_TYPE: + return ROUTETYPE.Inter_Area; + case EXTERNAL_TYPE_1: + return ROUTETYPE.External_1; + case EXTERNAL_TYPE_2: + return ROUTETYPE.External_2; + case NSSA_TYPE_1: + return ROUTETYPE.NSSA_1; + case NSSA_TYPE_2: + return ROUTETYPE.NSSA_2; + default: + throw new BGPParseException(BGPErrorType.UPDATE_MESSAGE_ERROR, (byte) 0, null); + } + } + + @Override + public int hashCode() { + return Objects.hash(routeType); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof OSPFRouteTypeTlv) { + OSPFRouteTypeTlv other = (OSPFRouteTypeTlv) obj; + return Objects.equals(routeType, other.routeType); + } + return false; + } + + @Override + public int write(ChannelBuffer c) { + int iLenStartIndex = c.writerIndex(); + c.writeShort(TYPE); + c.writeShort(LENGTH); + c.writeByte(routeType); + return c.writerIndex() - iLenStartIndex; + } + + /** + * Reads from ChannelBuffer and parses OSPFRouteTypeTlv. + * + * @param cb channelBuffer + * @return object of OSPFRouteTypeTlv + */ + public static OSPFRouteTypeTlv read(ChannelBuffer cb) { + return OSPFRouteTypeTlv.of(cb.readByte()); + } + + @Override + public short getType() { + return TYPE; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("Type", TYPE) + .add("Length", LENGTH) + .add("Value", routeType) + .toString(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/BgpAttrNodeFlagBitTlv.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/BgpAttrNodeFlagBitTlv.java new file mode 100755 index 00000000..ba02f6d1 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/BgpAttrNodeFlagBitTlv.java @@ -0,0 +1,177 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgpio.types.attr; + +import java.util.Objects; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onosproject.bgpio.exceptions.BGPParseException; +import org.onosproject.bgpio.types.BGPErrorType; +import org.onosproject.bgpio.types.BGPValueType; +import org.onosproject.bgpio.util.Validation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +/** + * Implements BGP attribute node flag. + */ +public class BgpAttrNodeFlagBitTlv implements BGPValueType { + + protected static final Logger log = LoggerFactory + .getLogger(BgpAttrNodeFlagBitTlv.class); + + public static final int ATTRNODE_FLAGBIT = 1024; + + /* Node flag bit TLV */ + private boolean bOverloadBit; + private boolean bAttachedBit; + private boolean bExternalBit; + private boolean bABRBit; + + public static final int BIT_SET = 1; + public static final int FIRST_BIT = 0x80; + public static final int SECOND_BIT = 0x40; + public static final int THIRD_BIT = 0x20; + public static final int FOURTH_BIT = 0x01; + + /** + * Constructor to initialize parameters. + * + * @param bOverloadBit Overload bit + * @param bAttachedBit Attached bit + * @param bExternalBit External bit + * @param bABRBit ABR Bit + */ + BgpAttrNodeFlagBitTlv(boolean bOverloadBit, boolean bAttachedBit, + boolean bExternalBit, boolean bABRBit) { + this.bOverloadBit = bOverloadBit; + this.bAttachedBit = bAttachedBit; + this.bExternalBit = bExternalBit; + this.bABRBit = bABRBit; + } + + /** + * Reads the Node Flag Bits. + * + * @param cb ChannelBuffer + * @return attribute node flag bit tlv + * @throws BGPParseException while parsing BgpAttrNodeFlagBitTlv + */ + public static BgpAttrNodeFlagBitTlv read(ChannelBuffer cb) + throws BGPParseException { + boolean bOverloadBit = false; + boolean bAttachedBit = false; + boolean bExternalBit = false; + boolean bABRBit = false; + + short lsAttrLength = cb.readShort(); + + if (lsAttrLength != 1) { + Validation.validateLen(BGPErrorType.UPDATE_MESSAGE_ERROR, + BGPErrorType.ATTRIBUTE_LENGTH_ERROR, + lsAttrLength); + } + + byte nodeFlagBits = cb.readByte(); + + bOverloadBit = ((nodeFlagBits & (byte) FIRST_BIT) == FIRST_BIT); + bAttachedBit = ((nodeFlagBits & (byte) SECOND_BIT) == SECOND_BIT); + bExternalBit = ((nodeFlagBits & (byte) THIRD_BIT) == THIRD_BIT); + bABRBit = ((nodeFlagBits & (byte) FOURTH_BIT) == FOURTH_BIT); + + return new BgpAttrNodeFlagBitTlv(bOverloadBit, bAttachedBit, + bExternalBit, bABRBit); + } + + /** + * Returns Overload Bit. + * + * @return Overload Bit + */ + boolean getOverLoadBit() { + return bOverloadBit; + } + + /** + * Returns Attached Bit. + * + * @return Attached Bit + */ + boolean getAttachedBit() { + return bAttachedBit; + } + + /** + * Returns External Bit. + * + * @return External Bit + */ + boolean getExternalBit() { + return bExternalBit; + } + + /** + * Returns ABR Bit. + * + * @return ABR Bit + */ + boolean getABRBit() { + return bABRBit; + } + + @Override + public short getType() { + return ATTRNODE_FLAGBIT; + } + + @Override + public int write(ChannelBuffer cb) { + // TODO will be implementing it later + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(bOverloadBit, bAttachedBit, bExternalBit, bABRBit); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof BgpAttrNodeFlagBitTlv) { + BgpAttrNodeFlagBitTlv other = (BgpAttrNodeFlagBitTlv) obj; + return Objects.equals(bOverloadBit, other.bOverloadBit) + && Objects.equals(bAttachedBit, other.bAttachedBit) + && Objects.equals(bExternalBit, other.bExternalBit) + && Objects.equals(bABRBit, other.bABRBit); + } + return false; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("bOverloadBit", bOverloadBit) + .add("bAttachedBit", bAttachedBit) + .add("bExternalBit", bExternalBit).add("bABRBit", bABRBit) + .toString(); + } +} diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/BgpAttrNodeMultiTopologyId.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/BgpAttrNodeMultiTopologyId.java new file mode 100644 index 00000000..194d6dac --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/BgpAttrNodeMultiTopologyId.java @@ -0,0 +1,127 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.types.attr; + +import java.util.Objects; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.onosproject.bgpio.exceptions.BGPParseException; +import org.onosproject.bgpio.types.BGPErrorType; +import org.onosproject.bgpio.types.BGPValueType; +import org.onosproject.bgpio.util.Validation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.MoreObjects; + +/** + * BGP Multi-Topology ID of the LS attribute. + */ +public class BgpAttrNodeMultiTopologyId implements BGPValueType { + + private static final Logger log = LoggerFactory + .getLogger(BgpAttrNodeMultiTopologyId.class); + + public static final int ATTRNODE_MULTITOPOLOGY = 263; + + /* Opaque Node Attribute */ + private short[] multiTopologyId; + + /** + * Constructor to initialize the Node attribute multi-topology ID. + * + * @param multiTopologyId multi-topology ID + */ + BgpAttrNodeMultiTopologyId(short[] multiTopologyId) { + this.multiTopologyId = multiTopologyId; + } + + /** + * Reads the Multi-topology ID of Node attribute. + * + * @param cb ChannelBuffer + * @return Constructor of BgpAttrNodeMultiTopologyId + * @throws BGPParseException while parsing BgpAttrNodeMultiTopologyId + */ + public static BgpAttrNodeMultiTopologyId read(ChannelBuffer cb) + throws BGPParseException { + + log.debug("BgpAttrNodeMultiTopologyId"); + short lsAttrLength = cb.readShort(); + int len = lsAttrLength / 2; // Length is 2*n and n is the number of MT-IDs + + if (cb.readableBytes() < lsAttrLength) { + Validation.validateLen(BGPErrorType.UPDATE_MESSAGE_ERROR, + BGPErrorType.ATTRIBUTE_LENGTH_ERROR, + cb.readableBytes()); + } + + short[] multiTopologyId; + multiTopologyId = new short[len]; + for (int i = 0; i < len; i++) { + multiTopologyId[i] = cb.readShort(); + } + + return new BgpAttrNodeMultiTopologyId(multiTopologyId); + } + + /** + * to get the multi-topology ID. + * + * @return multitopology ID + */ + short[] getAttrMultiTopologyId() { + return multiTopologyId; + } + + @Override + public short getType() { + return ATTRNODE_MULTITOPOLOGY; + } + + @Override + public int hashCode() { + return Objects.hash(multiTopologyId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof BgpAttrNodeMultiTopologyId) { + BgpAttrNodeMultiTopologyId other = (BgpAttrNodeMultiTopologyId) obj; + return Objects.equals(multiTopologyId, other.multiTopologyId); + } + return false; + } + + @Override + public int write(ChannelBuffer cb) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .omitNullValues() + .add("multiTopologyId", multiTopologyId) + .toString(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/package-info.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/package-info.java new file mode 100755 index 00000000..e2a74dbc --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/attr/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Implementation of BGP Link state attribute Tlvs. + */ +package org.onosproject.bgpio.types.attr; diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/package-info.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/package-info.java new file mode 100755 index 00000000..1f2ed95e --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/types/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Implementation of Tlvs, Attributes and Descriptors. + */ +package org.onosproject.bgpio.types; diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/UnSupportedAttribute.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/UnSupportedAttribute.java new file mode 100644 index 00000000..663b1e9a --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/UnSupportedAttribute.java @@ -0,0 +1,51 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.util; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides methods to handle UnSupportedAttribute. + */ +public final class UnSupportedAttribute { + protected static final Logger log = LoggerFactory.getLogger(UnSupportedAttribute.class); + + private UnSupportedAttribute() { + } + + /** + * Reads channel buffer parses attribute header and skips specified length. + * + * @param cb channelBuffer + */ + public static void read(ChannelBuffer cb) { + Validation parseFlags = Validation.parseAttributeHeader(cb); + cb.skipBytes(parseFlags.getLength()); + } + + /** + * Skip specified bytes in channel buffer. + * + * @param cb channelBuffer + * @param length to be skipped + */ + public static void skipBytes(ChannelBuffer cb, short length) { + cb.skipBytes(length); + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/Validation.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/Validation.java new file mode 100644 index 00000000..915aa580 --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/Validation.java @@ -0,0 +1,189 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgpio.util; + +import java.util.Arrays; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.onlab.packet.IpAddress; +import org.onlab.packet.IpPrefix; +import org.onosproject.bgpio.exceptions.BGPParseException; + +import com.google.common.primitives.Ints; + +/** + * Provides methods to parse attribute header, validate length and type. + */ +public class Validation { + public static final byte FIRST_BIT = (byte) 0x80; + public static final byte SECOND_BIT = 0x40; + public static final byte THIRD_BIT = 0x20; + public static final byte FOURTH_BIT = 0x01; + public static final byte IPV4_SIZE = 4; + private boolean firstBit; + private boolean secondBit; + private boolean thirdBit; + private boolean fourthBit; + private int len; + private boolean isShort; + + Validation(boolean firstBit, boolean secondBit, boolean thirdBit, boolean fourthBit, int len, boolean isShort) { + this.firstBit = firstBit; + this.secondBit = secondBit; + this.thirdBit = thirdBit; + this.fourthBit = fourthBit; + this.len = len; + this.isShort = isShort; + } + + /** + * Parses attribute Header. + * + * @param cb ChannelBuffer + * @return object of Validation + */ + public static Validation parseAttributeHeader(ChannelBuffer cb) { + + boolean firstBit; + boolean secondBit; + boolean thirdBit; + boolean fourthBit; + boolean isShort; + byte flags = cb.readByte(); + byte typeCode = cb.readByte(); + byte temp = flags; + //first Bit : Optional (1) or well-known (0) + firstBit = ((temp & FIRST_BIT) == FIRST_BIT); + //second Bit : Transitive (1) or non-Transitive (0) + secondBit = ((temp & SECOND_BIT) == SECOND_BIT); + //third Bit : partial (1) or complete (0) + thirdBit = ((temp & THIRD_BIT) == THIRD_BIT); + //forth Bit(Extended Length bit) : Attribute Length is 1 octects (0) or 2 octects (1) + fourthBit = ((temp & FOURTH_BIT) == FOURTH_BIT); + int len; + if (fourthBit) { + isShort = true; + short length = cb.readShort(); + len = length; + } else { + isShort = false; + byte length = cb.readByte(); + len = length; + } + return new Validation(firstBit, secondBit, thirdBit, fourthBit, len, isShort); + } + + /** + * Throws exception if length is not correct. + * + * @param errorCode Error code + * @param subErrCode Sub Error Code + * @param length erroneous length + * @throws BGPParseException for erroneous length + */ + public static void validateLen(byte errorCode, byte subErrCode, int length) throws BGPParseException { + byte[] errLen = Ints.toByteArray(length); + ChannelBuffer buffer = ChannelBuffers.dynamicBuffer(); + buffer.writeBytes(errLen); + throw new BGPParseException(errorCode, subErrCode, buffer); + } + + /** + * Throws exception if type is not correct. + * + * @param errorCode Error code + * @param subErrCode Sub Error Code + * @param type erroneous type + * @throws BGPParseException for erroneous type + */ + public static void validateType(byte errorCode, byte subErrCode, int type) throws BGPParseException { + byte[] errType = Ints.toByteArray(type); + ChannelBuffer buffer = ChannelBuffers.dynamicBuffer(); + buffer.writeBytes(errType); + throw new BGPParseException(errorCode, subErrCode, buffer); + } + + /** + * Returns first bit in type flags. + * + * @return first bit in type flags + */ + public boolean getFirstBit() { + return this.firstBit; + } + + /** + * Returns second bit in type flags. + * + * @return second bit in type flags + */ + public boolean getSecondBit() { + return this.secondBit; + } + + /** + * Returns third bit in type flags. + * + * @return third bit in type flags + */ + public boolean getThirdBit() { + return this.thirdBit; + } + + /** + * Returns fourth bit in type flags. + * + * @return fourth bit in type flags + */ + public boolean getFourthBit() { + return this.fourthBit; + } + + /** + * Returns attribute length. + * + * @return attribute length + */ + public int getLength() { + return this.len; + } + + /** + * Returns whether attribute length read in short or byte. + * + * @return whether attribute length read in short or byte + */ + public boolean isShort() { + return this.isShort; + } + + /** + * Converts byte array of prefix value to IpPrefix object. + * + * @param value byte array of prefix value + * @param length prefix length in bits + * @return object of IpPrefix + */ + public static IpPrefix bytesToPrefix(byte[] value, int length) { + if (value.length != IPV4_SIZE) { + value = Arrays.copyOf(value, IPV4_SIZE); + } + IpPrefix ipPrefix = IpPrefix.valueOf(IpAddress.Version.INET, value, length); + return ipPrefix; + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/package-info.java b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/package-info.java new file mode 100755 index 00000000..3229d89a --- /dev/null +++ b/framework/src/onos/bgp/bgpio/src/main/java/org/onosproject/bgpio/util/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Implementation of BGP utility functions. + */ +package org.onosproject.bgpio.util; diff --git a/framework/src/onos/bgp/ctl/pom.xml b/framework/src/onos/bgp/ctl/pom.xml new file mode 100755 index 00000000..5cefd737 --- /dev/null +++ b/framework/src/onos/bgp/ctl/pom.xml @@ -0,0 +1,65 @@ +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-bgp</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>onos-bgp-ctl</artifactId> + <packaging>bundle</packaging> + + <description>ONOS BGP controller subsystem API</description> + + <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-bgp-api</artifactId> + </dependency> + <dependency> + <groupId>io.netty</groupId> + <artifactId>netty</artifactId> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-misc</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-scr-plugin</artifactId> + </plugin> + </plugins> + </build> + +</project> diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPChannelHandler.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPChannelHandler.java new file mode 100755 index 00000000..942d3658 --- /dev/null +++ b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPChannelHandler.java @@ -0,0 +1,34 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgp.controller.impl; + +import org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler; + +/** + * Channel handler deals with the bgp peer connection and dispatches messages from peer to the appropriate locations. + */ +class BGPChannelHandler extends IdleStateAwareChannelHandler { + + // TODO: implement FSM and session handling mechanism + /** + * Create a new unconnected BGPChannelHandler. + * + * @param bgpCtrlImpl bgp controller implementation object + */ + BGPChannelHandler(BGPControllerImpl bgpCtrlImpl) { + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPConfig.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPConfig.java new file mode 100755 index 00000000..56877a16 --- /dev/null +++ b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPConfig.java @@ -0,0 +1,344 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgp.controller.impl; + +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; + +import org.onlab.packet.Ip4Address; +import org.onosproject.bgp.controller.BGPCfg; +import org.onosproject.bgp.controller.BGPPeerCfg; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provides BGP configuration of this BGP speaker. + */ +public class BGPConfig implements BGPCfg { + + protected static final Logger log = LoggerFactory.getLogger(BGPConfig.class); + + private static final short DEFAULT_HOLD_TIMER = 120; + private static final short DEFAULT_CONN_RETRY_TIME = 120; + private static final short DEFAULT_CONN_RETRY_COUNT = 5; + + private State state = State.INIT; + private int localAs; + private int maxSession; + private boolean lsCapability; + private short holdTime; + private boolean largeAs = false; + private int maxConnRetryTime; + private int maxConnRetryCount; + + private Ip4Address routerId = null; + private TreeMap<String, BGPPeerCfg> bgpPeerTree = new TreeMap<>(); + + /** + * Constructor to initialize the values. + */ + public BGPConfig() { + + this.holdTime = DEFAULT_HOLD_TIMER; + this.maxConnRetryTime = DEFAULT_CONN_RETRY_TIME; + this.maxConnRetryCount = DEFAULT_CONN_RETRY_COUNT; + } + + @Override + public State getState() { + return state; + } + + @Override + public void setState(State state) { + this.state = state; + } + + @Override + public int getAsNumber() { + return this.localAs; + } + + @Override + public void setAsNumber(int localAs) { + + State localState = getState(); + this.localAs = localAs; + + /* Set configuration state */ + if (localState == State.IP_CONFIGURED) { + setState(State.IP_AS_CONFIGURED); + } else { + setState(State.AS_CONFIGURED); + } + } + + @Override + public int getMaxSession() { + return this.maxSession; + } + + @Override + public void setMaxSession(int maxSession) { + this.maxSession = maxSession; + } + + @Override + public boolean getLsCapability() { + return this.lsCapability; + } + + @Override + public void setLsCapability(boolean lsCapability) { + this.lsCapability = lsCapability; + } + + @Override + public String getRouterId() { + if (this.routerId != null) { + return this.routerId.toString(); + } else { + return null; + } + } + + @Override + public void setRouterId(String routerId) { + State localState = getState(); + this.routerId = Ip4Address.valueOf(routerId); + + /* Set configuration state */ + if (localState == State.AS_CONFIGURED) { + setState(State.IP_AS_CONFIGURED); + } else { + setState(State.IP_CONFIGURED); + } + } + + @Override + public boolean addPeer(String routerid, int remoteAs) { + return addPeer(routerid, remoteAs, DEFAULT_HOLD_TIMER); + } + + @Override + public boolean addPeer(String routerid, short holdTime) { + return addPeer(routerid, this.getAsNumber(), holdTime); + } + + @Override + public boolean addPeer(String routerid, int remoteAs, short holdTime) { + BGPPeerConfig lspeer = new BGPPeerConfig(); + if (this.bgpPeerTree.get(routerid) == null) { + + lspeer.setPeerRouterId(routerid); + lspeer.setAsNumber(remoteAs); + lspeer.setHoldtime(holdTime); + lspeer.setState(BGPPeerCfg.State.IDLE); + lspeer.setSelfInnitConnection(false); + + if (this.getAsNumber() == remoteAs) { + lspeer.setIsIBgp(true); + } else { + lspeer.setIsIBgp(false); + } + + this.bgpPeerTree.put(routerid, lspeer); + log.debug("added successfully"); + return true; + } else { + log.debug("already exists"); + return false; + } + } + + @Override + public boolean connectPeer(String routerid) { + BGPPeerCfg lspeer = this.bgpPeerTree.get(routerid); + + if (lspeer != null) { + lspeer.setSelfInnitConnection(true); + // TODO: initiate peer connection + return true; + } + + return false; + } + + @Override + public boolean removePeer(String routerid) { + BGPPeerCfg lspeer = this.bgpPeerTree.get(routerid); + + if (lspeer != null) { + + //TODO DISCONNECT PEER + disconnectPeer(routerid); + lspeer.setSelfInnitConnection(false); + lspeer = this.bgpPeerTree.remove(routerid); + log.debug("Deleted : " + routerid + " successfully"); + + return true; + } else { + log.debug("Did not find : " + routerid); + return false; + } + } + + @Override + public boolean disconnectPeer(String routerid) { + BGPPeerCfg lspeer = this.bgpPeerTree.get(routerid); + + if (lspeer != null) { + + //TODO DISCONNECT PEER + lspeer.setState(BGPPeerCfg.State.IDLE); + lspeer.setSelfInnitConnection(false); + log.debug("Disconnected : " + routerid + " successfully"); + + return true; + } else { + log.debug("Did not find : " + routerid); + return false; + } + } + + @Override + public void setPeerConnState(String routerid, BGPPeerCfg.State state) { + BGPPeerCfg lspeer = this.bgpPeerTree.get(routerid); + + if (lspeer != null) { + lspeer.setState(state); + log.debug("Peer : " + routerid + " is not available"); + + return; + } else { + log.debug("Did not find : " + routerid); + return; + } + } + + @Override + public BGPPeerCfg.State getPeerConnState(String routerid) { + BGPPeerCfg lspeer = this.bgpPeerTree.get(routerid); + + if (lspeer != null) { + return lspeer.getState(); + } else { + return BGPPeerCfg.State.INVALID; //No instance + } + } + + @Override + public boolean isPeerConnectable(String routerid) { + BGPPeerCfg lspeer = this.bgpPeerTree.get(routerid); + + if ((lspeer != null) && lspeer.getState().equals(BGPPeerCfg.State.IDLE)) { + return true; + } + + return false; + } + + @Override + public TreeMap<String, BGPPeerCfg> getPeerTree() { + return this.bgpPeerTree; + } + + @Override + public TreeMap<String, BGPPeerCfg> displayPeers() { + if (this.bgpPeerTree.isEmpty()) { + log.debug("There are no BGP peers"); + } else { + Set<Entry<String, BGPPeerCfg>> set = this.bgpPeerTree.entrySet(); + Iterator<Entry<String, BGPPeerCfg>> list = set.iterator(); + BGPPeerCfg lspeer; + + while (list.hasNext()) { + Entry<String, BGPPeerCfg> me = list.next(); + lspeer = me.getValue(); + log.debug("Peer neighbor IP :" + me.getKey()); + log.debug(", AS Number : " + lspeer.getAsNumber()); + log.debug(", Hold Timer : " + lspeer.getHoldtime()); + log.debug(", Is iBGP : " + lspeer.getIsIBgp()); + } + } + return null; + } + + @Override + public BGPPeerCfg displayPeers(String routerid) { + + if (this.bgpPeerTree.isEmpty()) { + log.debug("There are no BGP peers"); + } else { + return this.bgpPeerTree.get(routerid); + } + return null; + } + + @Override + public void setHoldTime(short holdTime) { + this.holdTime = holdTime; + } + + @Override + public short getHoldTime() { + return this.holdTime; + } + + @Override + public boolean getLargeASCapability() { + return this.largeAs; + } + + @Override + public void setLargeASCapability(boolean largeAs) { + this.largeAs = largeAs; + } + + @Override + public boolean isPeerConfigured(String routerid) { + BGPPeerCfg lspeer = this.bgpPeerTree.get(routerid); + return (lspeer != null) ? true : false; + } + + @Override + public boolean isPeerConnected(String routerid) { + // TODO: is peer connected + return true; + } + + @Override + public int getMaxConnRetryCount() { + return this.maxConnRetryCount; + } + + @Override + public void setMaxConnRetryCout(int retryCount) { + this.maxConnRetryCount = retryCount; + } + + @Override + public int getMaxConnRetryTime() { + return this.maxConnRetryTime; + } + + @Override + public void setMaxConnRetryTime(int retryTime) { + this.maxConnRetryTime = retryTime; + } +} diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPControllerImpl.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPControllerImpl.java new file mode 100755 index 00000000..95eafdbc --- /dev/null +++ b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPControllerImpl.java @@ -0,0 +1,104 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgp.controller.impl; + +import static org.onlab.util.Tools.groupedThreads; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Service; +import org.onosproject.bgp.controller.BGPCfg; +import org.onosproject.bgp.controller.BGPController; +import org.onosproject.bgp.controller.BGPId; +import org.onosproject.bgpio.protocol.BGPMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component(immediate = true) +@Service +public class BGPControllerImpl implements BGPController { + + private static final Logger log = LoggerFactory.getLogger(BGPControllerImpl.class); + + private final ExecutorService executorMsgs = Executors.newFixedThreadPool(32, + groupedThreads("onos/bgp", + "event-stats-%d")); + + private final ExecutorService executorBarrier = Executors.newFixedThreadPool(4, + groupedThreads("onos/bgp", + "event-barrier-%d")); + + final Controller ctrl = new Controller(this); + + private BGPConfig bgpconfig = new BGPConfig(); + + @Activate + public void activate() { + this.ctrl.start(); + log.info("Started"); + } + + @Deactivate + public void deactivate() { + // Close all connected peers + this.ctrl.stop(); + log.info("Stopped"); + } + + @Override + public void writeMsg(BGPId bgpId, BGPMessage msg) { + // TODO: Send message + } + + @Override + public void processBGPPacket(BGPId bgpId, BGPMessage msg) { + + switch (msg.getType()) { + case OPEN: + // TODO: Process Open message + break; + case KEEP_ALIVE: + // TODO: Process keepalive message + break; + case NOTIFICATION: + // TODO: Process notificatoin message + break; + case UPDATE: + // TODO: Process update message + break; + default: + // TODO: Process other message + break; + } + } + + /** + * Get controller instance. + * + * @return ctrl the controller. + */ + public Controller getController() { + return ctrl; + } + + @Override + public BGPCfg getConfig() { + return this.bgpconfig; + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPMessageDecoder.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPMessageDecoder.java new file mode 100755 index 00000000..39f2862d --- /dev/null +++ b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPMessageDecoder.java @@ -0,0 +1,53 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgp.controller.impl; + +import java.util.LinkedList; +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.handler.codec.frame.FrameDecoder; +import org.onosproject.bgpio.protocol.BGPMessage; +import org.onlab.util.HexDump; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Decode an bgp message from a Channel, for use in a netty pipeline. + */ +public class BGPMessageDecoder extends FrameDecoder { + + protected static final Logger log = LoggerFactory.getLogger(BGPMessageDecoder.class); + + @Override + protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { + + List<BGPMessage> msgList = new LinkedList<BGPMessage>(); + + log.debug("MESSAGE IS RECEIVED."); + if (!channel.isConnected()) { + log.info("Channel is not connected."); + return null; + } + + HexDump.dump(buffer); + + // TODO: decode bgp messages + return msgList; + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPMessageEncoder.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPMessageEncoder.java new file mode 100755 index 00000000..f0d38c3d --- /dev/null +++ b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPMessageEncoder.java @@ -0,0 +1,60 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgp.controller.impl; + +import java.util.List; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.handler.codec.oneone.OneToOneEncoder; +import org.onosproject.bgpio.protocol.BGPMessage; +import org.onlab.util.HexDump; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Encode an bgp message for output into a ChannelBuffer, for use in a + * netty pipeline. + */ +public class BGPMessageEncoder extends OneToOneEncoder { + protected static final Logger log = LoggerFactory.getLogger(BGPMessageEncoder.class); + + @Override + protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception { + log.debug("BGPMessageEncoder::encode"); + if (!(msg instanceof List)) { + log.debug("Invalid msg."); + return msg; + } + + @SuppressWarnings("unchecked") + List<BGPMessage> msglist = (List<BGPMessage>) msg; + + ChannelBuffer buf = ChannelBuffers.dynamicBuffer(); + + log.debug("SENDING MESSAGE"); + for (BGPMessage pm : msglist) { + pm.writeTo(buf); + } + + HexDump.dump(buf); + + return buf; + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPacketStatsImpl.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPacketStatsImpl.java new file mode 100755 index 00000000..41407dc4 --- /dev/null +++ b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPacketStatsImpl.java @@ -0,0 +1,128 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgp.controller.impl; + +import org.onosproject.bgp.controller.BGPPacketStats; + +/** + * A representation of a packet context which allows any provider + * to view a packet in event, but may block the response to the + * event if blocked has been called. This packet context can be used + * to react to the packet in event with a packet out. + */ +public class BGPPacketStatsImpl implements BGPPacketStats { + + private int inPacketCount; + private int outPacketCount; + private int wrongPacketCount; + private long time; + + /** + * Resets parameter. + */ + public BGPPacketStatsImpl() { + this.inPacketCount = 0; + this.outPacketCount = 0; + this.wrongPacketCount = 0; + this.time = 0; + } + + /** + * Get the outgoing packet count number. + * + * @return + * packet count + */ + public int outPacketCount() { + return outPacketCount; + } + + /** + * Get the incoming packet count number. + * + * @return + * packet count + */ + public int inPacketCount() { + return inPacketCount; + } + + /** + * Get the wrong packet count number. + * + * @return + * packet count + */ + public int wrongPacketCount() { + return wrongPacketCount; + } + + /** + * Increments the received packet counter. + */ + public void addInPacket() { + this.inPacketCount++; + } + + /** + * Increments the sent packet counter. + */ + public void addOutPacket() { + this.outPacketCount++; + } + + /** + * Increments the sent packet counter by specified value. + * + * @param value of no of packets sent + */ + public void addOutPacket(int value) { + this.outPacketCount = this.outPacketCount + value; + } + + /** + * Increments the wrong packet counter. + */ + public void addWrongPacket() { + this.wrongPacketCount++; + } + + /** + * Resets wrong packet count. + */ + public void resetWrongPacket() { + this.wrongPacketCount = 0; + } + + /** + * Get the time. + * + * @return + * time + */ + public long getTime() { + return this.time; + } + + /** + * Sets the time. + * + * @param time value to set + */ + public void setTime(long time) { + this.time = time; + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPeerConfig.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPeerConfig.java new file mode 100755 index 00000000..51b95a4b --- /dev/null +++ b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPeerConfig.java @@ -0,0 +1,109 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgp.controller.impl; + +import org.onlab.packet.Ip4Address; +import org.onosproject.bgp.controller.BGPPeerCfg; + +/** + * BGP Peer configuration information. + */ +public class BGPPeerConfig implements BGPPeerCfg { + private int asNumber; + private short holdTime; + private boolean isIBgp; + private Ip4Address peerId = null; + private State state; + private boolean selfInitiated; + + /** + * Constructor to initialize the values. + */ + BGPPeerConfig() { + state = State.IDLE; + selfInitiated = false; + } + + @Override + public int getAsNumber() { + return this.asNumber; + } + + @Override + public void setAsNumber(int asNumber) { + this.asNumber = asNumber; + } + + @Override + public short getHoldtime() { + return this.holdTime; + } + + @Override + public void setHoldtime(short holdTime) { + this.holdTime = holdTime; + } + + @Override + public boolean getIsIBgp() { + return this.isIBgp; + } + + @Override + public void setIsIBgp(boolean isIBgp) { + this.isIBgp = isIBgp; + } + + @Override + public String getPeerRouterId() { + if (this.peerId != null) { + return this.peerId.toString(); + } else { + return null; + } + } + + @Override + public void setPeerRouterId(String peerId) { + this.peerId = Ip4Address.valueOf(peerId); + } + + @Override + public void setPeerRouterId(String peerId, int asNumber) { + this.peerId = Ip4Address.valueOf(peerId); + this.asNumber = asNumber; + } + + @Override + public State getState() { + return this.state; + } + + @Override + public void setState(State state) { + this.state = state; + } + + @Override + public boolean getSelfInnitConnection() { + return this.selfInitiated; + } + + @Override + public void setSelfInnitConnection(boolean selfInit) { + this.selfInitiated = selfInit; + } +} diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPipelineFactory.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPipelineFactory.java new file mode 100755 index 00000000..b2ca5077 --- /dev/null +++ b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/BGPPipelineFactory.java @@ -0,0 +1,67 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.bgp.controller.impl; + +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.handler.timeout.ReadTimeoutHandler; +import org.jboss.netty.util.ExternalResourceReleasable; +import org.jboss.netty.util.HashedWheelTimer; +import org.jboss.netty.util.Timer; + +/** + * Creates a ChannelPipeline for a server-side bgp channel. + */ +public class BGPPipelineFactory + implements ChannelPipelineFactory, ExternalResourceReleasable { + + static final Timer TIMER = new HashedWheelTimer(); + protected ReadTimeoutHandler readTimeoutHandler; + BGPControllerImpl bgpCtrlImpl; + + /** + * Constructor to initialize the values. + * + * @param ctrlImpl parent ctrlImpl + * @param isServBgp if it is a server or not + */ + public BGPPipelineFactory(BGPControllerImpl ctrlImpl, boolean isServBgp) { + super(); + bgpCtrlImpl = ctrlImpl; + /* hold time*/ + readTimeoutHandler = new ReadTimeoutHandler(TIMER, bgpCtrlImpl.getConfig().getHoldTime()); + } + + @Override + public ChannelPipeline getPipeline() throws Exception { + BGPChannelHandler handler = new BGPChannelHandler(bgpCtrlImpl); + + ChannelPipeline pipeline = Channels.pipeline(); + pipeline.addLast("bgpmessagedecoder", new BGPMessageDecoder()); + pipeline.addLast("bgpmessageencoder", new BGPMessageEncoder()); + pipeline.addLast("holdTime", readTimeoutHandler); + pipeline.addLast("PassiveHandler", handler); + + return pipeline; + } + + @Override + public void releaseExternalResources() { + TIMER.stop(); + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/Controller.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/Controller.java new file mode 100755 index 00000000..402e8c94 --- /dev/null +++ b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/Controller.java @@ -0,0 +1,183 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.bgp.controller.impl; + +import static org.onlab.util.Tools.groupedThreads; + +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executors; + +import org.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.channel.group.ChannelGroup; +import org.jboss.netty.channel.group.DefaultChannelGroup; +import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The main controller class. Handles all setup and network listeners - Distributed ownership control of bgp peer + * through IControllerRegistryService + */ +public class Controller { + + protected static final Logger log = LoggerFactory.getLogger(Controller.class); + + private ChannelGroup cg; + + // Configuration options + private static final short BGP_PORT_NUM = 179; + private int workerThreads = 16; + + // Start time of the controller + protected long systemStartTime; + + private NioServerSocketChannelFactory serverExecFactory; + + // Perf. related configuration + protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024; + + BGPControllerImpl bgpCtrlImpl; + + /** + * Constructor to initialize parameter. + * + * @param bgpCtrlImpl BGP controller Impl instance + */ + public Controller(BGPControllerImpl bgpCtrlImpl) { + this.bgpCtrlImpl = bgpCtrlImpl; + } + + // *************** + // Getters/Setters + // *************** + + /** + * To get system start time. + * + * @return system start time in milliseconds + */ + public long getSystemStartTime() { + return (this.systemStartTime); + } + + // ************** + // Initialization + // ************** + + /** + * Tell controller that we're ready to accept bgp peer connections. + */ + public void run() { + + try { + final ServerBootstrap bootstrap = createServerBootStrap(); + + bootstrap.setOption("reuseAddr", true); + bootstrap.setOption("child.keepAlive", true); + bootstrap.setOption("child.tcpNoDelay", true); + bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE); + + ChannelPipelineFactory pfact = new BGPPipelineFactory(bgpCtrlImpl, true); + + bootstrap.setPipelineFactory(pfact); + InetSocketAddress sa = new InetSocketAddress(getBgpPortNum()); + cg = new DefaultChannelGroup(); + cg.add(bootstrap.bind(sa)); + log.info("Listening for Peer connection on {}", sa); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Creates server boot strap. + * + * @return ServerBootStrap + */ + private ServerBootstrap createServerBootStrap() { + + if (workerThreads == 0) { + serverExecFactory = new NioServerSocketChannelFactory( + Executors.newCachedThreadPool(groupedThreads("onos/bgp", "boss-%d")), + Executors.newCachedThreadPool(groupedThreads("onos/bgp", "worker-%d"))); + return new ServerBootstrap(serverExecFactory); + } else { + serverExecFactory = new NioServerSocketChannelFactory( + Executors.newCachedThreadPool(groupedThreads("onos/bgp", "boss-%d")), + Executors.newCachedThreadPool(groupedThreads("onos/bgp", "worker-%d")), + workerThreads); + return new ServerBootstrap(serverExecFactory); + } + } + + /** + * Initialize internal data structures. + */ + public void init() { + // These data structures are initialized here because other + // module's startUp() might be called before ours + this.systemStartTime = System.currentTimeMillis(); + } + + // ************** + // Utility methods + // ************** + + public Map<String, Long> getMemory() { + Map<String, Long> m = new HashMap<>(); + Runtime runtime = Runtime.getRuntime(); + m.put("total", runtime.totalMemory()); + m.put("free", runtime.freeMemory()); + return m; + } + + public Long getUptime() { + RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean(); + return rb.getUptime(); + } + + /** + * Starts the BGP controller. + */ + public void start() { + log.info("Started"); + this.init(); + this.run(); + } + + /** + * Stops the BGP controller. + */ + public void stop() { + log.info("Stopped"); + serverExecFactory.shutdown(); + cg.close(); + } + + /** + * Returns port number. + * + * @return port number + */ + public static short getBgpPortNum() { + return BGP_PORT_NUM; + } +}
\ No newline at end of file diff --git a/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/package-info.java b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/package-info.java new file mode 100755 index 00000000..fd4e9506 --- /dev/null +++ b/framework/src/onos/bgp/ctl/src/main/java/org/onosproject/bgp/controller/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Implementation of the BGP controller IO subsystem. + */ +package org.onosproject.bgp.controller.impl; diff --git a/framework/src/onos/bgp/pom.xml b/framework/src/onos/bgp/pom.xml new file mode 100755 index 00000000..941168ba --- /dev/null +++ b/framework/src/onos/bgp/pom.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>onos-bgp</artifactId> + <packaging>pom</packaging> + + <description>ONOS BGP Protocol subsystem</description> + + <modules> + <module>api</module> + <module>ctl</module> + <module>bgpio</module> + </modules> + + <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-misc</artifactId> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-junit</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + </plugin> + </plugins> + </build> + +</project> diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/Comparators.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/Comparators.java index b0cbbdd6..1df2f049 100644 --- a/framework/src/onos/cli/src/main/java/org/onosproject/cli/Comparators.java +++ b/framework/src/onos/cli/src/main/java/org/onosproject/cli/Comparators.java @@ -25,6 +25,8 @@ import org.onosproject.net.ElementId; import org.onosproject.net.Port; import org.onosproject.net.flow.FlowRule; import org.onosproject.net.group.Group; + +import org.onosproject.net.statistic.TypedFlowEntryWithLoad; import org.onosproject.net.topology.TopologyCluster; import java.util.Comparator; @@ -115,4 +117,12 @@ public final class Comparators { public static final Comparator<Interface> INTERFACES_COMPARATOR = (intf1, intf2) -> CONNECT_POINT_COMPARATOR.compare(intf1.connectPoint(), intf2.connectPoint()); + public static final Comparator<TypedFlowEntryWithLoad> TYPEFLOWENTRY_WITHLOAD_COMPARATOR = + new Comparator<TypedFlowEntryWithLoad>() { + @Override + public int compare(TypedFlowEntryWithLoad fe1, TypedFlowEntryWithLoad fe2) { + long delta = fe1.load().rate() - fe2.load().rate(); + return delta == 0 ? 0 : (delta > 0 ? -1 : +1); + } + }; } diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/cfg/NetworkConfigCommand.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/cfg/NetworkConfigCommand.java index d99a1839..7ce56692 100644 --- a/framework/src/onos/cli/src/main/java/org/onosproject/cli/cfg/NetworkConfigCommand.java +++ b/framework/src/onos/cli/src/main/java/org/onosproject/cli/cfg/NetworkConfigCommand.java @@ -35,13 +35,13 @@ import static com.google.common.base.Strings.isNullOrEmpty; description = "Manages network configuration") public class NetworkConfigCommand extends AbstractShellCommand { - @Argument(index = 0, name = "subjectKey", description = "Subject key", + @Argument(index = 0, name = "subjectClassKey", description = "Subject class key", required = false, multiValued = false) - String subjectKey = null; + String subjectClassKey = null; - @Argument(index = 1, name = "subject", description = "Subject", + @Argument(index = 1, name = "subjectKey", description = "Subject key", required = false, multiValued = false) - String subject = null; + String subjectKey = null; @Argument(index = 2, name = "configKey", description = "Config key", required = false, multiValued = false) @@ -54,18 +54,18 @@ public class NetworkConfigCommand extends AbstractShellCommand { protected void execute() { service = get(NetworkConfigService.class); JsonNode root = mapper.createObjectNode(); - if (isNullOrEmpty(subjectKey)) { + if (isNullOrEmpty(subjectClassKey)) { addAll((ObjectNode) root); } else { - SubjectFactory subjectFactory = service.getSubjectFactory(subjectKey); - if (isNullOrEmpty(subject)) { + SubjectFactory subjectFactory = service.getSubjectFactory(subjectClassKey); + if (isNullOrEmpty(subjectKey)) { addSubjectClass((ObjectNode) root, subjectFactory); } else { - Object s = subjectFactory.createSubject(subject); + Object s = subjectFactory.createSubject(subjectKey); if (isNullOrEmpty(configKey)) { addSubject((ObjectNode) root, s); } else { - root = getSubjectConfig(getConfig(s, subjectKey, configKey)); + root = getSubjectConfig(getConfig(s, subjectClassKey, configKey)); } } } @@ -82,14 +82,14 @@ public class NetworkConfigCommand extends AbstractShellCommand { service.getSubjectClasses() .forEach(sc -> { SubjectFactory sf = service.getSubjectFactory(sc); - addSubjectClass(newObject(root, sf.subjectKey()), sf); + addSubjectClass(newObject(root, sf.subjectClassKey()), sf); }); } @SuppressWarnings("unchecked") private void addSubjectClass(ObjectNode root, SubjectFactory sf) { service.getSubjects(sf.subjectClass()) - .forEach(s -> addSubject(newObject(root, s.toString()), s)); + .forEach(s -> addSubject(newObject(root, sf.subjectKey(s)), s)); } private void addSubject(ObjectNode root, Object s) { diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/cfg/NetworkConfigRegistryCommand.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/cfg/NetworkConfigRegistryCommand.java index f94967e1..cf588770 100644 --- a/framework/src/onos/cli/src/main/java/org/onosproject/cli/cfg/NetworkConfigRegistryCommand.java +++ b/framework/src/onos/cli/src/main/java/org/onosproject/cli/cfg/NetworkConfigRegistryCommand.java @@ -42,7 +42,7 @@ public class NetworkConfigRegistryCommand extends AbstractShellCommand { private void print(ConfigFactory configFactory) { print(shortOnly ? SHORT_FMT : FMT, - configFactory.subjectFactory().subjectKey(), + configFactory.subjectFactory().subjectClassKey(), configFactory.configKey(), configFactory.subjectFactory().subjectClass().getName(), configFactory.configClass().getName()); diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DeviceControllersCommand.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DeviceControllersCommand.java new file mode 100644 index 00000000..328d4700 --- /dev/null +++ b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DeviceControllersCommand.java @@ -0,0 +1,47 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.cli.net; + +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.net.behaviour.ControllerConfig; +import org.onosproject.net.driver.DriverHandler; +import org.onosproject.net.driver.DriverService; + +/** + * Sets role of the controller node for the given infrastructure device. + */ +@Command(scope = "onos", name = "device-controllers", + description = "gets the list of controllers for the given infrastructure device") +public class DeviceControllersCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "uri", description = "Device ID", + required = true, multiValued = false) + String uri = null; + private DeviceId deviceId; + + @Override + protected void execute() { + DriverService service = get(DriverService.class); + deviceId = DeviceId.deviceId(uri); + DriverHandler h = service.createHandler(deviceId); + ControllerConfig config = h.behaviour(ControllerConfig.class); + config.getControllers().forEach(c -> print(c.target())); + } + +} diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DevicePortsListCommand.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DevicePortsListCommand.java index 494273cc..e2d3e6d6 100644 --- a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DevicePortsListCommand.java +++ b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DevicePortsListCommand.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 Open Networking Laboratory + * Copyright 2015 Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,8 +22,12 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.karaf.shell.commands.Argument; import org.apache.karaf.shell.commands.Command; import org.apache.karaf.shell.commands.Option; +import org.onlab.util.Frequency; import org.onosproject.cli.Comparators; import org.onosproject.net.Device; +import org.onosproject.net.OchPort; +import org.onosproject.net.OduCltPort; +import org.onosproject.net.OmsPort; import org.onosproject.net.Port; import org.onosproject.net.PortNumber; import org.onosproject.net.device.DeviceService; @@ -41,7 +45,10 @@ import static org.onosproject.net.DeviceId.deviceId; description = "Lists all ports or all ports of a device") public class DevicePortsListCommand extends DevicesListCommand { - private static final String FMT = " port=%s, state=%s, type=%s, speed=%s%s"; + private static final String FMT = " port=%s, state=%s, type=%s, speed=%s %s"; + private static final String FMT_OCH = " port=%s, state=%s, type=%s, signalType=%s, isTunable=%s %s"; + private static final String FMT_ODUCLT = " port=%s, state=%s, type=%s, signalType=%s %s"; + private static final String FMT_OMS = " port=%s, state=%s, type=%s, Freqs= %s / %s / %s GHz, totalChannels=%s %s"; @Option(name = "-e", aliases = "--enabled", description = "Show only enabled ports", required = false, multiValued = false) @@ -137,13 +144,34 @@ public class DevicePortsListCommand extends DevicesListCommand { List<Port> ports = new ArrayList<>(service.getPorts(device.id())); Collections.sort(ports, Comparators.PORT_COMPARATOR); for (Port port : ports) { - if (isIncluded(port)) { - print(FMT, portName(port.number()), - port.isEnabled() ? "enabled" : "disabled", - port.type().toString().toLowerCase(), port.portSpeed(), - annotations(port.annotations())); + if (!isIncluded(port)) { + continue; + } + String portName = portName(port.number()); + Object portIsEnabled = port.isEnabled() ? "enabled" : "disabled"; + String portType = port.type().toString().toLowerCase(); + String annotations = annotations(port.annotations()); + switch (port.type()) { + case OCH: + print(FMT_OCH, portName, portIsEnabled, portType, + ((OchPort) port).signalType().toString(), + ((OchPort) port).isTunable() ? "yes" : "no", annotations); + break; + case ODUCLT: + print(FMT_ODUCLT, portName, portIsEnabled, portType, + ((OduCltPort) port).signalType().toString(), annotations); + break; + case OMS: + print(FMT_OMS, portName, portIsEnabled, portType, + ((OmsPort) port).minFrequency().asHz() / Frequency.ofGHz(1).asHz(), + ((OmsPort) port).maxFrequency().asHz() / Frequency.ofGHz(1).asHz(), + ((OmsPort) port).grid().asHz() / Frequency.ofGHz(1).asHz(), + ((OmsPort) port).totalChannels(), annotations); + break; + default: + print(FMT, portName, portIsEnabled, portType, port.portSpeed(), annotations); + break; } } } - } diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DeviceSetControllersCommand.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DeviceSetControllersCommand.java new file mode 100644 index 00000000..c3693799 --- /dev/null +++ b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/DeviceSetControllersCommand.java @@ -0,0 +1,69 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.cli.net; + +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.net.behaviour.ControllerConfig; +import org.onosproject.net.behaviour.ControllerInfo; +import org.onosproject.net.driver.DriverHandler; +import org.onosproject.net.driver.DriverService; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Sets role of the controller node for the given infrastructure device. + */ +@Command(scope = "onos", name = "device-setcontrollers", + description = "sets the list of controllers for the given infrastructure device") +public class DeviceSetControllersCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "uri", description = "Device ID", + required = true, multiValued = false) + String uri = null; + + @Argument(index = 1, name = "controllersListStrings", description = "list of " + + "controllers to set for the specified device", + required = true, multiValued = true) + String[] controllersListStrings = null; + + private DeviceId deviceId; + private List<ControllerInfo> newControllers = new ArrayList<>(); + + @Override + protected void execute() { + + Arrays.asList(controllersListStrings).forEach( + cInfoString -> newControllers.add(new ControllerInfo(cInfoString))); + DriverService service = get(DriverService.class); + deviceId = DeviceId.deviceId(uri); + DriverHandler h = service.createHandler(deviceId); + ControllerConfig config = h.behaviour(ControllerConfig.class); + print("before:"); + config.getControllers().forEach(c -> print(c.target())); + + config.setControllers(newControllers); + print("after:"); + config.getControllers().forEach(c -> print(c.target())); + print("size %d", config.getControllers().size()); + } + +} diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/GetFlowStatistics.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/GetFlowStatistics.java new file mode 100644 index 00000000..cafe87f9 --- /dev/null +++ b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/GetFlowStatistics.java @@ -0,0 +1,323 @@ +/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.cli.net;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.TypedStoredFlowEntry;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.statistic.FlowStatisticService;
+import org.onosproject.net.statistic.SummaryFlowEntryWithLoad;
+import org.onosproject.net.statistic.TypedFlowEntryWithLoad;
+
+import java.util.List;
+import java.util.Map;
+
+import static org.onosproject.net.DeviceId.deviceId;
+import static org.onosproject.net.PortNumber.portNumber;
+
+/**
+ * Fetches flow statistics with a flow type and instruction type.
+ */
+@Command(scope = "onos", name = "get-flow-stats",
+ description = "Fetches flow stats for a connection point with given flow type and instruction type")
+public class GetFlowStatistics extends AbstractShellCommand {
+ @Argument(index = 0, name = "devicePort",
+ description = "Device[/Port] connectPoint Description",
+ required = true, multiValued = false)
+ String devicePort = null;
+
+ @Option(name = "-s", aliases = "--summary",
+ description = "Show flow stats summary",
+ required = false, multiValued = false)
+ boolean showSummary = true; // default summary
+
+ @Option(name = "-a", aliases = "--all",
+ description = "Show flow stats all",
+ required = false, multiValued = false)
+ boolean showAll = false;
+
+ @Option(name = "-t", aliases = "--topn",
+ description = "Show flow stats topn",
+ required = false, multiValued = false)
+ String showTopn = null;
+
+ @Option(name = "-f", aliases = "--flowType",
+ description = "Flow live type, It includes IMMEDIATE, SHORT, MID, LONG, UNKNOWN"
+ + ", and is valid with -a or -t option only",
+ required = false, multiValued = false)
+ String flowLiveType = null;
+
+ @Option(name = "-i", aliases = "--instructionType",
+ description = "Flow instruction type, It includes DROP, OUTPUT, GROUP, L0MODIFICATION, L2MODIFICATION,"
+ + " TABLE, L3MODIFICATION, METADATA"
+ + ", and is valid with -a or -t option only",
+ required = false, multiValued = false)
+ String instructionType = null;
+
+ @Override
+ protected void execute() {
+ DeviceService deviceService = get(DeviceService.class);
+ FlowStatisticService flowStatsService = get(FlowStatisticService.class);
+
+ String deviceURI = getDeviceId(devicePort);
+ String portURI = getPortNumber(devicePort);
+
+ DeviceId ingressDeviceId = deviceId(deviceURI);
+ PortNumber ingressPortNumber;
+ if (portURI.length() == 0) {
+ ingressPortNumber = null;
+ } else {
+ ingressPortNumber = portNumber(portURI);
+ }
+
+ Device device = deviceService.getDevice(ingressDeviceId);
+ if (device == null) {
+ error("No such device %s", ingressDeviceId.uri());
+ return;
+ }
+
+ if (ingressPortNumber != null) {
+ Port port = deviceService.getPort(ingressDeviceId, ingressPortNumber);
+ if (port == null) {
+ error("No such port %s on device %s", portURI, ingressDeviceId.uri());
+ return;
+ }
+ }
+
+ if (flowLiveType != null) {
+ flowLiveType = flowLiveType.toUpperCase();
+ }
+ if (instructionType != null) {
+ instructionType = instructionType.toUpperCase();
+ }
+
+ // convert String to FlowLiveType and check validity
+ TypedStoredFlowEntry.FlowLiveType inLiveType;
+ if (flowLiveType == null) {
+ inLiveType = null;
+ } else {
+ inLiveType = getFlowLiveType(flowLiveType);
+ if (inLiveType == null) {
+ error("Invalid flow live type [%s] error", flowLiveType);
+ return;
+ }
+ }
+ // convert String to InstructionType and check validity
+ Instruction.Type inInstructionType;
+ if (instructionType == null) {
+ inInstructionType = null;
+ } else {
+ inInstructionType = getInstructionType(instructionType);
+ if (inInstructionType == null) {
+ error("Invalid instruction type [%s] error", instructionType);
+ return;
+ }
+ }
+
+ if (showTopn != null) {
+ int topn = Integer.parseInt(showTopn);
+
+ if (topn <= 0) {
+ topn = 100; //default value
+ } else if (topn > 1000) {
+ topn = 1000; //max value
+ }
+
+ // print show topn head line with type
+ print("deviceId=%s, show TOPN=%s flows, live type=%s, instruction type=%s",
+ deviceURI,
+ Integer.toString(topn),
+ flowLiveType == null ? "ALL" : flowLiveType,
+ instructionType == null ? "ALL" : instructionType);
+ if (ingressPortNumber == null) {
+ Map<ConnectPoint, List<TypedFlowEntryWithLoad>> typedFlowLoadMap =
+ flowStatsService.loadTopnByType(device, inLiveType, inInstructionType, topn);
+ // print all ports topn flows load for a given device
+ for (ConnectPoint cp : typedFlowLoadMap.keySet()) {
+ printPortFlowsLoad(cp, typedFlowLoadMap.get(cp));
+ }
+ } else {
+ List<TypedFlowEntryWithLoad> typedFlowLoad =
+ flowStatsService.loadTopnByType(device, ingressPortNumber, inLiveType, inInstructionType, topn);
+ // print device/port topn flows load
+ ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);
+ printPortFlowsLoad(cp, typedFlowLoad);
+ }
+ } else if (showAll) { // is true?
+ // print show all head line with type
+ print("deviceId=%s, show ALL flows, live type=%s, instruction type=%s",
+ deviceURI,
+ flowLiveType == null ? "ALL" : flowLiveType,
+ instructionType == null ? "ALL" : instructionType);
+ if (ingressPortNumber == null) {
+ Map<ConnectPoint, List<TypedFlowEntryWithLoad>> typedFlowLoadMap =
+ flowStatsService.loadAllByType(device, inLiveType, inInstructionType);
+ // print all ports all flows load for a given device
+ for (ConnectPoint cp : typedFlowLoadMap.keySet()) {
+ printPortFlowsLoad(cp, typedFlowLoadMap.get(cp));
+ }
+ } else {
+ List<TypedFlowEntryWithLoad> typedFlowLoad =
+ flowStatsService.loadAllByType(device, ingressPortNumber, inLiveType, inInstructionType);
+ // print device/port all flows load
+ ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);
+ printPortFlowsLoad(cp, typedFlowLoad);
+ }
+ } else { // if (showSummary == true) //always is true
+ // print show summary head line
+ print("deviceId=%s, show SUMMARY flows", deviceURI);
+ if (ingressPortNumber == null) {
+ Map<ConnectPoint, SummaryFlowEntryWithLoad> summaryFlowLoadMap =
+ flowStatsService.loadSummary(device);
+ // print all ports flow load summary for a given device
+ for (ConnectPoint cp : summaryFlowLoadMap.keySet()) {
+ printPortSummaryLoad(cp, summaryFlowLoadMap.get(cp));
+ }
+ } else {
+ SummaryFlowEntryWithLoad summaryFlowLoad =
+ flowStatsService.loadSummary(device, ingressPortNumber);
+ // print device/port flow load summary
+ ConnectPoint cp = new ConnectPoint(ingressDeviceId, ingressPortNumber);
+ printPortSummaryLoad(cp, summaryFlowLoad);
+ }
+ }
+ }
+
+ /**
+ * Extracts the port number portion of the ConnectPoint.
+ *
+ * @param deviceString string representing the device/port
+ * @return port number as a string, empty string if the port is not found
+ */
+ private String getPortNumber(String deviceString) {
+ if (deviceString == null) {
+ return "";
+ }
+
+ int slash = deviceString.indexOf('/');
+ if (slash <= 0) {
+ return ""; // return when no port number
+ }
+ return deviceString.substring(slash + 1, deviceString.length());
+ }
+
+ /**
+ * Extracts the device ID portion of the ConnectPoint.
+ *
+ * @param deviceString string representing the device/port
+ * @return device ID string
+ */
+ private String getDeviceId(String deviceString) {
+ if (deviceString == null) {
+ return "";
+ }
+
+ int slash = deviceString.indexOf('/');
+ if (slash <= 0) {
+ return deviceString; // return only included device ID
+ }
+ return deviceString.substring(0, slash);
+ }
+
+ /**
+ * converts string of flow live type to FloeLiveType enum.
+ *
+ * @param liveType string representing the flow live type
+ * @return TypedStoredFlowEntry.FlowLiveType
+ */
+ private TypedStoredFlowEntry.FlowLiveType getFlowLiveType(String liveType) {
+ String liveTypeUC = liveType.toUpperCase();
+
+ if (liveTypeUC.equals("IMMEDIATE")) {
+ return TypedStoredFlowEntry.FlowLiveType.IMMEDIATE_FLOW;
+ } else if (liveTypeUC.equals("SHORT")) {
+ return TypedStoredFlowEntry.FlowLiveType.SHORT_FLOW;
+ } else if (liveTypeUC.equals("MID")) {
+ return TypedStoredFlowEntry.FlowLiveType.MID_FLOW;
+ } else if (liveTypeUC.equals("LONG")) {
+ return TypedStoredFlowEntry.FlowLiveType.LONG_FLOW;
+ } else if (liveTypeUC.equals("UNKNOWN")) {
+ return TypedStoredFlowEntry.FlowLiveType.UNKNOWN_FLOW;
+ } else {
+ return null; // flow live type error
+ }
+ }
+
+ /**
+ * converts string of instruction type to Instruction type enum.
+ *
+ * @param instType string representing the instruction type
+ * @return Instruction.Type
+ */
+ private Instruction.Type getInstructionType(String instType) {
+ String instTypeUC = instType.toUpperCase();
+
+ if (instTypeUC.equals("DROP")) {
+ return Instruction.Type.DROP;
+ } else if (instTypeUC.equals("OUTPUT")) {
+ return Instruction.Type.OUTPUT;
+ } else if (instTypeUC.equals("GROUP")) {
+ return Instruction.Type.GROUP;
+ } else if (instTypeUC.equals("L0MODIFICATION")) {
+ return Instruction.Type.L0MODIFICATION;
+ } else if (instTypeUC.equals("L2MODIFICATION")) {
+ return Instruction.Type.L2MODIFICATION;
+ } else if (instTypeUC.equals("TABLE")) {
+ return Instruction.Type.TABLE;
+ } else if (instTypeUC.equals("L3MODIFICATION")) {
+ return Instruction.Type.L3MODIFICATION;
+ } else if (instTypeUC.equals("METADATA")) {
+ return Instruction.Type.METADATA;
+ } else {
+ return null; // instruction type error
+ }
+ }
+
+ private void printPortFlowsLoad(ConnectPoint cp, List<TypedFlowEntryWithLoad> typedFlowLoad) {
+ print(" deviceId/Port=%s/%s, %s flows", cp.elementId(), cp.port(), typedFlowLoad.size());
+ for (TypedFlowEntryWithLoad tfel: typedFlowLoad) {
+ TypedStoredFlowEntry tfe = tfel.typedStoredFlowEntry();
+ print(" flowId=%s, state=%s, liveType=%s, life=%s -> %s",
+ Long.toHexString(tfe.id().value()),
+ tfe.state(),
+ tfe.flowLiveType(),
+ tfe.life(),
+ tfel.load().isValid() ? tfel.load() : "Load{rate=0, NOT VALID}");
+ }
+ }
+
+ private void printPortSummaryLoad(ConnectPoint cp, SummaryFlowEntryWithLoad summaryFlowLoad) {
+ print(" deviceId/Port=%s/%s, Total=%s, Immediate=%s, Short=%s, Mid=%s, Long=%s, Unknown=%s",
+ cp.elementId(),
+ cp.port(),
+ summaryFlowLoad.totalLoad().isValid() ? summaryFlowLoad.totalLoad() : "Load{rate=0, NOT VALID}",
+ summaryFlowLoad.immediateLoad().isValid() ? summaryFlowLoad.immediateLoad() : "Load{rate=0, NOT VALID}",
+ summaryFlowLoad.shortLoad().isValid() ? summaryFlowLoad.shortLoad() : "Load{rate=0, NOT VALID}",
+ summaryFlowLoad.midLoad().isValid() ? summaryFlowLoad.midLoad() : "Load{rate=0, NOT VALID}",
+ summaryFlowLoad.longLoad().isValid() ? summaryFlowLoad.longLoad() : "Load{rate=0, NOT VALID}",
+ summaryFlowLoad.unknownLoad().isValid() ? summaryFlowLoad.unknownLoad() : "Load{rate=0, NOT VALID}");
+ }
+}
diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/InterfaceAddCommand.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/InterfaceAddCommand.java new file mode 100644 index 00000000..eefb711a --- /dev/null +++ b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/InterfaceAddCommand.java @@ -0,0 +1,79 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.cli.net; + +import com.google.common.collect.Sets; +import org.apache.karaf.shell.commands.Command; +import org.apache.karaf.shell.commands.Option; +import org.onlab.packet.MacAddress; +import org.onlab.packet.VlanId; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.incubator.net.intf.Interface; +import org.onosproject.incubator.net.intf.InterfaceAdminService; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.host.InterfaceIpAddress; + +import java.util.Set; + +/** + * Adds a new interface configuration. + */ +@Command(scope = "onos", name = "add-interface", + description = "Adds a new configured interface") +public class InterfaceAddCommand extends AbstractShellCommand { + + @Option(name = "-c", aliases = "--connectPoint", + description = "Device port that the interface is associated with", + required = true, multiValued = false) + private String connectPoint = null; + + @Option(name = "-m", aliases = "--mac", + description = "MAC address of the interface", + required = true, multiValued = false) + private String mac = null; + + @Option(name = "-i", aliases = "--ip", + description = "IP address configured on the interface\n" + + "(e.g. 10.0.1.1/24). Can be specified multiple times.", + required = false, multiValued = true) + private String[] ips = null; + + @Option(name = "-v", aliases = "--vlan", + description = "VLAN configured on the interface", + required = false, multiValued = false) + private String vlan = null; + + @Override + protected void execute() { + InterfaceAdminService interfaceService = get(InterfaceAdminService.class); + + Set<InterfaceIpAddress> ipAddresses = Sets.newHashSet(); + if (ips != null) { + for (String strIp : ips) { + ipAddresses.add(InterfaceIpAddress.valueOf(strIp)); + } + } + + VlanId vlanId = vlan == null ? VlanId.NONE : VlanId.vlanId(Short.parseShort(vlan)); + + Interface intf = new Interface(ConnectPoint.deviceConnectPoint(connectPoint), + ipAddresses, MacAddress.valueOf(mac), vlanId); + + interfaceService.add(intf); + } + +} diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/InterfaceRemoveCommand.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/InterfaceRemoveCommand.java new file mode 100644 index 00000000..941a65db --- /dev/null +++ b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/InterfaceRemoveCommand.java @@ -0,0 +1,51 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.cli.net; + +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.incubator.net.intf.InterfaceAdminService; +import org.onosproject.net.ConnectPoint; + +/** + * Removes an interface configuration. + */ +@Command(scope = "onos", name = "remove-interface", + description = "Removes a configured interface") +public class InterfaceRemoveCommand extends AbstractShellCommand { + + @Argument(index = 0, name = "connectPoint", + description = "Connect point of the interface", + required = true, multiValued = false) + private String connectPoint = null; + + @Argument(index = 1, name = "vlan", + description = "Interface vlan", + required = true, multiValued = false) + private String vlan = null; + + @Override + protected void execute() { + InterfaceAdminService interfaceService = get(InterfaceAdminService.class); + + interfaceService.remove(ConnectPoint.deviceConnectPoint(connectPoint), + VlanId.vlanId(Short.parseShort(vlan))); + } + +} diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/PacketProcessorsListCommand.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/PacketProcessorsListCommand.java index ff66b803..6b7d9336 100644 --- a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/PacketProcessorsListCommand.java +++ b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/PacketProcessorsListCommand.java @@ -17,7 +17,7 @@ package org.onosproject.cli.net; import org.apache.karaf.shell.commands.Command; import org.onosproject.cli.AbstractShellCommand; -import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketProcessorEntry; import org.onosproject.net.packet.PacketService; import static org.onosproject.net.packet.PacketProcessor.ADVISOR_MAX; @@ -30,7 +30,7 @@ import static org.onosproject.net.packet.PacketProcessor.DIRECTOR_MAX; description = "Lists packet processors") public class PacketProcessorsListCommand extends AbstractShellCommand { - private static final String FMT = "priority=%s, class=%s"; + private static final String FMT = "priority=%s, class=%s, packets=%d, avgNanos=%d"; @Override protected void execute() { @@ -43,8 +43,10 @@ public class PacketProcessorsListCommand extends AbstractShellCommand { } } - private void print(int priority, PacketProcessor processor) { - print(FMT, priorityFormat(priority), processor.getClass().getName()); + private void print(PacketProcessorEntry entry) { + print(FMT, priorityFormat(entry.priority()), + entry.processor().getClass().getName(), + entry.invocations(), entry.averageNanos()); } private String priorityFormat(int priority) { diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/TableStatisticsCommand.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/TableStatisticsCommand.java new file mode 100644 index 00000000..e0cd72fc --- /dev/null +++ b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/TableStatisticsCommand.java @@ -0,0 +1,145 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.cli.net; + +import static com.google.common.collect.Lists.newArrayList; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.apache.karaf.shell.commands.Option; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.cli.Comparators; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.flow.FlowRuleService; +import org.onosproject.net.flow.TableStatisticsEntry; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Lists port statistic of all ports in the system. + */ +@Command(scope = "onos", name = "tablestats", + description = "Lists statistics of all tables in the device") +public class TableStatisticsCommand extends AbstractShellCommand { + + @Option(name = "-t", aliases = "--table", description = "Show human readable table format for statistics", + required = false, multiValued = false) + private boolean table = false; + + @Argument(index = 0, name = "uri", description = "Device ID", + required = false, multiValued = false) + String uri = null; + + private static final String FORMAT = + " table=%s, active=%s, lookedup=%s, matched=%s"; + + @Override + protected void execute() { + FlowRuleService flowService = get(FlowRuleService.class); + DeviceService deviceService = get(DeviceService.class); + + SortedMap<Device, List<TableStatisticsEntry>> deviceTableStats = + getSortedTableStats(deviceService, flowService); + + if (outputJson()) { + print("%s", json(deviceTableStats.keySet(), deviceTableStats)); + } else { + deviceTableStats.forEach((device, tableStats) -> printTableStats(device, tableStats)); + } + } + + /** + * Produces a JSON array of table statistics grouped by the each device. + * + * @param devices collection of devices + * @param deviceTableStats collection of table statistics per each device + * @return JSON array + */ + private JsonNode json(Iterable<Device> devices, + Map<Device, List<TableStatisticsEntry>> deviceTableStats) { + ObjectMapper mapper = new ObjectMapper(); + ArrayNode result = mapper.createArrayNode(); + for (Device device : devices) { + result.add(json(mapper, device, deviceTableStats.get(device))); + } + return result; + } + + // Produces JSON object with the table statistics of the given device. + private ObjectNode json(ObjectMapper mapper, + Device device, List<TableStatisticsEntry> tableStats) { + ObjectNode result = mapper.createObjectNode(); + ArrayNode array = mapper.createArrayNode(); + + tableStats.forEach(tableStat -> array.add(jsonForEntity(tableStat, TableStatisticsEntry.class))); + + result.put("device", device.id().toString()) + .put("tableCount", tableStats.size()) + .set("tables", array); + return result; + } + + /** + * Prints flow table statistics. + * + * @param d the device + * @param tableStats the set of flow table statistics for that device + */ + protected void printTableStats(Device d, + List<TableStatisticsEntry> tableStats) { + boolean empty = tableStats == null || tableStats.isEmpty(); + print("deviceId=%s, tableCount=%d", d.id(), empty ? 0 : tableStats.size()); + if (!empty) { + for (TableStatisticsEntry t : tableStats) { + print(FORMAT, t.tableId(), t.activeFlowEntries(), + t.packetsLookedup(), t.packetsMatched()); + } + } + } + + /** + * Returns the list of table statistics sorted using the device ID URIs and table IDs. + * + * @param deviceService device service + * @param flowService flow rule service + * @return sorted table statistics list + */ + protected SortedMap<Device, List<TableStatisticsEntry>> getSortedTableStats(DeviceService deviceService, + FlowRuleService flowService) { + SortedMap<Device, List<TableStatisticsEntry>> deviceTableStats = new TreeMap<>(Comparators.ELEMENT_COMPARATOR); + List<TableStatisticsEntry> tableStatsList; + Iterable<Device> devices = uri == null ? deviceService.getDevices() : + Collections.singletonList(deviceService.getDevice(DeviceId.deviceId(uri))); + for (Device d : devices) { + tableStatsList = newArrayList(flowService.getFlowTableStatistics(d.id())); + tableStatsList.sort((p1, p2) -> Integer.valueOf(p1.tableId()).compareTo(Integer.valueOf(p2.tableId()))); + deviceTableStats.put(d, tableStatsList); + } + return deviceTableStats; + } + +} diff --git a/framework/src/onos/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml index 459ffa96..28461e27 100644 --- a/framework/src/onos/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml +++ b/framework/src/onos/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -105,6 +105,18 @@ </completers> </command> <command> + <action class="org.onosproject.cli.net.DeviceControllersCommand"/> + <completers> + <ref component-id="deviceIdCompleter"/> + </completers> + </command> + <command> + <action class="org.onosproject.cli.net.DeviceSetControllersCommand"/> + <completers> + <ref component-id="deviceIdCompleter"/> + </completers> + </command> + <command> <action class="org.onosproject.cli.net.DeviceRemoveCommand"/> <completers> <ref component-id="deviceIdCompleter"/> @@ -222,6 +234,12 @@ </completers> </command> <command> + <action class="org.onosproject.cli.net.GetFlowStatistics"/> + <completers> + <ref component-id="deviceIdCompleter"/> + </completers> + </command> + <command> <action class="org.onosproject.cli.net.AddMultiPointToSinglePointIntentCommand"/> <completers> <ref component-id="connectPointCompleter"/> @@ -333,7 +351,19 @@ <command> <action class="org.onosproject.cli.net.InterfacesListCommand"/> </command> - + <command> + <action class="org.onosproject.cli.net.InterfaceAddCommand"/> + <optional-completers> + <entry key="-c" value-ref="connectPointCompleter"/> + <entry key="--connectPoint" value-ref="connectPointCompleter"/> + </optional-completers> + </command> + <command> + <action class="org.onosproject.cli.net.InterfaceRemoveCommand"/> + <completers> + <ref component-id="connectPointCompleter"/> + </completers> + </command> <command> <action class="org.onosproject.cli.net.GroupsListCommand"/> </command> @@ -343,6 +373,10 @@ </command> <command> + <action class="org.onosproject.cli.net.TableStatisticsCommand"/> + </command> + + <command> <action class="org.onosproject.cli.net.FlowsListCommand"/> <completers> <ref component-id="flowRuleStatusCompleter"/> diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/cfg/ComponentConfigService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/cfg/ComponentConfigService.java index a311002f..408a8933 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/cfg/ComponentConfigService.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/cfg/ComponentConfigService.java @@ -63,6 +63,16 @@ public interface ComponentConfigService { void setProperty(String componentName, String name, String value); /** + * Presets the value of the specified configuration property, regardless + * of the component's state. + * + * @param componentName component name + * @param name property name + * @param value new property value + */ + void preSetProperty(String componentName, String name, String value); + + /** * Clears the value of the specified configuration property thus making * the property take on its default value. * @@ -72,3 +82,4 @@ public interface ComponentConfigService { void unsetProperty(String componentName, String name); } + diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java index 4949bc40..8c5fb790 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/AnnotationKeys.java @@ -25,8 +25,10 @@ package org.onosproject.net; */ public final class AnnotationKeys { + // Prohibit instantiation - private AnnotationKeys() {} + private AnnotationKeys() { + } /** * Annotation key for instance name. @@ -125,12 +127,22 @@ public final class AnnotationKeys { public static final String OWNER = "owner"; /** + * Annotation key for the channel id. + */ + public static final String CHANNEL_ID = "channelId"; + + /** + * Annotation key for the management address. + */ + public static final String MANAGEMENT_ADDRESS = "managementAddress"; + + /** * Returns the value annotated object for the specified annotation key. * The annotated value is expected to be String that can be parsed as double. * If parsing fails, the returned value will be 1.0. * * @param annotated annotated object whose annotated value is obtained - * @param key key of annotation + * @param key key of annotation * @return double value of annotated object for the specified key */ public static double getAnnotatedValue(Annotated annotated, String key) { diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/DefaultDisjointPath.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/DefaultDisjointPath.java new file mode 100644 index 00000000..4895964f --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/DefaultDisjointPath.java @@ -0,0 +1,100 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.net; + +import org.onosproject.net.provider.ProviderId; + +import java.util.List; +import java.util.Objects; +import static com.google.common.collect.ImmutableSet.of; + +/** + * Default implementation of a network disjoint path pair. + */ +public class DefaultDisjointPath extends DefaultPath implements DisjointPath { + + private final DefaultPath path1; + private final DefaultPath path2; + + boolean usingPath1 = true; + + /** + * Creates a disjoint path pair from two default paths. + * + * @param providerId provider identity + * @param path1 primary path + * @param path2 backup path + */ + public DefaultDisjointPath(ProviderId providerId, DefaultPath path1, DefaultPath path2) { + super(providerId, path1.links(), path1.cost() + path2.cost()); + this.path1 = path1; + this.path2 = path2; + } + + @Override + public List<Link> links() { + if (usingPath1) { + return path1.links(); + } else { + return path2.links(); + } + } + + @Override + public double cost() { + if (usingPath1) { + return path1.cost(); + } + return path2.cost(); + } + + @Override + public Path primary() { + return path1; + } + + @Override + public Path backup() { + return path2; + } + + @Override + public int hashCode() { + return Objects.hash(of(path1, path2), src(), dst()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DefaultDisjointPath) { + final DefaultDisjointPath other = (DefaultDisjointPath) obj; + return Objects.equals(this.path1, other.path1) && Objects.equals(this.path2, other.path2); + } + return false; + } + + @Override + public boolean useBackup() { + if (path2 == null || path2.links() == null) { + return false; + } + usingPath1 = !usingPath1; + return true; + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/DisjointPath.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/DisjointPath.java new file mode 100644 index 00000000..3d54cbfc --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/DisjointPath.java @@ -0,0 +1,49 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.net; + + +/** + * Representation of a contiguous directed path in a network. Path comprises + * of a sequence of links, where adjacent links must share the same device, + * meaning that destination of the source of one link must coincide with the + * destination of the previous link. + */ +public interface DisjointPath extends Path { + + /** + * Uses backup path. + * + * @return boolean corresponding to whether request to use + * backup was successful. + */ + boolean useBackup(); + + /** + * Gets primary path. + * + * @return primary path + */ + Path primary(); + + /** + * Gets secondary path. + * + * @return secondary path + */ + Path backup(); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/ControllerConfig.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/ControllerConfig.java index bb8a788b..cd7cb977 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/ControllerConfig.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/ControllerConfig.java @@ -15,23 +15,27 @@ */ package org.onosproject.net.behaviour; +import org.onosproject.net.driver.HandlerBehaviour; + import java.util.List; /** * Device behaviour to obtain and set controllers at the device. */ -public interface ControllerConfig { +public interface ControllerConfig extends HandlerBehaviour { //TODO: add other controller parameters as needed. /** * Obtain the list of controller which are currently configured. + * * @return a list for controller descriptions */ List<ControllerInfo> getControllers(); /** * Set a list of controllers on a device. + * * @param controllers a list of controller descriptions */ void setControllers(List<ControllerInfo> controllers); diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/ControllerInfo.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/ControllerInfo.java index 9ff808a9..ded3b3ae 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/ControllerInfo.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/behaviour/ControllerInfo.java @@ -15,24 +15,112 @@ */ package org.onosproject.net.behaviour; +import com.google.common.base.Preconditions; import org.onlab.packet.IpAddress; +import java.util.Objects; + /** * Represents information for a device to connect to a controller. */ public class ControllerInfo { - public final IpAddress ip; - public final int tcpPort; + private IpAddress ip = IpAddress.valueOf("0.0.0.0"); + private int port = 6653; + private String type = "error"; /** * Information for contacting the controller. * - * @param ip the ip address - * @param tcpPort the tcp port + * @param ip the ip address + * @param port the tcp port */ - public ControllerInfo(IpAddress ip, int tcpPort) { + public ControllerInfo(IpAddress ip, int port, String type) { this.ip = ip; - this.tcpPort = tcpPort; + this.port = port; + this.type = type; + } + + /** + * Information for contacting the controller, if some information + * is not contained in the target string because it's optional + * it's leaved as in the field declaration (default values). + * + * @param target column returned from ovsdb query + */ + public ControllerInfo(String target) { + String[] data = target.split(":"); + this.type = data[0]; + Preconditions.checkArgument(!data[0].contains("unix"), + "Unable to create controller info " + + "from {} because it's based " + + "on unix sockets", target); + if (data[0].startsWith("p")) { + if (data.length >= 2) { + this.port = Integer.parseInt(data[1]); + } + if (data.length == 3) { + this.ip = IpAddress.valueOf(data[2]); + } + } else { + this.ip = IpAddress.valueOf(data[1]); + if (data.length == 3) { + this.port = Integer.parseInt(data[2]); + } + } + } + + /** + * Exposes the ip address of the controller. + * + * @return IpAddress ip address + */ + public IpAddress ip() { + return ip; + } + + /** + * Exposes the tcp port of the controller. + * + * @return int tcp port + */ + public int port() { + return port; + } + + /** + * Exposes the type of the controller connection. + * + * @return String type + */ + public String type() { + return type; + } + + public String target() { + if (type.startsWith("p")) { + return type + ":" + port + ":" + ip; + } else { + return type + ":" + ip + ":" + port; + } + } + + + @Override + public int hashCode() { + return Objects.hash(ip, port, type); + } + + @Override + public boolean equals(Object toBeCompared) { + if (toBeCompared instanceof ControllerInfo) { + ControllerInfo controllerInfo = (ControllerInfo) toBeCompared; + if (controllerInfo.type().equals(this.type) + && controllerInfo.ip().equals(this.ip()) + && controllerInfo.port() == this.port) { + return true; + } + } + return false; } } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/config/NetworkConfigService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/config/NetworkConfigService.java index c1eed980..8eb69a45 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/config/NetworkConfigService.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/config/NetworkConfigService.java @@ -41,27 +41,27 @@ public interface NetworkConfigService /** * Returns the subject factory with the specified key. * - * @param subjectKey subject class key + * @param subjectClassKey subject class key * @return subject class */ - SubjectFactory getSubjectFactory(String subjectKey); + SubjectFactory getSubjectFactory(String subjectClassKey); /** * Returns the subject factory for the specified class. * * @param subjectClass subject class - * @return subject class key + * @return subject class factory */ SubjectFactory getSubjectFactory(Class subjectClass); /** * Returns the configuration class with the specified key. * - * @param subjectKey subject class key - * @param configKey subject class name + * @param subjectClassKey subject class key + * @param configKey subject class name * @return subject class */ - Class<? extends Config> getConfigClass(String subjectKey, String configKey); + Class<? extends Config> getConfigClass(String subjectClassKey, String configKey); /** * Returns the set of subjects for which some configuration is available. diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/config/SubjectFactory.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/config/SubjectFactory.java index cd2db344..f992d727 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/config/SubjectFactory.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/config/SubjectFactory.java @@ -28,7 +28,7 @@ import com.google.common.annotations.Beta; public abstract class SubjectFactory<S> { private final Class<S> subjectClass; - private final String subjectKey; + private final String subjectClassKey; /** * Creates a new configuration factory for the specified class of subjects @@ -36,12 +36,12 @@ public abstract class SubjectFactory<S> { * subject and configuration class keys are used merely as keys for use in * composite JSON trees. * - * @param subjectClass subject class - * @param subjectKey subject class key + * @param subjectClass subject class + * @param subjectClassKey subject class key */ - protected SubjectFactory(Class<S> subjectClass, String subjectKey) { + protected SubjectFactory(Class<S> subjectClass, String subjectClassKey) { this.subjectClass = subjectClass; - this.subjectKey = subjectKey; + this.subjectClassKey = subjectClassKey; } /** @@ -60,8 +60,20 @@ public abstract class SubjectFactory<S> { * * @return configuration key */ - public String subjectKey() { - return subjectKey; + public String subjectClassKey() { + return subjectClassKey; + } + + /** + * Returns the unique key of the specified configuration subject. + * This is primarily aimed for use in composite JSON trees in external + * representations and has no bearing on the internal behaviours. + * + * @param subject specific subject + * @return subject key + */ + public String subjectKey(S subject) { + return subject.toString(); } /** diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/config/basics/BasicDeviceConfig.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/config/basics/BasicDeviceConfig.java index fd8bfa3e..afde9a9e 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/config/basics/BasicDeviceConfig.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/config/basics/BasicDeviceConfig.java @@ -25,6 +25,7 @@ public class BasicDeviceConfig extends BasicElementConfig<DeviceId> { public static final String TYPE = "type"; public static final String DRIVER = "driver"; + public static final String MANAGEMENT_ADDRESS = "managementAddress"; /** * Returns the device type. @@ -64,6 +65,25 @@ public class BasicDeviceConfig extends BasicElementConfig<DeviceId> { return (BasicElementConfig) setOrClear(DRIVER, driverName); } + /** + * Returns the device management ip (ip:port). + * + * @return device management address (ip:port) or null if not set + */ + public String managementAddress() { + return get(MANAGEMENT_ADDRESS, null); + } + + /** + * Sets the driver name. + * + * @param managementAddress new device management address (ip:port); null to clear + * @return self + */ + public BasicElementConfig managementAddress(String managementAddress) { + return (BasicElementConfig) setOrClear(MANAGEMENT_ADDRESS, managementAddress); + } + // TODO: device port meta-data to be configured via BasicPortsConfig // TODO: device credentials/keys diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/config/basics/SubjectFactories.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/config/basics/SubjectFactories.java index 884f2e20..311566b3 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/config/basics/SubjectFactories.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/config/basics/SubjectFactories.java @@ -43,6 +43,10 @@ public final class SubjectFactories { public ApplicationId createSubject(String key) { return coreService.registerApplication(key); } + @Override + public String subjectKey(ApplicationId subject) { + return subject.name(); + } }; public static final SubjectFactory<DeviceId> DEVICE_SUBJECT_FACTORY = @@ -59,6 +63,10 @@ public final class SubjectFactories { public ConnectPoint createSubject(String key) { return ConnectPoint.deviceConnectPoint(key); } + @Override + public String subjectKey(ConnectPoint subject) { + return key(subject); + } }; public static final SubjectFactory<HostId> HOST_SUBJECT_FACTORY = @@ -78,6 +86,10 @@ public final class SubjectFactories { return LinkKey.linkKey(ConnectPoint.deviceConnectPoint(cps[0]), ConnectPoint.deviceConnectPoint(cps[1])); } + @Override + public String subjectKey(LinkKey subject) { + return key(subject.src()) + "-" + key(subject.dst()); + } }; /** @@ -90,4 +102,8 @@ public final class SubjectFactories { coreService = service; } + private static String key(ConnectPoint subject) { + return subject.deviceId() + "/" + subject.port(); + } + } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTableStatisticsEntry.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTableStatisticsEntry.java new file mode 100644 index 00000000..929b285d --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTableStatisticsEntry.java @@ -0,0 +1,89 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.net.flow; + +import org.onosproject.net.DeviceId; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Default implementation of table statistics entry interface. + */ +public final class DefaultTableStatisticsEntry implements TableStatisticsEntry { + + private final DeviceId deviceId; + private final int tableId; + private final long activeFlowEntries; + private final long packetsLookedupCount; + private final long packetsMatchedCount; + + /** + * Default table statistics constructor. + * + * @param deviceId device identifier + * @param tableId table identifier + * @param activeFlowEntries number of active flow entries in the table + * @param packetsLookedupCount number of packets looked up in table + * @param packetsMatchedCount number of packets that hit table + */ + public DefaultTableStatisticsEntry(DeviceId deviceId, + int tableId, + long activeFlowEntries, + long packetsLookedupCount, + long packetsMatchedCount) { + this.deviceId = checkNotNull(deviceId); + this.tableId = tableId; + this.activeFlowEntries = activeFlowEntries; + this.packetsLookedupCount = packetsLookedupCount; + this.packetsMatchedCount = packetsMatchedCount; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("device: " + deviceId + ", "); + + sb.append("tableId: " + this.tableId + ", "); + sb.append("activeEntries: " + this.activeFlowEntries + ", "); + sb.append("packetsLookedUp: " + this.packetsLookedupCount + ", "); + sb.append("packetsMatched: " + this.packetsMatchedCount); + + return sb.toString(); + } + + @Override + public int tableId() { + return tableId; + } + + @Override + public long activeFlowEntries() { + return activeFlowEntries; + } + + @Override + public long packetsLookedup() { + return packetsLookedupCount; + } + + @Override + public long packetsMatched() { + return packetsMatchedCount; + } + + @Override + public DeviceId deviceId() { + return deviceId; + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java index f88c6bc3..4416456c 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficSelector.java @@ -23,22 +23,26 @@ import org.onlab.packet.MacAddress; import org.onlab.packet.MplsLabel; import org.onlab.packet.TpPort; import org.onlab.packet.VlanId; -import org.onosproject.net.IndexedLambda; import org.onosproject.net.PortNumber; import org.onosproject.net.flow.criteria.Criteria; import org.onosproject.net.flow.criteria.Criterion; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.TreeSet; /** * Default traffic selector implementation. */ public final class DefaultTrafficSelector implements TrafficSelector { + private static final Comparator<? super Criterion> TYPE_COMPARATOR = + (c1, c2) -> c1.type().compareTo(c2.type()); + private final Set<Criterion> criteria; private static final TrafficSelector EMPTY @@ -50,7 +54,9 @@ public final class DefaultTrafficSelector implements TrafficSelector { * @param criteria criteria */ private DefaultTrafficSelector(Set<Criterion> criteria) { - this.criteria = ImmutableSet.copyOf(criteria); + TreeSet<Criterion> elements = new TreeSet<>(TYPE_COMPARATOR); + elements.addAll(criteria); + this.criteria = ImmutableSet.copyOf(elements); } @Override @@ -345,18 +351,6 @@ public final class DefaultTrafficSelector implements TrafficSelector { return add(Criteria.matchIPv6ExthdrFlags(exthdrFlags)); } - @Deprecated - @Override - public Builder matchLambda(short lambda) { - return add(Criteria.matchLambda(new IndexedLambda(lambda))); - } - - @Deprecated - @Override - public Builder matchOpticalSignalType(short signalType) { - return add(Criteria.matchOpticalSignalType(signalType)); - } - @Override public TrafficSelector build() { return new DefaultTrafficSelector(ImmutableSet.copyOf(selector.values())); diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java index 5d18a9ad..a628725c 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java @@ -31,7 +31,6 @@ import org.onosproject.net.flow.instructions.Instruction; import org.onosproject.net.flow.instructions.Instructions; import org.onosproject.net.meter.MeterId; -import java.util.Collections; import java.util.List; import java.util.Objects; @@ -51,7 +50,7 @@ public final class DefaultTrafficTreatment implements TrafficTreatment { private final boolean hasClear; private static final DefaultTrafficTreatment EMPTY - = new DefaultTrafficTreatment(Collections.emptyList()); + = new DefaultTrafficTreatment(ImmutableList.of(Instructions.createNoAction())); private final Instructions.MeterInstruction meter; /** @@ -212,8 +211,6 @@ public final class DefaultTrafficTreatment implements TrafficTreatment { List<Instruction> current = immediate; - - // Creates a new builder private Builder() { } @@ -224,7 +221,10 @@ public final class DefaultTrafficTreatment implements TrafficTreatment { treatment.deferred().forEach(i -> add(i)); immediate(); - treatment.immediate().forEach(i -> add(i)); + treatment.immediate().stream() + // NOACTION will get re-added if there are no other actions + .filter(i -> i.type() != Instruction.Type.NOACTION) + .forEach(i -> add(i)); clear = treatment.clearedDeferred(); } @@ -234,6 +234,7 @@ public final class DefaultTrafficTreatment implements TrafficTreatment { switch (instruction.type()) { case DROP: + case NOACTION: case OUTPUT: case GROUP: case L0MODIFICATION: @@ -250,6 +251,7 @@ public final class DefaultTrafficTreatment implements TrafficTreatment { break; case METER: meter = (Instructions.MeterInstruction) instruction; + break; default: throw new IllegalArgumentException("Unknown instruction type: " + instruction.type()); @@ -258,9 +260,23 @@ public final class DefaultTrafficTreatment implements TrafficTreatment { return this; } + /** + * Add a NOACTION when DROP instruction is explicitly specified. + * + * @return the traffic treatment builder + */ @Override public Builder drop() { - return add(Instructions.createDrop()); + return add(Instructions.createNoAction()); + } + + /** + * Add a NOACTION when no instruction is specified. + * + * @return the traffic treatment builder + */ + private Builder noAction() { + return add(Instructions.createNoAction()); } @Override @@ -380,11 +396,6 @@ public final class DefaultTrafficTreatment implements TrafficTreatment { } @Override - public Builder transition(FlowRule.Type type) { - return add(Instructions.transition(type.ordinal())); - } - - @Override public Builder transition(Integer tableId) { return add(Instructions.transition(tableId)); } @@ -463,14 +474,11 @@ public final class DefaultTrafficTreatment implements TrafficTreatment { @Override public TrafficTreatment build() { - //Don't add DROP instruction by default when instruction - //set is empty. This will be handled in DefaultSingleTablePipeline - //driver. - - //if (deferred.size() == 0 && immediate.size() == 0 - // && table == null && !clear) { - // drop(); - //} + if (deferred.size() == 0 && immediate.size() == 0 + && table == null && !clear) { + immediate(); + noAction(); + } return new DefaultTrafficTreatment(deferred, immediate, table, clear, meta, meter); } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTypedFlowEntry.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTypedFlowEntry.java new file mode 100644 index 00000000..afceb14e --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTypedFlowEntry.java @@ -0,0 +1,122 @@ +/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.flow;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Default flow entry class with FlowLiveType value, IMMEDIATE_FLOW, SHORT_FLOW, MID_FLOW, LONG_FLOW.
+ */
+public class DefaultTypedFlowEntry extends DefaultFlowEntry
+ implements TypedStoredFlowEntry {
+ private FlowLiveType liveType;
+
+ /**
+ * Creates a typed flow entry from flow rule and its statistics, with default flow live type(IMMEDIATE_FLOW).
+ *
+ * @param rule the flow rule
+ * @param state the flow state
+ * @param life the flow duration since creation
+ * @param packets the flow packets count
+ * @param bytes the flow bytes count
+ *
+ */
+ public DefaultTypedFlowEntry(FlowRule rule, FlowEntryState state,
+ long life, long packets, long bytes) {
+ super(rule, state, life, packets, bytes);
+ this.liveType = FlowLiveType.IMMEDIATE_FLOW;
+ }
+
+ /**
+ * Creates a typed flow entry from flow rule, with default flow live type(IMMEDIATE_FLOW).
+ *
+ * @param rule the flow rule
+ *
+ */
+ public DefaultTypedFlowEntry(FlowRule rule) {
+ super(rule);
+ this.liveType = FlowLiveType.IMMEDIATE_FLOW;
+ }
+
+ /**
+ * Creates a typed flow entry from flow entry, with default flow live type(IMMEDIATE_FLOW).
+ *
+ * @param fe the flow entry
+ *
+ */
+ public DefaultTypedFlowEntry(FlowEntry fe) {
+ super(fe, fe.state(), fe.life(), fe.packets(), fe.bytes());
+ this.liveType = FlowLiveType.IMMEDIATE_FLOW;
+ }
+
+ /**
+ * Creates a typed flow entry from flow rule and flow live type.
+ *
+ * @param rule the flow rule
+ * @param liveType the flow live type
+ *
+ */
+ public DefaultTypedFlowEntry(FlowRule rule, FlowLiveType liveType) {
+ super(rule);
+ this.liveType = liveType;
+ }
+
+ /**
+ * Creates a typed flow entry from flow entry and flow live type.
+ *
+ * @param fe the flow rule
+ * @param liveType the flow live type
+ *
+ */
+ public DefaultTypedFlowEntry(FlowEntry fe, FlowLiveType liveType) {
+ super(fe, fe.state(), fe.life(), fe.packets(), fe.bytes());
+ this.liveType = liveType;
+ }
+
+ /**
+ * Creates a typed flow entry from flow rule, error code and flow live type.
+ *
+ * @param rule the flow rule
+ * @param errType the flow error type
+ * @param errCode the flow error code
+ * @param liveType the flow live type
+ *
+ */
+ public DefaultTypedFlowEntry(FlowRule rule, int errType, int errCode, FlowLiveType liveType) {
+ super(rule, errType, errCode);
+ this.liveType = liveType;
+ }
+
+ @Override
+ public FlowLiveType flowLiveType() {
+ return this.liveType;
+ }
+
+ @Override
+ public void setFlowLiveType(FlowLiveType liveType) {
+ this.liveType = liveType;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("entry", super.toString())
+ .add("type", liveType)
+ .toString();
+ }
+}
+
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java index a487cbc4..35d45fbd 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRule.java @@ -29,43 +29,6 @@ public interface FlowRule { int MIN_PRIORITY = 0; /** - * The FlowRule type is used to determine in which table the flow rule needs - * to be put for multi-table support switch. For single table switch, - * Default is used. - * - * @deprecated in Cardinal Release - */ - @Deprecated - enum Type { - /* - * Default type - used in flow rule for single table switch NOTE: this - * setting should not be used as Table 0 in a multi-table pipeline - */ - DEFAULT, - /* Used in flow entry for IP table */ - IP, - /* Used in flow entry for MPLS table */ - MPLS, - /* Used in flow entry for ACL table */ - ACL, - - /* VLAN-to-MPLS table */ - VLAN_MPLS, - - /* VLAN table */ - VLAN, - - /* Ethtype table */ - ETHER, - - /* Class of Service table */ - COS, - - /* Table 0 in a multi-table pipeline */ - FIRST, - } - - /** * Returns the ID of this flow. * * @return the flow ID diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRuleProviderService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRuleProviderService.java index 8a36a921..aefa96b4 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRuleProviderService.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRuleProviderService.java @@ -15,6 +15,8 @@ */ package org.onosproject.net.flow; +import java.util.List; + import org.onosproject.net.DeviceId; import org.onosproject.net.provider.ProviderService; @@ -41,6 +43,24 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries); /** + * Pushes the collection of flow entries currently applied on the given + * device without flowMissing process. + * + * @param deviceId device identifier + * @param flowEntries collection of flow rules + */ + void pushFlowMetricsWithoutFlowMissing(DeviceId deviceId, Iterable<FlowEntry> flowEntries); + + /** + * Pushes the collection of table statistics entries currently extracted + * from the given device. + * + * @param deviceId device identifier + * @param tableStatsEntries collection of flow table statistics entries + */ + void pushTableStatistics(DeviceId deviceId, List<TableStatisticsEntry> tableStatsEntries); + + /** * Indicates to the core that the requested batch operation has * been completed. * @@ -48,5 +68,4 @@ public interface FlowRuleProviderService extends ProviderService<FlowRuleProvide * @param operation the resulting outcome of the operation */ void batchOperationCompleted(long batchId, CompletedBatchOperation operation); - } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRuleService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRuleService.java index d4f959c3..ee8d5a98 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRuleService.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRuleService.java @@ -104,4 +104,11 @@ public interface FlowRuleService */ void apply(FlowRuleOperations ops); + /** + * Returns the collection of flow table statistics of the specified device. + * + * @param deviceId device identifier + * @return collection of flow table statistics + */ + Iterable<TableStatisticsEntry> getFlowTableStatistics(DeviceId deviceId); } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRuleStore.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRuleStore.java index cece9893..d81c73c9 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRuleStore.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/FlowRuleStore.java @@ -15,6 +15,8 @@ */ package org.onosproject.net.flow; +import java.util.List; + import org.onosproject.net.DeviceId; import org.onosproject.store.Store; @@ -93,4 +95,23 @@ public interface FlowRuleStore extends Store<FlowRuleBatchEvent, FlowRuleStoreDe * @return flow_removed event, or null if nothing removed */ FlowRuleEvent removeFlowRule(FlowEntry rule); + + /** + * Updates the flow table statistics of the specified device using + * the given statistics. + * + * @param deviceId device identifier + * @param tableStats list of table statistics + * @return ready to send event describing what occurred; + */ + FlowRuleEvent updateTableStatistics(DeviceId deviceId, + List<TableStatisticsEntry> tableStats); + + /** + * Returns the flow table statistics associated with a device. + * + * @param deviceId the device ID + * @return the flow table statistics + */ + Iterable<TableStatisticsEntry> getTableStatistics(DeviceId deviceId); } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TableStatisticsEntry.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TableStatisticsEntry.java new file mode 100644 index 00000000..563f31ce --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TableStatisticsEntry.java @@ -0,0 +1,59 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.net.flow; + +import org.onosproject.net.DeviceId; + +/** + * Interface for flow table statistics of a device. + */ +public interface TableStatisticsEntry { + + /** + * Returns the device Id. + * + * @return device id + */ + DeviceId deviceId(); + + /** + * Returns the table number. + * + * @return table number + */ + int tableId(); + + /** + * Returns the number of active flow entries in this table. + * + * @return the number of active flow entries + */ + long activeFlowEntries(); + + /** + * Returns the number of packets looked up in the table. + * + * @return the number of packets looked up in the table + */ + long packetsLookedup(); + + /** + * Returns the number of packets that successfully matched in the table. + * + * @return the number of packets that successfully matched in the table + */ + long packetsMatched(); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficSelector.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficSelector.java index 534f6b9e..1286ffc1 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficSelector.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficSelector.java @@ -386,30 +386,6 @@ public interface TrafficSelector { Builder matchIPv6ExthdrFlags(short exthdrFlags); /** - * Matches an optical signal ID or lambda. - * - * @param lambda lambda - * @return a selection builder - * @deprecated in Cardinal Release. - * Use {@link #add(Criterion)} with an instance created - * by {@link org.onosproject.net.flow.criteria.Criteria#matchLambda(org.onosproject.net.Lambda)}. - */ - @Deprecated - Builder matchLambda(short lambda); - - /** - * Matches an optical Signal Type. - * - * @param signalType signalType - * @return a selection builder - * @deprecated in Cardinal Release. - * Use {@link #add(Criterion)}} with an instance created - * by {@link org.onosproject.net.flow.criteria.Criteria#matchOchSignalType(org.onosproject.net.OchSignalType)}. - */ - @Deprecated - Builder matchOpticalSignalType(short signalType); - - /** * Builds an immutable traffic selector. * * @return traffic selector diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java index 1ce669c2..33753afa 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java @@ -269,16 +269,6 @@ public interface TrafficTreatment { Builder meter(MeterId meterId); /** - * Sets the next table type to transition to. - * - * @param type the table type - * @return a treatement builder - * @deprecated in Cardinal Release - */ - @Deprecated - Builder transition(FlowRule.Type type); - - /** * Sets the next table id to transition to. * * @param tableId the table table diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TypedStoredFlowEntry.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TypedStoredFlowEntry.java new file mode 100644 index 00000000..a93dc071 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TypedStoredFlowEntry.java @@ -0,0 +1,65 @@ +/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.flow;
+
+/**
+ * Represents a flow live type for a given flow entry.
+ */
+public interface TypedStoredFlowEntry extends StoredFlowEntry {
+ enum FlowLiveType {
+ /**
+ * Indicates that this rule has been submitted for addition immediately.
+ * Not necessarily collecting flow stats.
+ */
+ IMMEDIATE_FLOW,
+
+ /**
+ * Indicates that this rule has been submitted for a short time.
+ * Necessarily collecting flow stats every calAndPollInterval.
+ */
+ SHORT_FLOW,
+
+ /**
+ * Indicates that this rule has been submitted for a mid time.
+ * Necessarily collecting flow stats every midPollInterval.
+ */
+ MID_FLOW,
+
+ /**
+ * Indicates that this rule has been submitted for a long time.
+ * Necessarily collecting flow stats every longPollInterval.
+ */
+ LONG_FLOW,
+
+ /**
+ * Indicates that this rule has been submitted for UNKNOWN or ERROR.
+ * Not necessarily collecting flow stats.
+ */
+ UNKNOWN_FLOW
+ }
+
+ /**
+ * Gets the flow live type for this entry.
+ */
+ FlowLiveType flowLiveType();
+
+ /**
+ * Sets the new flow live type for this entry.
+ * @param liveType new flow live type.
+ */
+ void setFlowLiveType(FlowLiveType liveType);
+}
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/criteria/Criteria.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/criteria/Criteria.java index 0252cfbc..7e1d43a5 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/criteria/Criteria.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/criteria/Criteria.java @@ -461,18 +461,6 @@ public final class Criteria { } /** - * Creates a match on lambda field using the specified value. - * - * @param lambda lambda to match on (16 bits unsigned integer) - * @return match criterion - * @deprecated in Cardinal Release. Use {@link #matchLambda(Lambda)} instead. - */ - @Deprecated - public static Criterion matchLambda(int lambda) { - return new LambdaCriterion(lambda, Type.OCH_SIGID); - } - - /** * Creates a match on lambda using the specified value. * * @param lambda lambda @@ -489,18 +477,6 @@ public final class Criteria { } /** - * Creates a match on optical signal type using the specified value. - * - * @param sigType optical signal type (8 bits unsigned integer) - * @return match criterion - * @deprecated in Cardinal Release - */ - @Deprecated - public static Criterion matchOpticalSignalType(short sigType) { - return new OpticalSignalTypeCriterion(sigType, Type.OCH_SIGTYPE); - } - - /** * Create a match on OCh (Optical Channel) signal type. * * @param signalType OCh signal type diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java index 6f2cac6b..d01ea298 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java @@ -27,9 +27,18 @@ public interface Instruction { /** * Signifies that the traffic should be dropped. */ + @Deprecated DROP, /** + * Signifies that the traffic requires no action. + * + * In OF10, the behavior of NOACTION is DROP. + * In OF13, the behavior depends on current Action Set. + */ + NOACTION, + + /** * Signifies that the traffic should be output to a port. */ OUTPUT, diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java index c5358a29..c9f10685 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java @@ -68,11 +68,21 @@ public final class Instructions { * * @return drop instruction */ + @Deprecated public static DropInstruction createDrop() { return new DropInstruction(); } /** + * Creates a no action instruction. + * + * @return no action instruction + */ + public static NoActionInstruction createNoAction() { + return new NoActionInstruction(); + } + + /** * Creates a group instruction. * * @param groupId Group Id @@ -89,19 +99,6 @@ public final class Instructions { } /** - * Creates a l0 modification. - * - * @param lambda the lambda to modify to - * @return a l0 modification - * @deprecated in Cardinal Release. Use {@link #modL0Lambda(Lambda)} instead. - */ - @Deprecated - public static L0ModificationInstruction modL0Lambda(short lambda) { - checkNotNull(lambda, "L0 lambda cannot be null"); - return new ModLambdaInstruction(L0SubType.LAMBDA, lambda); - } - - /** * Creates an L0 modification with the specified OCh signal. * * @param lambda OCh signal @@ -303,21 +300,6 @@ public final class Instructions { * * @param etherType Ethernet type to set * @return a L2 modification. - * @deprecated in Cardinal Release - */ - @Deprecated - public static Instruction popMpls(int etherType) { - checkNotNull(etherType, "Ethernet type cannot be null"); - return new L2ModificationInstruction.PushHeaderInstructions( - L2ModificationInstruction.L2SubType.MPLS_POP, new EthType(etherType)); - } - - - /** - * Creates a pop MPLS header instruction with a particular ethertype. - * - * @param etherType Ethernet type to set - * @return a L2 modification. */ public static Instruction popMpls(EthType etherType) { checkNotNull(etherType, "Ethernet type cannot be null"); @@ -478,6 +460,7 @@ public final class Instructions { /** * Drop instruction. */ + @Deprecated public static final class DropInstruction implements Instruction { private DropInstruction() {} @@ -510,6 +493,40 @@ public final class Instructions { } /** + * No Action instruction. + */ + public static final class NoActionInstruction implements Instruction { + + private NoActionInstruction() {} + + @Override + public Type type() { + return Type.NOACTION; + } + + @Override + public String toString() { + return toStringHelper(type().toString()).toString(); + } + + @Override + public int hashCode() { + return Objects.hash(type().ordinal()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof NoActionInstruction) { + return true; + } + return false; + } + } + + /** * Output Instruction. */ public static final class OutputInstruction implements Instruction { diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/host/HostProviderService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/host/HostProviderService.java index f7b7c499..068663bd 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/host/HostProviderService.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/host/HostProviderService.java @@ -15,6 +15,7 @@ */ package org.onosproject.net.host; +import org.onlab.packet.IpAddress; import org.onosproject.net.HostId; import org.onosproject.net.provider.ProviderService; @@ -29,6 +30,7 @@ public interface HostProviderService extends ProviderService<HostProvider> { * * @param hostId id of the host that been detected * @param hostDescription description of host and its location + * @deprecated in Drake release */ @Deprecated default void hostDetected(HostId hostId, HostDescription hostDescription) { @@ -52,4 +54,11 @@ public interface HostProviderService extends ProviderService<HostProvider> { */ void hostVanished(HostId hostId); + /** + * Notifies the core when a host is no longer detected on a network. + * + * @param hostId id of the host that vanished + */ + void removeIpFromHost(HostId hostId, IpAddress ipAddress); + } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/host/HostStore.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/host/HostStore.java index 5894fe92..918ced45 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/host/HostStore.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/host/HostStore.java @@ -55,6 +55,15 @@ public interface HostStore extends Store<HostEvent, HostStoreDelegate> { HostEvent removeHost(HostId hostId); /** + * Removes the specified ip from the host entry. + * + * @param hostId host identification + * @param ipAddress ipAddress to be removed + * @return remove event or null if host was not found + */ + HostEvent removeIp(HostId hostId, IpAddress ipAddress); + + /** * Returns the number of hosts in the store. * * @return host count diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/FlowRuleIntent.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/FlowRuleIntent.java index 0646a003..2a2d7c78 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/FlowRuleIntent.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/intent/FlowRuleIntent.java @@ -23,7 +23,6 @@ import org.onosproject.net.NetworkResource; import org.onosproject.net.flow.FlowRule; import java.util.Collection; -import java.util.Collections; import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; @@ -38,18 +37,6 @@ public class FlowRuleIntent extends Intent { private final Collection<FlowRule> flowRules; /** - * Creates an flow rule intent with the specified flow rules to be set. - * - * @param appId application id - * @param flowRules flow rules to be set. - * @deprecated in Cardinal Release - */ - @Deprecated - public FlowRuleIntent(ApplicationId appId, List<FlowRule> flowRules) { - this(appId, null, flowRules, Collections.emptyList()); - } - - /** * Creates a flow rule intent with the specified flow rules and resources. * * @param appId application id diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastEvent.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastEvent.java new file mode 100644 index 00000000..979194c3 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastEvent.java @@ -0,0 +1,118 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.net.mcast; + +import com.google.common.annotations.Beta; +import org.onosproject.event.AbstractEvent; +import org.onosproject.net.ConnectPoint; + +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; + +/** + * An entity representing a multicast event. Event either add or remove + * sinks or sources. + */ +@Beta +public class McastEvent extends AbstractEvent<McastEvent.Type, McastRoute> { + + private final Optional<ConnectPoint> sink; + private final Optional<ConnectPoint> source; + + public enum Type { + /** + * A new mcast route has been added. + */ + ROUTE_ADDED, + + /** + * A mcast route has been removed. + */ + ROUTE_REMOVED, + + /** + * A source for a mcast route (ie. the subject) has been added. + */ + SOURCE_ADDED, + + /** + * A sink for a mcast route (ie. the subject) has been added. + */ + SINK_ADDED, + + /** + * A source for a mcast route (ie. the subject) has been removed. + */ + SINK_REMOVED + } + + private McastEvent(McastEvent.Type type, McastRoute subject) { + super(type, subject); + sink = Optional.empty(); + source = Optional.empty(); + } + + private McastEvent(McastEvent.Type type, McastRoute subject, long time) { + super(type, subject, time); + sink = Optional.empty(); + source = Optional.empty(); + } + + public McastEvent(McastEvent.Type type, McastRoute subject, + ConnectPoint sink, + ConnectPoint source) { + super(type, subject); + this.sink = Optional.ofNullable(sink); + this.source = Optional.ofNullable(source); + } + + public McastEvent(McastEvent.Type type, McastRoute subject, long time, + ConnectPoint sink, + ConnectPoint source) { + super(type, subject, time); + this.sink = Optional.ofNullable(sink); + this.source = Optional.ofNullable(source); + } + + /** + * The sink which has been removed or added. The field may not be set + * if the sink has not been detected yet or has been removed. + * + * @return an optional connect point + */ + public Optional<ConnectPoint> sink() { + return sink; + } + + /** + * The source which has been removed or added. + + * @return an optional connect point + */ + public Optional<ConnectPoint> source() { + return source; + } + + @Override + public String toString() { + return toStringHelper(this) + .add("type", type()) + .add("route", subject()) + .add("source", source) + .add("sinks", sink).toString(); + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastListener.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastListener.java new file mode 100644 index 00000000..06449b99 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastListener.java @@ -0,0 +1,26 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.net.mcast; + +import com.google.common.annotations.Beta; +import org.onosproject.event.EventListener; + +/** + * A listener interface for multicast events. + */ +@Beta +public interface McastListener extends EventListener<McastEvent> { +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastRoute.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastRoute.java new file mode 100644 index 00000000..ff1292bf --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/McastRoute.java @@ -0,0 +1,117 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.net.mcast; + +import com.google.common.annotations.Beta; +import com.google.common.base.Objects; +import org.onlab.packet.IpPrefix; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * An entity representing a multicast route consisting of a source + * and a multicast group address. + */ +@Beta +public class McastRoute { + + public enum Type { + /** + * Route originates from PIM. + */ + PIM, + + /** + * Route originates from IGMP. + */ + IGMP, + + /** + * Route originates from other config (ie. REST, CLI). + */ + STATIC + } + + private final IpPrefix source; + private final IpPrefix group; + private final Type type; + + public McastRoute(IpPrefix source, IpPrefix group, Type type) { + checkNotNull(source, "Multicast route must have a source"); + checkNotNull(group, "Multicast route must specify a group address"); + checkNotNull(type, "Must indicate what type of route"); + this.source = source; + this.group = group; + this.type = type; + } + + /** + * Fetches the source address of this route. + * + * @return an ip address + */ + public IpPrefix source() { + return source; + } + + /** + * Fetches the group address of this route. + * + * @return an ip address + */ + public IpPrefix group() { + return group; + } + + /** + * Obtains how this route was created. + * @return a type of route + + */ + public Type type() { + return type; + } + + @Override + public String toString() { + return toStringHelper(this) + .add("source", source) + .add("group", group) + .add("origin", type) + .toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + McastRoute that = (McastRoute) o; + return Objects.equal(source, that.source) && + Objects.equal(group, that.group) && + Objects.equal(type, that.type); + } + + @Override + public int hashCode() { + return Objects.hashCode(source, group, type); + } + +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/MulticastRouteService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/MulticastRouteService.java new file mode 100644 index 00000000..56e87c55 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/MulticastRouteService.java @@ -0,0 +1,84 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.net.mcast; + +import com.google.common.annotations.Beta; +import org.onosproject.net.ConnectPoint; + +import java.util.List; + +/** + * A service interface for maintaining multicast information. + */ +@Beta +public interface MulticastRouteService { + + /** + * Adds a route to the information base. + * + * @param route a multicast route + */ + void add(McastRoute route); + + /** + * Removes a route from the information base. + * + * @param route a multicast route + */ + void remove(McastRoute route); + + /** + * Adds a source connection to the route from where the + * data stream is originating. + * + * @param route the multicast route + * @param connectPoint a source connect point + */ + void addSource(McastRoute route, ConnectPoint connectPoint); + + /** + * Adds a sink to the route to which a data stream should be + * sent to. + * + * @param route a multicast route + * @param connectPoint a sink connect point + */ + void addSink(McastRoute route, ConnectPoint connectPoint); + + /** + * Removes a sink from the route. + * + * @param route the multicast route + * @param connectPoint a sink connect point + */ + void removeSink(McastRoute route, ConnectPoint connectPoint); + + /** + * Find the data source association for this multicast route. + * + * @param route a multicast route + * @return a connect point + */ + ConnectPoint fetchSource(McastRoute route); + + /** + * Find the list of sinks for this route. + * + * @param route a multicast route + * @return a list of connect points + */ + List<ConnectPoint> fetchSinks(McastRoute route); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/package-info.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/package-info.java new file mode 100644 index 00000000..e8dcc7b8 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/mcast/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * External model entities of the multicast RIB. + */ +package org.onosproject.net.mcast;
\ No newline at end of file diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterRequest.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterRequest.java index 94cada47..51394c30 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterRequest.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/DefaultMeterRequest.java @@ -43,7 +43,8 @@ public final class DefaultMeterRequest implements MeterRequest { private DefaultMeterRequest(DeviceId deviceId, ApplicationId appId, Meter.Unit unit, boolean burst, - Collection<Band> bands, MeterContext context, Type op) { + Collection<Band> bands, MeterContext context, + Type op) { this.deviceId = deviceId; this.appId = appId; this.unit = unit; @@ -58,6 +59,7 @@ public final class DefaultMeterRequest implements MeterRequest { return deviceId; } + @Override public ApplicationId appId() { return appId; @@ -107,6 +109,7 @@ public final class DefaultMeterRequest implements MeterRequest { private Collection<Band> bands; private DeviceId deviceId; private MeterContext context; + private Optional<MeterId> desiredId = Optional.empty(); @Override diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/MeterKey.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/MeterKey.java new file mode 100644 index 00000000..5bc01b02 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/MeterKey.java @@ -0,0 +1,72 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.net.meter; + +import com.google.common.base.Objects; +import org.onosproject.net.DeviceId; + +import static com.google.common.base.MoreObjects.toStringHelper; + +/** + * A meter key represents a meter uniquely. + */ +public final class MeterKey { + + private final DeviceId deviceId; + private final MeterId id; + + private MeterKey(DeviceId deviceId, MeterId id) { + this.deviceId = deviceId; + this.id = id; + } + + public DeviceId deviceId() { + return deviceId; + } + + public MeterId meterId() { + return id; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MeterKey meterKey = (MeterKey) o; + return Objects.equal(deviceId, meterKey.deviceId) && + Objects.equal(id, meterKey.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(deviceId, id); + } + + @Override + public String toString() { + return toStringHelper(this) + .add("deviceId", deviceId) + .add("meterId", id).toString(); + } + + public static MeterKey key(DeviceId deviceId, MeterId id) { + return new MeterKey(deviceId, id); + } +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/MeterService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/MeterService.java index bdc90eb7..2e07cb67 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/MeterService.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/MeterService.java @@ -16,6 +16,7 @@ package org.onosproject.net.meter; import org.onosproject.event.ListenerService; +import org.onosproject.net.DeviceId; import java.util.Collection; @@ -46,10 +47,11 @@ public interface MeterService /** * Fetch the meter by the meter id. * + * @param deviceId a device id * @param id a meter id * @return a meter */ - Meter getMeter(MeterId id); + Meter getMeter(DeviceId deviceId, MeterId id); /** * Fetches all the meters. diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/MeterStore.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/MeterStore.java index 5112a4a3..f429e95a 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/MeterStore.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/meter/MeterStore.java @@ -57,12 +57,12 @@ public interface MeterStore extends Store<MeterEvent, MeterStoreDelegate> { void updateMeterState(Meter meter); /** - * Obtains a meter matching the given meter id. + * Obtains a meter matching the given meter key. * - * @param meterId a meter id + * @param key a meter key * @return a meter */ - Meter getMeter(MeterId meterId); + Meter getMeter(MeterKey key); /** * Returns all meters stored in the store. diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/newresource/ResourceService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/newresource/ResourceService.java index 618042a3..82d84743 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/newresource/ResourceService.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/newresource/ResourceService.java @@ -125,6 +125,15 @@ public interface ResourceService { boolean release(ResourceConsumer consumer); /** + * Returns resource allocation of the specified resource. + * + * @param resource resource to check the allocation + * @return allocation information enclosed by Optional. + * If the resource is not allocated, the return value is empty. + */ + Optional<ResourceAllocation> getResourceAllocation(ResourcePath resource); + + /** * Returns allocated resources being as children of the specified parent and being the specified resource type. * * @param parent parent resource path diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketProcessorEntry.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketProcessorEntry.java new file mode 100644 index 00000000..40386fb7 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketProcessorEntry.java @@ -0,0 +1,58 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.net.packet; + +/** + * Packet processor entry tracking the processor, its priority and + * time consumption. + */ +public interface PacketProcessorEntry { + + /** + * Returns the packet processor. + * + * @return packet processor + */ + PacketProcessor processor(); + + /** + * Returns the packet processor priority. + * + * @return processor priority + */ + int priority(); + + /** + * Returns the number of invocations. + * + * @return number of invocations + */ + long invocations(); + + /** + * Returns the total time, in nanoseconds, spent processing packets. + * + * @return total time in nanos + */ + long totalNanos(); + + /** + * Returns the average time, in nanoseconds, spent processing packets. + * + * @return average time in nanos + */ + long averageNanos(); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketService.java index 98f4d8e0..2e7a1b91 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketService.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketService.java @@ -20,7 +20,6 @@ import org.onosproject.core.ApplicationId; import org.onosproject.net.flow.TrafficSelector; import java.util.List; -import java.util.Map; /** * Service for intercepting data plane packets and for emitting synthetic @@ -52,13 +51,12 @@ public interface PacketService { void removeProcessor(PacketProcessor processor); /** - * Returns priority bindings of all registered packet processors. + * Returns priority bindings of all registered packet processor entries. * - * @return list of existing packet processors + * @return list of existing packet processor entries */ @Beta - // TODO: Consider returning list of PacketProcessorEntry with processor, priority and stats - Map<Integer, PacketProcessor> getProcessors(); + List<PacketProcessorEntry> getProcessors(); /** * Requests that packets matching the given selector are punted from the diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketStore.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketStore.java index 97f7cb55..d83fc9a2 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketStore.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketStore.java @@ -37,17 +37,15 @@ public interface PacketStore extends Store<PacketEvent, PacketStoreDelegate> { * Requests intercept of packets that match the given selector. * * @param request a packet request - * @return true if the first time the given selector was requested */ - boolean requestPackets(PacketRequest request); + void requestPackets(PacketRequest request); /** * Cancels intercept of packets that match the given selector. * * @param request a packet request - * @return true if there is no other application requesting the given selector */ - boolean cancelPackets(PacketRequest request); + void cancelPackets(PacketRequest request); /** * Obtains all existing requests in the system. diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketStoreDelegate.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketStoreDelegate.java index bf5c3cc0..2e59b19d 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketStoreDelegate.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/packet/PacketStoreDelegate.java @@ -21,4 +21,20 @@ import org.onosproject.store.StoreDelegate; * Packet store delegate abstraction. */ public interface PacketStoreDelegate extends StoreDelegate<PacketEvent> { + + /** + * Requests that packets matching to following request be collected + * from all switches. + * + * @param request packet request + */ + void requestPackets(PacketRequest request); + + /** + * Requests that packets matching to following request no longer be + * collected from any switches. + * + * @param request packet request + */ + void cancelPackets(PacketRequest request); } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/device/IntentSetMultimap.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/device/IntentSetMultimap.java new file mode 100644 index 00000000..67c539df --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/device/IntentSetMultimap.java @@ -0,0 +1,47 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.net.resource.device; + +import org.onosproject.net.intent.IntentId; + +import java.util.Set; + +public interface IntentSetMultimap { + + /** + * Allocates the mapping between the given intents. + * + * @param keyIntentId key intent ID + * @param valIntentId value intent ID + * @return true if mapping was successful, false otherwise + */ + boolean allocateMapping(IntentId keyIntentId, IntentId valIntentId); + + /** + * Returns the set of intents mapped to a lower intent. + * + * @param intentId intent ID + * @return set of intent IDs + */ + Set<IntentId> getMapping(IntentId intentId); + + /** + * Releases the mapping of the given intent. + * + * @param intentId intent ID + */ + void releaseMapping(IntentId intentId); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/BandwidthResourceRequest.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/BandwidthResourceRequest.java index 91cc3d19..e07309cb 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/BandwidthResourceRequest.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/resource/link/BandwidthResourceRequest.java @@ -64,7 +64,7 @@ public class BandwidthResourceRequest implements ResourceRequest { if (obj == null || getClass() != obj.getClass()) { return false; } - final BandwidthResourceAllocation other = (BandwidthResourceAllocation) obj; + final BandwidthResourceRequest other = (BandwidthResourceRequest) obj; return Objects.equals(this.bandwidth, other.bandwidth()); } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/FlowStatisticService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/FlowStatisticService.java new file mode 100644 index 00000000..f59670bc --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/FlowStatisticService.java @@ -0,0 +1,105 @@ +/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.statistic;
+
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.TypedStoredFlowEntry;
+import org.onosproject.net.flow.instructions.Instruction;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Service for obtaining individual flow statistic information about device and link in the system.
+ * Basic statistics are obtained from the StatisticService
+ */
+public interface FlowStatisticService {
+
+ /**
+ * Obtain the summary load list for the device with the given link.
+ *
+ * @param device the Device to query.
+ * @return map of summary flow entry load
+ */
+ Map<ConnectPoint, SummaryFlowEntryWithLoad> loadSummary(Device device);
+
+ /**
+ * Obtain the summary load for the device with the given link or port.
+ *
+ * @param device the Device to query.
+ * @param pNumber the port number to query.
+ * @return summary flow entry load
+ */
+ SummaryFlowEntryWithLoad loadSummary(Device device, PortNumber pNumber);
+
+ /**
+ * Obtain the set of the flow type and load list for the device with the given link.
+ *
+ * @param device the Device to query.
+ * @param liveType the FlowLiveType to filter, null means no filtering .
+ * @param instType the InstructionType to filter, null means no filtering.
+ * @return map of flow entry load
+ */
+ Map<ConnectPoint, List<TypedFlowEntryWithLoad>> loadAllByType(Device device,
+ TypedStoredFlowEntry.FlowLiveType liveType,
+ Instruction.Type instType);
+
+ /**
+ * Obtain the flow type and load list for the device with the given link or port.
+ *
+ * @param device the Device to query.
+ * @param pNumber the port number of the Device to query
+ * @param liveType the FlowLiveType to filter, null means no filtering .
+ * @param instType the InstructionType to filter, null means no filtering.
+ * @return list of flow entry load
+ */
+ List<TypedFlowEntryWithLoad> loadAllByType(Device device, PortNumber pNumber,
+ TypedStoredFlowEntry.FlowLiveType liveType,
+ Instruction.Type instType);
+
+ /**
+ * Obtain the set of the flow type and load topn list for the device with the given link.
+ *
+ * @param device the Device to query.
+ * @param liveType the FlowLiveType to filter, null means no filtering .
+ * @param instType the InstructionType to filter, null means no filtering.
+ * @param topn the top number to filter, null means no filtering.
+ * @return map of flow entry load
+ */
+ Map<ConnectPoint, List<TypedFlowEntryWithLoad>> loadTopnByType(Device device,
+ TypedStoredFlowEntry.FlowLiveType liveType,
+ Instruction.Type instType,
+ int topn);
+
+ /**
+ * Obtain the flow type and load topn list for the device with the given link or port.
+ *
+ * @param device the Device to query.
+ * @param pNumber the port number of the Device to query
+ * @param liveType the FlowLiveType to filter, null means no filtering .
+ * @param instType the InstructionType to filter, null means no filtering.
+ * @return list of flow entry load
+ */
+ List<TypedFlowEntryWithLoad> loadTopnByType(Device device, PortNumber pNumber,
+ TypedStoredFlowEntry.FlowLiveType liveType,
+ Instruction.Type instType,
+ int topn);
+}
+
+
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/FlowStatisticStore.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/FlowStatisticStore.java new file mode 100644 index 00000000..3c2aa89b --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/FlowStatisticStore.java @@ -0,0 +1,65 @@ +/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.statistic;
+
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+
+import java.util.Set;
+
+/**
+ * Flow Store to house the computed statistics.
+ */
+public interface FlowStatisticStore {
+ /**
+ * Remove entries associated with this rule.
+ *
+ * @param rule {@link org.onosproject.net.flow.FlowRule}
+ */
+ void removeFlowStatistic(FlowRule rule);
+
+ /**
+ * Adds a flow stats observation for a flow rule. The previous flow will be removed.
+ *
+ * @param rule a {@link org.onosproject.net.flow.FlowEntry}
+ */
+ void addFlowStatistic(FlowEntry rule);
+
+ /**
+ * Updates a stats observation for a flow rule. The old flow stats will be moved to previous stats.
+ *
+ * @param rule a {@link org.onosproject.net.flow.FlowEntry}
+ */
+ void updateFlowStatistic(FlowEntry rule);
+
+ /**
+ * Fetches the current observed flow stats values.
+ *
+ * @param connectPoint the port to fetch information for
+ * @return set of current flow rules
+ */
+ Set<FlowEntry> getCurrentFlowStatistic(ConnectPoint connectPoint);
+
+ /**
+ * Fetches the current observed flow stats values.
+ *
+ * @param connectPoint the port to fetch information for
+ * @return set of current values
+ */
+ Set<FlowEntry> getPreviousFlowStatistic(ConnectPoint connectPoint);
+}
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/SummaryFlowEntryWithLoad.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/SummaryFlowEntryWithLoad.java new file mode 100644 index 00000000..60da636a --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/SummaryFlowEntryWithLoad.java @@ -0,0 +1,143 @@ +/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.statistic;
+
+import org.onosproject.net.ConnectPoint;
+
+/**
+ * Summary Load classified by flow live type.
+ */
+public class SummaryFlowEntryWithLoad {
+ private ConnectPoint cp;
+ private Load totalLoad;
+ private Load immediateLoad;
+ private Load shortLoad;
+ private Load midLoad;
+ private Load longLoad;
+ private Load unknownLoad;
+
+ /**
+ * Creates a new summary flow entry having load for the given connect point and total load.
+ *
+ * @param cp connect point
+ * @param totalLoad total load
+ */
+ public SummaryFlowEntryWithLoad(ConnectPoint cp, Load totalLoad) {
+ this.cp = cp;
+ this.totalLoad = totalLoad;
+ this.immediateLoad = new DefaultLoad();
+ this.shortLoad = new DefaultLoad();
+ this.midLoad = new DefaultLoad();
+ this.longLoad = new DefaultLoad();
+ this.unknownLoad = new DefaultLoad();
+ }
+
+ /**
+ * Creates a new summary flow entry having load for the given connect point
+ * and total, immediate, short, mid, and long load.
+ *
+ * @param cp connect point
+ * @param totalLoad total load
+ * @param immediateLoad immediate load
+ * @param shortLoad short load
+ * @param midLoad mid load
+ * @param longLoad long load
+ */
+ public SummaryFlowEntryWithLoad(ConnectPoint cp,
+ Load totalLoad, Load immediateLoad, Load shortLoad, Load midLoad, Load longLoad) {
+ this.cp = cp;
+ this.totalLoad = totalLoad;
+ this.immediateLoad = immediateLoad;
+ this.shortLoad = shortLoad;
+ this.midLoad = midLoad;
+ this.longLoad = longLoad;
+ this.unknownLoad = new DefaultLoad();
+ }
+
+ /**
+ * Creates a new summary flow entry having load for the given connect point
+ * and total, immediate, short, mid, long, and unknown load.
+ *
+ * @param cp connect point
+ * @param totalLoad total load
+ * @param immediateLoad immediate load
+ * @param shortLoad short load
+ * @param midLoad mid load
+ * @param longLoad long load
+ * @param unknownLoad long load
+ */
+ public SummaryFlowEntryWithLoad(ConnectPoint cp,
+ Load totalLoad, Load immediateLoad,
+ Load shortLoad, Load midLoad, Load longLoad, Load unknownLoad) {
+ this.cp = cp;
+ this.totalLoad = totalLoad;
+ this.immediateLoad = immediateLoad;
+ this.shortLoad = shortLoad;
+ this.midLoad = midLoad;
+ this.longLoad = longLoad;
+ this.unknownLoad = unknownLoad;
+ }
+
+ /**
+ * Returns connect point.
+ */
+ public ConnectPoint connectPoint() {
+ return cp;
+ }
+
+ /**
+ * Returns total load of connect point.
+ */
+ public Load totalLoad() {
+ return totalLoad;
+ }
+
+ /**
+ * Returns immediate load of connect point.
+ */
+ public Load immediateLoad() {
+ return immediateLoad;
+ }
+
+ /**
+ * Returns short load of connect point.
+ */
+ public Load shortLoad() {
+ return shortLoad;
+ }
+
+ /**
+ * Returns mid load of connect point.
+ */
+ public Load midLoad() {
+ return midLoad;
+ }
+
+ /**
+ * Returns long load of connect point.
+ */
+ public Load longLoad() {
+ return longLoad;
+ }
+
+ /**
+ * Returns unknown load of connect point.
+ */
+ public Load unknownLoad() {
+ return unknownLoad;
+ }
+}
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/TypedFlowEntryWithLoad.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/TypedFlowEntryWithLoad.java new file mode 100644 index 00000000..3e2dbdf8 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/statistic/TypedFlowEntryWithLoad.java @@ -0,0 +1,143 @@ +/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.statistic;
+
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.TypedStoredFlowEntry;
+import org.onosproject.net.flow.DefaultTypedFlowEntry;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Load of flow entry of flow live type.
+ */
+public class TypedFlowEntryWithLoad {
+ private ConnectPoint cp;
+ private TypedStoredFlowEntry tfe;
+ private Load load;
+
+ //TODO: make this variables class, and share with NewAdaptivceFlowStatsCollector class
+ private static final int CAL_AND_POLL_INTERVAL = 5; // means SHORT_POLL_INTERVAL
+ private static final int MID_POLL_INTERVAL = 10;
+ private static final int LONG_POLL_INTERVAL = 15;
+
+
+ public TypedFlowEntryWithLoad(ConnectPoint cp, TypedStoredFlowEntry tfe, Load load) {
+ this.cp = cp;
+ this.tfe = tfe;
+ this.load = load;
+ }
+
+ public TypedFlowEntryWithLoad(ConnectPoint cp, TypedStoredFlowEntry tfe) {
+ this.cp = cp;
+ this.tfe = tfe;
+ this.load = new DefaultLoad(tfe.bytes(), 0, typedPollInterval(tfe));
+ }
+
+ public TypedFlowEntryWithLoad(ConnectPoint cp, FlowEntry fe) {
+ this.cp = cp;
+ this.tfe = newTypedStoredFlowEntry(fe);
+ this.load = new DefaultLoad(fe.bytes(), 0, typedPollInterval(this.tfe));
+ }
+
+ public ConnectPoint connectPoint() {
+ return cp;
+ }
+ public TypedStoredFlowEntry typedStoredFlowEntry() {
+ return tfe;
+ }
+ public Load load() {
+ return load;
+ }
+ public void setLoad(Load load) {
+ this.load = load;
+ }
+
+ /**
+ * Returns short polling interval.
+ */
+ public static int shortPollInterval() {
+ return CAL_AND_POLL_INTERVAL;
+ }
+
+ /**
+ * Returns mid polling interval.
+ */
+ public static int midPollInterval() {
+ return MID_POLL_INTERVAL;
+ }
+
+ /**
+ * Returns long polling interval.
+ */
+ public static int longPollInterval() {
+ return LONG_POLL_INTERVAL;
+ }
+
+ /**
+ * Returns average polling interval.
+ */
+ public static int avgPollInterval() {
+ return (CAL_AND_POLL_INTERVAL + MID_POLL_INTERVAL + LONG_POLL_INTERVAL) / 3;
+ }
+
+ /**
+ * Returns current typed flow entry's polling interval.
+ *
+ * @param tfe typed flow entry
+ */
+ public static long typedPollInterval(TypedStoredFlowEntry tfe) {
+ checkNotNull(tfe, "TypedStoredFlowEntry cannot be null");
+
+ switch (tfe.flowLiveType()) {
+ case LONG_FLOW:
+ return LONG_POLL_INTERVAL;
+ case MID_FLOW:
+ return MID_POLL_INTERVAL;
+ case SHORT_FLOW:
+ case IMMEDIATE_FLOW:
+ default:
+ return CAL_AND_POLL_INTERVAL;
+ }
+ }
+
+ /**
+ * Creates a new typed flow entry with the given flow entry fe.
+ *
+ * @param fe flow entry
+ */
+ public static TypedStoredFlowEntry newTypedStoredFlowEntry(FlowEntry fe) {
+ if (fe == null) {
+ return null;
+ }
+
+ long life = fe.life();
+
+ if (life >= LONG_POLL_INTERVAL) {
+ return new DefaultTypedFlowEntry(fe, TypedStoredFlowEntry.FlowLiveType.LONG_FLOW);
+ } else if (life >= MID_POLL_INTERVAL) {
+ return new DefaultTypedFlowEntry(fe, TypedStoredFlowEntry.FlowLiveType.MID_FLOW);
+ } else if (life >= CAL_AND_POLL_INTERVAL) {
+ return new DefaultTypedFlowEntry(fe, TypedStoredFlowEntry.FlowLiveType.SHORT_FLOW);
+ } else if (life >= 0) {
+ return new DefaultTypedFlowEntry(fe, TypedStoredFlowEntry.FlowLiveType.IMMEDIATE_FLOW);
+ } else { // life < 0
+ return new DefaultTypedFlowEntry(fe, TypedStoredFlowEntry.FlowLiveType.UNKNOWN_FLOW);
+ }
+ }
+}
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/DefaultGraphDescription.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/DefaultGraphDescription.java index f1e20dac..965c05d4 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/DefaultGraphDescription.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/DefaultGraphDescription.java @@ -50,23 +50,6 @@ public class DefaultGraphDescription extends AbstractDescription * and process the topology graph. * * @param nanos time in nanos of when the topology description was created - * @param devices collection of infrastructure devices - * @param links collection of infrastructure links - * @param annotations optional key/value annotations map - * @deprecated in Cardinal Release - */ - @Deprecated - public DefaultGraphDescription(long nanos, Iterable<Device> devices, - Iterable<Link> links, - SparseAnnotations... annotations) { - this(nanos, System.currentTimeMillis(), devices, links, annotations); - } - - /** - * Creates a minimal topology graph description to allow core to construct - * and process the topology graph. - * - * @param nanos time in nanos of when the topology description was created * @param millis time in millis of when the topology description was created * @param devices collection of infrastructure devices * @param links collection of infrastructure links diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/PathService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/PathService.java index be8c7cfc..0bd4d75d 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/PathService.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/PathService.java @@ -15,9 +15,12 @@ */ package org.onosproject.net.topology; +import org.onosproject.net.DisjointPath; import org.onosproject.net.ElementId; +import org.onosproject.net.Link; import org.onosproject.net.Path; +import java.util.Map; import java.util.Set; /** @@ -41,11 +44,58 @@ public interface PathService { * edge-weight entity, between the specified source and destination * network elements. * - * @param src source element - * @param dst destination element + * @param src source element + * @param dst destination element * @param weight edge-weight entity * @return set of all shortest paths between the two element */ Set<Path> getPaths(ElementId src, ElementId dst, LinkWeight weight); + /** + * Returns the set of all disjoint shortest path pairs, precomputed in terms of hop-count, + * between the specified source and destination devices. + * + * @param src source device + * @param dst destination device + * @return set of all shortest paths between the two devices + */ + Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst); + + /** + * Returns the set of all disjoint shortest path pairs, computed using the supplied + * edge-weight entity, between the specified source and destination devices. + * + * @param src source device + * @param dst destination device + * @param weight edge-weight entity + * @return set of all shortest paths between the two devices + */ + Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst, + LinkWeight weight); + + /** + * Returns the set of all disjoint shortest path pairs, precomputed in terms of hop-count, + * between the specified source and destination devices. + * + * @param src source device + * @param dst destination device + * @param riskProfile map of edges to risk profiles + * @return set of all shortest paths between the two devices + */ + Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst, + Map<Link, Object> riskProfile); + + /** + * Returns the set of all disjoint shortest path pairs, precomputed in terms of hop-count, + * between the specified source and destination devices. + * + * @param src source device + * @param dst destination device + * @param weight edge-weight entity + * @param riskProfile map of edges to risk profiles + * @return set of all shortest paths between the two devices + */ + Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst, + LinkWeight weight, Map<Link, Object> riskProfile); + } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/TopologyService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/TopologyService.java index 41eac2c4..466e4f9b 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/TopologyService.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/TopologyService.java @@ -18,16 +18,18 @@ package org.onosproject.net.topology; import org.onosproject.event.ListenerService; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; +import org.onosproject.net.DisjointPath; import org.onosproject.net.Link; import org.onosproject.net.Path; +import java.util.Map; import java.util.Set; /** * Service for providing network topology information. */ public interface TopologyService - extends ListenerService<TopologyEvent, TopologyListener> { + extends ListenerService<TopologyEvent, TopologyListener> { /** * Returns the current topology descriptor. @@ -72,8 +74,8 @@ public interface TopologyService /** * Returns the set of devices that belong to the specified cluster. * - * @param topology topology descriptor - * @param cluster topology cluster + * @param topology topology descriptor + * @param cluster topology cluster * @return set of cluster devices */ Set<DeviceId> getClusterDevices(Topology topology, TopologyCluster cluster); @@ -81,8 +83,8 @@ public interface TopologyService /** * Returns the set of links that form the specified cluster. * - * @param topology topology descriptor - * @param cluster topology cluster + * @param topology topology descriptor + * @param cluster topology cluster * @return set of cluster links */ Set<Link> getClusterLinks(Topology topology, TopologyCluster cluster); @@ -112,6 +114,57 @@ public interface TopologyService LinkWeight weight); /** + * Returns the set of all disjoint shortest path pairs, precomputed in terms of hop-count, + * between the specified source and destination devices. + * + * @param topology topology descriptor + * @param src source device + * @param dst destination device + * @return set of all shortest paths between the two devices + */ + Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst); + + /** + * Returns the set of all disjoint shortest path pairs, computed using the supplied + * edge-weight entity, between the specified source and destination devices. + * + * @param topology topology descriptor + * @param src source device + * @param dst destination device + * @param weight edge-weight entity + * @return set of all shortest paths between the two devices + */ + Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst, + LinkWeight weight); + + /** + * Returns the set of all disjoint shortest path pairs, precomputed in terms of hop-count, + * between the specified source and destination devices. + * + * @param topology topology descriptor + * @param src source device + * @param dst destination device + * @param riskProfile map of edges to risk profiles + * @return set of all shortest paths between the two devices + */ + Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst, + Map<Link, Object> riskProfile); + + /** + * Returns the set of all disjoint shortest path pairs, precomputed in terms of hop-count, + * between the specified source and destination devices. + * + * @param topology topology descriptor + * @param src source device + * @param dst destination device + * @param weight edge-weight entity + * @param riskProfile map of edges to risk profiles + * @return set of all shortest paths between the two devices + */ + Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst, + LinkWeight weight, Map<Link, Object> riskProfile); + + /** * Indicates whether the specified connection point is part of the network * infrastructure or part of network edge. * diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/TopologyStore.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/TopologyStore.java index 983e616e..039a205c 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/TopologyStore.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/topology/TopologyStore.java @@ -20,11 +20,13 @@ import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; import org.onosproject.net.Link; import org.onosproject.net.Path; +import org.onosproject.net.DisjointPath; import org.onosproject.net.provider.ProviderId; import org.onosproject.store.Store; import java.util.List; import java.util.Set; +import java.util.Map; /** * Manages inventory of topology snapshots; not intended for direct use. @@ -112,6 +114,59 @@ public interface TopologyStore extends Store<TopologyEvent, TopologyStoreDelegat LinkWeight weight); /** + * Computes and returns the set of disjoint shortest path pairs + * between src and dst. + * + * @param topology topology descriptor + * @param src source device + * @param dst destination device + * @param weight link weight function + * @return set of shortest paths + */ + Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst, + LinkWeight weight); + + /** + * Computes and returns the set of disjoint shortest path pairs + * between src and dst. + * + * @param topology topology descriptor + * @param src source device + * @param dst destination device + * @return set of shortest paths + */ + Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst); + + /** + * Computes and returns the set of SRLG disjoint shortest path pairs between source + * and dst, given a mapping of edges to SRLG risk groups. + * + * @param topology topology descriptor + * @param src source device + * @param dst destination device + * @param weight link weight function + * @param riskProfile map of edges to objects. Edges that map to the same object will + * be treated as if they were in the same risk group. + * @return set of shortest paths + */ + Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst, + LinkWeight weight, Map<Link, Object> riskProfile); + + /** + * Returns the set of pre-computed SRLG shortest paths between src and dest. + * + * @param topology topology descriptor + * @param src source device + * @param dst destination device + * @param riskProfile map of edges to objects. Edges that map to the same object will + * be treated as if they were in the same risk group. + * @return set of shortest paths + */ + Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst, + Map<Link, Object> riskProfile); + + + /** * Indicates whether the given connect point is part of the network fabric. * * @param topology topology descriptor diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/AsyncAtomicCounter.java b/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/AsyncAtomicCounter.java index a879cc59..c0df7134 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/AsyncAtomicCounter.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/AsyncAtomicCounter.java @@ -58,4 +58,21 @@ public interface AsyncAtomicCounter { * @return current value */ CompletableFuture<Long> get(); + + + /** + * Atomically sets the given value to the current value. + * + * @return future void + */ + CompletableFuture<Void> set(long value); + + /** + * Atomically sets the given counter to the updated value if the current value is the expected value, otherwise + * no change occurs. + * @param expectedValue the expected current value of the counter + * @param updateValue the new value to be set + * @return true if the update occurred and the expected value was equal to the current value, false otherwise + */ + CompletableFuture<Boolean> compareAndSet(long expectedValue, long updateValue); } diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/AtomicCounter.java b/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/AtomicCounter.java index f620e082..3c9e02c8 100644 --- a/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/AtomicCounter.java +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/AtomicCounter.java @@ -51,6 +51,22 @@ public interface AtomicCounter { long addAndGet(long delta); /** + * Atomically sets the given value to the current value. + * + * @param value the value to set + */ + void set(long value); + + /** + * Atomically sets the given counter to the updated value if the current value is the expected value, otherwise + * no change occurs. + * @param expectedValue the expected current value of the counter + * @param updateValue the new value to be set + * @return true if the update occurred and the expected value was equal to the current value, false otherwise + */ + boolean compareAndSet(long expectedValue, long updateValue); + + /** * Returns the current value of the counter without modifying it. * * @return current value diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/MutexExecutionService.java b/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/MutexExecutionService.java new file mode 100644 index 00000000..d05f3b91 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/MutexExecutionService.java @@ -0,0 +1,34 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.service; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +/** + * Service for mutually exclusive job execution. + */ +public interface MutexExecutionService { + + /** + * Runs the specified task in a mutually exclusive fashion. + * @param task task to run + * @param exclusionPath path on which different instances synchronize + * @param executor executor to use for running the task + * @return future that is completed when the task execution completes. + */ + CompletableFuture<Void> execute(MutexTask task, String exclusionPath, Executor executor); +}
\ No newline at end of file diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/MutexTask.java b/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/MutexTask.java new file mode 100644 index 00000000..ba5ee47f --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/store/service/MutexTask.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.service; + +/** + * The MutexTask interface should be implemented by any class whose + * instances distributed across controllers are intended to be executed + * in a mutually exclusive fashion. + */ +public interface MutexTask { + + /** + * Begins the execution of a mutually exclusive task. + * The start method will be called once the "lock" is acquired. + * After the start method returns the lock is released and some other + * instance can take over execution. + */ + void start(); + + /** + * This method will be called when exclusivity of task execution + * can no longer be guaranteed. The implementation should take necessary steps + * to halt task execution in order to ensure correctness. + */ + void stop(); +} diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/NumberFormatter.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/NumberFormatter.java new file mode 100644 index 00000000..76f42466 --- /dev/null +++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/table/cell/NumberFormatter.java @@ -0,0 +1,50 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.ui.table.cell; + +import java.text.DecimalFormat; +import java.text.NumberFormat; + +/** + * Formats number using the specified format string". + */ +public final class NumberFormatter extends AbstractCellFormatter { + + private final NumberFormat format; + + /** + * Creates a formatter using a default decimal format. + */ + public NumberFormatter() { + this(new DecimalFormat("#,##0.00000")); + } + + /** + * Creates a formatter using the specified format. + * + * @param format number format + */ + public NumberFormatter(NumberFormat format) { + this.format = format; + } + + @Override + protected String nonNullFormat(Object value) { + return format.format(value); + } + +} diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/cfg/ComponentConfigAdapter.java b/framework/src/onos/core/api/src/test/java/org/onosproject/cfg/ComponentConfigAdapter.java index a1abd188..0fccef81 100644 --- a/framework/src/onos/core/api/src/test/java/org/onosproject/cfg/ComponentConfigAdapter.java +++ b/framework/src/onos/core/api/src/test/java/org/onosproject/cfg/ComponentConfigAdapter.java @@ -17,10 +17,13 @@ package org.onosproject.cfg; import java.util.Set; +import com.google.common.collect.ImmutableSet; + /** * Adapter for testing against component configuration service. */ public class ComponentConfigAdapter implements ComponentConfigService { + @Override public Set<String> getComponentNames() { return null; @@ -38,7 +41,7 @@ public class ComponentConfigAdapter implements ComponentConfigService { @Override public Set<ConfigProperty> getProperties(String componentName) { - return null; + return ImmutableSet.of(); } @Override @@ -47,6 +50,10 @@ public class ComponentConfigAdapter implements ComponentConfigService { } @Override + public void preSetProperty(String componentName, String name, String value) { + } + + @Override public void unsetProperty(String componentName, String name) { } diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/net/behaviour/ControllerInfoTest.java b/framework/src/onos/core/api/src/test/java/org/onosproject/net/behaviour/ControllerInfoTest.java new file mode 100644 index 00000000..ece7f199 --- /dev/null +++ b/framework/src/onos/core/api/src/test/java/org/onosproject/net/behaviour/ControllerInfoTest.java @@ -0,0 +1,112 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.net.behaviour; + + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.onlab.packet.IpAddress; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * Test for ControllerInfo class. + */ +public class ControllerInfoTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void tcpSslFormat() { + String target = "tcp:192.168.1.1:6653"; + ControllerInfo controllerInfo = new ControllerInfo(target); + assertEquals("wrong type", controllerInfo.type(), "tcp"); + assertEquals("wrong ip", controllerInfo.ip(), IpAddress.valueOf("192.168.1.1")); + assertEquals("wrong port", controllerInfo.port(), 6653); + + } + + @Test + public void ptcpPsslFormat() { + String target = "ptcp:6653:192.168.1.1"; + ControllerInfo controllerInfo = new ControllerInfo(target); + assertEquals("wrong type", controllerInfo.type(), "ptcp"); + assertEquals("wrong ip", controllerInfo.ip(), IpAddress.valueOf("192.168.1.1")); + assertEquals("wrong port", controllerInfo.port(), 6653); + + } + + @Test + public void unixFormat() { + String target = "unix:file"; + thrown.expect(IllegalArgumentException.class); + ControllerInfo controllerInfo = new ControllerInfo(target); + assertTrue("wrong type", controllerInfo.type().contains("unix")); + assertNull("wrong ip", controllerInfo.ip()); + assertEquals("wrong port", controllerInfo.port(), -1); + + } + + @Test + public void defaultValues() { + String target = "tcp:192.168.1.1"; + ControllerInfo controllerInfo = new ControllerInfo(target); + assertEquals("wrong type", controllerInfo.type(), "tcp"); + assertEquals("wrong ip", controllerInfo.ip(), IpAddress.valueOf("192.168.1.1")); + assertEquals("wrong port", controllerInfo.port(), 6653); + String target1 = "ptcp:5000:"; + ControllerInfo controllerInfo2 = new ControllerInfo(target1); + assertEquals("wrong type", controllerInfo2.type(), "ptcp"); + assertEquals("wrong ip", controllerInfo2.ip(), IpAddress.valueOf("0.0.0.0")); + assertEquals("wrong port", controllerInfo2.port(), 5000); + String target2 = "ptcp:"; + ControllerInfo controllerInfo3 = new ControllerInfo(target2); + assertEquals("wrong type", controllerInfo3.type(), "ptcp"); + assertEquals("wrong ip", controllerInfo3.ip(), IpAddress.valueOf("0.0.0.0")); + assertEquals("wrong port", controllerInfo3.port(), 6653); + } + + + @Test + public void testEquals() { + String target1 = "ptcp:6653:192.168.1.1"; + ControllerInfo controllerInfo1 = new ControllerInfo(target1); + String target2 = "ptcp:6653:192.168.1.1"; + ControllerInfo controllerInfo2 = new ControllerInfo(target2); + assertTrue("wrong equals method", controllerInfo1.equals(controllerInfo2)); + } + + @Test + public void testListEquals() { + String target1 = "ptcp:6653:192.168.1.1"; + ControllerInfo controllerInfo1 = new ControllerInfo(target1); + String target2 = "ptcp:6653:192.168.1.1"; + ControllerInfo controllerInfo2 = new ControllerInfo(target2); + String target3 = "tcp:192.168.1.1:6653"; + ControllerInfo controllerInfo3 = new ControllerInfo(target3); + String target4 = "tcp:192.168.1.1:6653"; + ControllerInfo controllerInfo4 = new ControllerInfo(target4); + List<ControllerInfo> list1 = new ArrayList<>(Arrays.asList(controllerInfo1, controllerInfo3)); + List<ControllerInfo> list2 = new ArrayList<>(Arrays.asList(controllerInfo2, controllerInfo4)); + assertTrue("wrong equals list method", list1.equals(list2)); + } +} diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/net/config/NetworkConfigServiceAdapter.java b/framework/src/onos/core/api/src/test/java/org/onosproject/net/config/NetworkConfigServiceAdapter.java index b70d14e8..73072583 100644 --- a/framework/src/onos/core/api/src/test/java/org/onosproject/net/config/NetworkConfigServiceAdapter.java +++ b/framework/src/onos/core/api/src/test/java/org/onosproject/net/config/NetworkConfigServiceAdapter.java @@ -29,7 +29,7 @@ public class NetworkConfigServiceAdapter implements NetworkConfigService { } @Override - public SubjectFactory getSubjectFactory(String subjectKey) { + public SubjectFactory getSubjectFactory(String subjectClassKey) { return null; } @@ -39,7 +39,7 @@ public class NetworkConfigServiceAdapter implements NetworkConfigService { } @Override - public Class<? extends Config> getConfigClass(String subjectKey, String configKey) { + public Class<? extends Config> getConfigClass(String subjectClassKey, String configKey) { return null; } diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficSelectorTest.java b/framework/src/onos/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficSelectorTest.java index b871397b..10c5a637 100644 --- a/framework/src/onos/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficSelectorTest.java +++ b/framework/src/onos/core/api/src/test/java/org/onosproject/net/flow/DefaultTrafficSelectorTest.java @@ -267,29 +267,5 @@ public class DefaultTrafficSelectorTest { selector = DefaultTrafficSelector.builder() .add(Criteria.matchLambda(new IndexedLambda(shortValue))).build(); assertThat(selector, hasCriterionWithType(Type.OCH_SIGID)); - - selector = DefaultTrafficSelector.builder() - .add(Criteria.matchOpticalSignalType(shortValue)).build(); - assertThat(selector, hasCriterionWithType(Type.OCH_SIGTYPE)); - } - - /** - * Tests the traffic selector builder. - */ - @Test - public void testTrafficSelectorBuilder() { - TrafficSelector selector; - final short shortValue = 33; - - final TrafficSelector baseSelector = DefaultTrafficSelector.builder() - .add(Criteria.matchLambda(new IndexedLambda(shortValue))).build(); - selector = DefaultTrafficSelector.builder(baseSelector) - .build(); - assertThat(selector, hasCriterionWithType(Type.OCH_SIGID)); - - final Criterion criterion = Criteria.matchLambda(shortValue); - selector = DefaultTrafficSelector.builder() - .add(criterion).build(); - assertThat(selector, hasCriterionWithType(Type.OCH_SIGID)); } } diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/net/flow/FlowRuleServiceAdapter.java b/framework/src/onos/core/api/src/test/java/org/onosproject/net/flow/FlowRuleServiceAdapter.java index c7b78791..56e59118 100644 --- a/framework/src/onos/core/api/src/test/java/org/onosproject/net/flow/FlowRuleServiceAdapter.java +++ b/framework/src/onos/core/api/src/test/java/org/onosproject/net/flow/FlowRuleServiceAdapter.java @@ -35,17 +35,14 @@ public class FlowRuleServiceAdapter implements FlowRuleService { @Override public void applyFlowRules(FlowRule... flowRules) { - } @Override public void removeFlowRules(FlowRule... flowRules) { - } @Override public void removeFlowRulesById(ApplicationId appId) { - } @Override @@ -60,16 +57,18 @@ public class FlowRuleServiceAdapter implements FlowRuleService { @Override public void apply(FlowRuleOperations ops) { - } @Override public void addListener(FlowRuleListener listener) { - } @Override public void removeListener(FlowRuleListener listener) { + } + @Override + public Iterable<TableStatisticsEntry> getFlowTableStatistics(DeviceId deviceId) { + return null; } } diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java b/framework/src/onos/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java index ee294f6f..95d605c6 100644 --- a/framework/src/onos/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java +++ b/framework/src/onos/core/api/src/test/java/org/onosproject/net/flow/criteria/CriteriaTest.java @@ -225,12 +225,6 @@ public class CriteriaTest { Criterion matchIpv6ExthdrFlags2 = Criteria.matchIPv6ExthdrFlags(ipv6ExthdrFlags2); - int lambda1 = 1; - int lambda2 = 2; - Criterion matchLambda1 = Criteria.matchLambda(lambda1); - Criterion sameAsMatchLambda1 = Criteria.matchLambda(lambda1); - Criterion matchLambda2 = Criteria.matchLambda(lambda2); - Criterion matchOchSignalType1 = Criteria.matchOchSignalType(OchSignalType.FIXED_GRID); Criterion sameAsMatchOchSignalType1 = Criteria.matchOchSignalType(OchSignalType.FIXED_GRID); Criterion matchOchSignalType2 = Criteria.matchOchSignalType(OchSignalType.FLEX_GRID); @@ -239,12 +233,6 @@ public class CriteriaTest { Criterion sameAsMatchIndexedLambda1 = Criteria.matchLambda(Lambda.indexedLambda(1)); Criterion matchIndexedLambda2 = Criteria.matchLambda(Lambda.indexedLambda(2)); - short signalLambda1 = 1; - short signalLambda2 = 2; - Criterion matchSignalLambda1 = Criteria.matchOpticalSignalType(signalLambda1); - Criterion sameAsMatchSignalLambda1 = Criteria.matchOpticalSignalType(signalLambda1); - Criterion matchSignalLambda2 = Criteria.matchOpticalSignalType(signalLambda2); - Criterion matchOchSignal1 = Criteria.matchLambda(Lambda.ochSignal(GridType.DWDM, ChannelSpacing.CHL_100GHZ, 4, 8)); Criterion sameAsMatchOchSignal1 = @@ -306,7 +294,6 @@ public class CriteriaTest { assertThatClassIsImmutable(MplsCriterion.class); assertThatClassIsImmutable(IPv6ExthdrFlagsCriterion.class); assertThatClassIsImmutable(LambdaCriterion.class); - assertThatClassIsImmutable(OpticalSignalTypeCriterion.class); } // PortCriterion class @@ -1057,32 +1044,6 @@ public class CriteriaTest { .testEquals(); } - // LambdaCriterion class - - /** - * Test the matchLambda method. - */ - @Test - public void testMatchLambdaMethod() { - Criterion matchLambda = Criteria.matchLambda(lambda1); - LambdaCriterion lambdaCriterion = - checkAndConvert(matchLambda, - Criterion.Type.OCH_SIGID, - LambdaCriterion.class); - assertThat(lambdaCriterion.lambda(), is(equalTo(lambda1))); - } - - /** - * Test the equals() method of the LambdaCriterion class. - */ - @Test - public void testLambdaCriterionEquals() { - new EqualsTester() - .addEqualityGroup(matchLambda1, sameAsMatchLambda1) - .addEqualityGroup(matchLambda2) - .testEquals(); - } - @Test public void testIndexedLambdaCriterionEquals() { new EqualsTester() @@ -1109,30 +1070,4 @@ public class CriteriaTest { .addEqualityGroup(matchOchSignalType2) .testEquals(); } - - // OpticalSignalTypeCriterion class - - /** - * Test the matchOpticalSignalType method. - */ - @Test - public void testMatchOpticalSignalTypeMethod() { - Criterion matchLambda = Criteria.matchOpticalSignalType(signalLambda1); - OpticalSignalTypeCriterion opticalSignalTypeCriterion = - checkAndConvert(matchLambda, - Criterion.Type.OCH_SIGTYPE, - OpticalSignalTypeCriterion.class); - assertThat(opticalSignalTypeCriterion.signalType(), is(equalTo(signalLambda1))); - } - - /** - * Test the equals() method of the OpticalSignalTypeCriterion class. - */ - @Test - public void testOpticalSignalTypeCriterionEquals() { - new EqualsTester() - .addEqualityGroup(matchSignalLambda1, sameAsMatchSignalLambda1) - .addEqualityGroup(matchSignalLambda2) - .testEquals(); - } } diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java b/framework/src/onos/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java index ac4ecff3..d42e22fa 100644 --- a/framework/src/onos/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java +++ b/framework/src/onos/core/api/src/test/java/org/onosproject/net/intent/IntentTestsMocks.java @@ -17,7 +17,6 @@ package org.onosproject.net.intent; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableSet; - import org.onlab.util.Bandwidth; import org.onosproject.core.DefaultGroupId; import org.onosproject.core.GroupId; @@ -37,6 +36,9 @@ import org.onosproject.net.flow.criteria.Criterion.Type; import org.onosproject.net.flow.instructions.Instruction; import org.onosproject.net.flow.instructions.Instructions; import org.onosproject.net.flow.instructions.Instructions.MetadataInstruction; +import org.onosproject.net.resource.ResourceAllocation; +import org.onosproject.net.resource.ResourceRequest; +import org.onosproject.net.resource.ResourceType; import org.onosproject.net.resource.link.BandwidthResource; import org.onosproject.net.resource.link.BandwidthResourceRequest; import org.onosproject.net.resource.link.LambdaResource; @@ -48,13 +50,10 @@ import org.onosproject.net.resource.link.LinkResourceRequest; import org.onosproject.net.resource.link.LinkResourceService; import org.onosproject.net.resource.link.MplsLabel; import org.onosproject.net.resource.link.MplsLabelResourceAllocation; -import org.onosproject.net.resource.ResourceAllocation; -import org.onosproject.net.resource.ResourceRequest; -import org.onosproject.net.resource.ResourceType; import org.onosproject.net.topology.DefaultTopologyEdge; import org.onosproject.net.topology.DefaultTopologyVertex; import org.onosproject.net.topology.LinkWeight; -import org.onosproject.net.topology.PathService; +import org.onosproject.net.topology.PathServiceAdapter; import org.onosproject.net.topology.TopologyVertex; import org.onosproject.store.Timestamp; @@ -68,9 +67,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; -import static org.onosproject.net.NetTestTools.createPath; -import static org.onosproject.net.NetTestTools.did; -import static org.onosproject.net.NetTestTools.link; +import static org.onosproject.net.NetTestTools.*; /** * Common mocks used by the intent framework tests. @@ -134,7 +131,7 @@ public class IntentTestsMocks { /** * Mock path service for creating paths within the test. */ - public static class MockPathService implements PathService { + public static class MockPathService extends PathServiceAdapter { final String[] pathHops; final String[] reversePathHops; @@ -424,7 +421,7 @@ public class IntentTestsMocks { } final MockFlowRule other = (MockFlowRule) obj; return Objects.equals(this.timestamp, other.timestamp) && - this.id == other.id; + this.id == other.id; } @Override @@ -450,7 +447,7 @@ public class IntentTestsMocks { public MockIntent(Long number) { super(NetTestTools.APP_ID, null, Collections.emptyList(), - Intent.DEFAULT_INTENT_PRIORITY); + Intent.DEFAULT_INTENT_PRIORITY); this.number = number; } diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/net/packet/PacketServiceAdapter.java b/framework/src/onos/core/api/src/test/java/org/onosproject/net/packet/PacketServiceAdapter.java index c4386593..2993ce6b 100644 --- a/framework/src/onos/core/api/src/test/java/org/onosproject/net/packet/PacketServiceAdapter.java +++ b/framework/src/onos/core/api/src/test/java/org/onosproject/net/packet/PacketServiceAdapter.java @@ -19,7 +19,6 @@ import org.onosproject.core.ApplicationId; import org.onosproject.net.flow.TrafficSelector; import java.util.List; -import java.util.Map; /** * Test adapter for packet service. @@ -34,7 +33,7 @@ public class PacketServiceAdapter implements PacketService { } @Override - public Map<Integer, PacketProcessor> getProcessors() { + public List<PacketProcessorEntry> getProcessors() { return null; } diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/net/topology/DefaultGraphDescriptionTest.java b/framework/src/onos/core/api/src/test/java/org/onosproject/net/topology/DefaultGraphDescriptionTest.java index 8b0f8f05..f3958491 100644 --- a/framework/src/onos/core/api/src/test/java/org/onosproject/net/topology/DefaultGraphDescriptionTest.java +++ b/framework/src/onos/core/api/src/test/java/org/onosproject/net/topology/DefaultGraphDescriptionTest.java @@ -41,7 +41,7 @@ public class DefaultGraphDescriptionTest { @Test public void basics() { DefaultGraphDescription desc = - new DefaultGraphDescription(4321L, ImmutableSet.of(DEV1, DEV2, DEV3), + new DefaultGraphDescription(4321L, System.currentTimeMillis(), ImmutableSet.of(DEV1, DEV2, DEV3), ImmutableSet.of(L1, L2)); assertEquals("incorrect time", 4321L, desc.timestamp()); assertEquals("incorrect vertex count", 3, desc.vertexes().size()); @@ -50,7 +50,7 @@ public class DefaultGraphDescriptionTest { @Test public void missingVertex() { - GraphDescription desc = new DefaultGraphDescription(4321L, + GraphDescription desc = new DefaultGraphDescription(4321L, System.currentTimeMillis(), ImmutableSet.of(DEV1, DEV3), ImmutableSet.of(L1, L2)); assertEquals("incorrect time", 4321L, desc.timestamp()); diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/net/topology/PathServiceAdapter.java b/framework/src/onos/core/api/src/test/java/org/onosproject/net/topology/PathServiceAdapter.java new file mode 100644 index 00000000..6a8e586f --- /dev/null +++ b/framework/src/onos/core/api/src/test/java/org/onosproject/net/topology/PathServiceAdapter.java @@ -0,0 +1,62 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.net.topology; + +import org.onosproject.net.DisjointPath; +import org.onosproject.net.ElementId; +import org.onosproject.net.Link; +import org.onosproject.net.Path; + +import java.util.Map; +import java.util.Set; + +/** + * Test adapter for path service. + */ +public class PathServiceAdapter implements PathService { + @Override + public Set<Path> getPaths(ElementId src, ElementId dst) { + return null; + } + + @Override + public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeight weight) { + return null; + } + + @Override + public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst) { + return null; + } + + @Override + public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst, LinkWeight weight) { + return null; + } + + @Override + public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst, + Map<Link, Object> riskProfile) { + return null; + } + + @Override + public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst, + LinkWeight weight, + Map<Link, Object> riskProfile) { + return null; + } +} diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/net/topology/TopologyServiceAdapter.java b/framework/src/onos/core/api/src/test/java/org/onosproject/net/topology/TopologyServiceAdapter.java index 07e67842..72cc67d7 100644 --- a/framework/src/onos/core/api/src/test/java/org/onosproject/net/topology/TopologyServiceAdapter.java +++ b/framework/src/onos/core/api/src/test/java/org/onosproject/net/topology/TopologyServiceAdapter.java @@ -17,9 +17,11 @@ package org.onosproject.net.topology; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; +import org.onosproject.net.DisjointPath; import org.onosproject.net.Link; import org.onosproject.net.Path; +import java.util.Map; import java.util.Set; /** @@ -89,4 +91,28 @@ public class TopologyServiceAdapter implements TopologyService { public void removeListener(TopologyListener listener) { } + @Override + public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst) { + return null; + } + + @Override + public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, + DeviceId dst, LinkWeight weight) { + return null; + } + + @Override + public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst, + Map<Link, Object> riskProfile) { + return null; + } + + @Override + public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, + DeviceId dst, LinkWeight weight, + Map<Link, Object> riskProfile) { + return null; + } + } diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/store/service/TestAtomicCounter.java b/framework/src/onos/core/api/src/test/java/org/onosproject/store/service/TestAtomicCounter.java index 01209be2..8c577df9 100644 --- a/framework/src/onos/core/api/src/test/java/org/onosproject/store/service/TestAtomicCounter.java +++ b/framework/src/onos/core/api/src/test/java/org/onosproject/store/service/TestAtomicCounter.java @@ -48,6 +48,16 @@ public final class TestAtomicCounter implements AtomicCounter { } @Override + public void set(long value) { + this.value.set(value); + } + + @Override + public boolean compareAndSet(long expectedValue, long updateValue) { + return value.compareAndSet(expectedValue, updateValue); + } + + @Override public long get() { return value.get(); } diff --git a/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/CodecManager.java b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/CodecManager.java index eb53152e..3433b3b7 100644 --- a/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/CodecManager.java +++ b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/CodecManager.java @@ -16,6 +16,7 @@ package org.onosproject.codec.impl; import com.google.common.collect.ImmutableSet; + import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; @@ -36,6 +37,7 @@ import org.onosproject.net.Port; import org.onosproject.net.driver.Driver; import org.onosproject.net.flow.FlowEntry; import org.onosproject.net.flow.FlowRule; +import org.onosproject.net.flow.TableStatisticsEntry; import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.flow.criteria.Criterion; @@ -99,6 +101,7 @@ public class CodecManager implements CodecService { registerCodec(Driver.class, new DriverCodec()); registerCodec(GroupBucket.class, new GroupBucketCodec()); registerCodec(Load.class, new LoadCodec()); + registerCodec(TableStatisticsEntry.class, new TableStatisticsEntryCodec()); log.info("Started"); } diff --git a/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java index d61cf38b..d12e4ad8 100644 --- a/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java +++ b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/EncodeInstructionCodecHelper.java @@ -215,6 +215,7 @@ public final class EncodeInstructionCodecHelper { break; case DROP: + case NOACTION: break; case L0MODIFICATION: diff --git a/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/TableStatisticsEntryCodec.java b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/TableStatisticsEntryCodec.java new file mode 100644 index 00000000..7834ceb1 --- /dev/null +++ b/framework/src/onos/core/common/src/main/java/org/onosproject/codec/impl/TableStatisticsEntryCodec.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.codec.impl; + +import org.onosproject.codec.CodecContext; +import org.onosproject.codec.JsonCodec; +import org.onosproject.net.flow.TableStatisticsEntry; + +import com.fasterxml.jackson.databind.node.ObjectNode; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Table statistics entry JSON codec. + */ +public final class TableStatisticsEntryCodec extends JsonCodec<TableStatisticsEntry> { + + @Override + public ObjectNode encode(TableStatisticsEntry entry, CodecContext context) { + checkNotNull(entry, "Table Statistics entry cannot be null"); + + final ObjectNode result = context.mapper().createObjectNode() + .put("tableId", entry.tableId()) + .put("deviceId", entry.deviceId().toString()) + .put("activeEntries", entry.activeFlowEntries()) + .put("packetsLookedUp", entry.packetsLookedup()) + .put("packetsMatched", entry.packetsMatched()); + + return result; + } + +} + diff --git a/framework/src/onos/core/common/src/main/java/org/onosproject/common/DefaultTopology.java b/framework/src/onos/core/common/src/main/java/org/onosproject/common/DefaultTopology.java index bdf7d732..3c5c540d 100644 --- a/framework/src/onos/core/common/src/main/java/org/onosproject/common/DefaultTopology.java +++ b/framework/src/onos/core/common/src/main/java/org/onosproject/common/DefaultTopology.java @@ -23,14 +23,19 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.ImmutableSetMultimap.Builder; import org.onlab.graph.DijkstraGraphSearch; +import org.onlab.graph.DisjointPathPair; import org.onlab.graph.GraphPathSearch; import org.onlab.graph.GraphPathSearch.Result; +import org.onlab.graph.SRLGGraphSearch; +import org.onlab.graph.SuurballeGraphSearch; import org.onlab.graph.TarjanGraphSearch; import org.onlab.graph.TarjanGraphSearch.SCCResult; import org.onosproject.net.AbstractModel; import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DefaultDisjointPath; import org.onosproject.net.DefaultPath; import org.onosproject.net.DeviceId; +import org.onosproject.net.DisjointPath; import org.onosproject.net.Link; import org.onosproject.net.Path; import org.onosproject.net.provider.ProviderId; @@ -45,10 +50,11 @@ import org.onosproject.net.topology.TopologyEdge; import org.onosproject.net.topology.TopologyGraph; import org.onosproject.net.topology.TopologyVertex; -import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import static com.google.common.base.MoreObjects.toStringHelper; import static com.google.common.base.Preconditions.checkArgument; @@ -67,6 +73,9 @@ public class DefaultTopology extends AbstractModel implements Topology { private static final DijkstraGraphSearch<TopologyVertex, TopologyEdge> DIJKSTRA = new DijkstraGraphSearch<>(); private static final TarjanGraphSearch<TopologyVertex, TopologyEdge> TARJAN = new TarjanGraphSearch<>(); + private static final SuurballeGraphSearch<TopologyVertex, TopologyEdge> SUURBALLE = + new SuurballeGraphSearch<>(); + private final long time; private final long creationTime; @@ -315,15 +324,135 @@ public class DefaultTopology extends AbstractModel implements Topology { return builder.build(); } + /** + * /** + * Returns the set of pre-computed shortest disjoint path pairs between source and + * destination devices. + * + * @param src source device + * @param dst destination device + * @return set of shortest disjoint path pairs + */ + public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst) { + return getDisjointPaths(src, dst, (LinkWeight) null); + } + + /** + * Computes on-demand the set of shortest disjoint path pairs between source and + * destination devices. + * + * @param src source device + * @param dst destination device + * @param weight link weight function + * @return set of disjoint shortest path pairs + */ + public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst, LinkWeight weight) { + final DefaultTopologyVertex srcV = new DefaultTopologyVertex(src); + final DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst); + Set<TopologyVertex> vertices = graph.getVertexes(); + if (!vertices.contains(srcV) || !vertices.contains(dstV)) { + // src or dst not part of the current graph + return ImmutableSet.of(); + } + + GraphPathSearch.Result<TopologyVertex, TopologyEdge> result = + SUURBALLE.search(graph, srcV, dstV, weight, ALL_PATHS); + ImmutableSet.Builder<DisjointPath> builder = ImmutableSet.builder(); + for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) { + builder.add(networkDisjointPath((org.onlab.graph.DisjointPathPair<TopologyVertex, TopologyEdge>) path)); + } + return builder.build(); + } + + /** + * Computes on-demand the set of shortest disjoint risk groups path pairs between source and + * destination devices. + * + * @param src source device + * @param dst destination device + * @param weight edge weight object + * @param riskProfile map representing risk groups for each edge + * @return set of shortest disjoint paths + */ + private Set<DisjointPath> disjointPaths(DeviceId src, DeviceId dst, LinkWeight weight, + Map<TopologyEdge, Object> riskProfile) { + DefaultTopologyVertex srcV = new DefaultTopologyVertex(src); + DefaultTopologyVertex dstV = new DefaultTopologyVertex(dst); + + Set<TopologyVertex> vertices = graph.getVertexes(); + if (!vertices.contains(srcV) || !vertices.contains(dstV)) { + // src or dst not part of the current graph + return ImmutableSet.of(); + } + + SRLGGraphSearch<TopologyVertex, TopologyEdge> srlg = new SRLGGraphSearch<>(riskProfile); + GraphPathSearch.Result<TopologyVertex, TopologyEdge> result = + srlg.search(graph, srcV, dstV, weight, ALL_PATHS); + ImmutableSet.Builder<DisjointPath> builder = ImmutableSet.builder(); + for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) { + builder.add(networkDisjointPath((org.onlab.graph.DisjointPathPair<TopologyVertex, TopologyEdge>) path)); + } + return builder.build(); + } + + /** + * Computes on-demand the set of shortest disjoint risk groups path pairs between source and + * destination devices. + * + * @param src source device + * @param dst destination device + * @param weight edge weight object + * @param riskProfile map representing risk groups for each link + * @return set of shortest disjoint paths + */ + public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst, LinkWeight weight, + Map<Link, Object> riskProfile) { + Map<TopologyEdge, Object> riskProfile2 = new HashMap<>(); + for (Link l : riskProfile.keySet()) { + riskProfile2.put(new TopologyEdge() { + Link cur = l; + + public Link link() { + return cur; + } + + public TopologyVertex src() { + return () -> src; + } + + public TopologyVertex dst() { + return () -> dst; + } + }, riskProfile.get(l)); + } + return disjointPaths(src, dst, weight, riskProfile2); + } + + /** + * Computes on-demand the set of shortest disjoint risk groups path pairs between source and + * destination devices. + * + * @param src source device + * @param dst destination device + * @param riskProfile map representing risk groups for each link + * @return set of shortest disjoint paths + */ + public Set<DisjointPath> getDisjointPaths(DeviceId src, DeviceId dst, Map<Link, Object> riskProfile) { + return getDisjointPaths(src, dst, null, riskProfile); + } + // Converts graph path to a network path with the same cost. private Path networkPath(org.onlab.graph.Path<TopologyVertex, TopologyEdge> path) { - List<Link> links = new ArrayList<>(); - for (TopologyEdge edge : path.edges()) { - links.add(edge.link()); - } + List<Link> links = path.edges().stream().map(TopologyEdge::link).collect(Collectors.toList()); return new DefaultPath(CORE_PROVIDER_ID, links, path.cost()); } + private DisjointPath networkDisjointPath(DisjointPathPair<TopologyVertex, TopologyEdge> path) { + return new DefaultDisjointPath(CORE_PROVIDER_ID, + (DefaultPath) networkPath(path.primary()), + (DefaultPath) networkPath(path.secondary())); + } + // Searches for SCC clusters in the network topology graph using Tarjan // algorithm. private SCCResult<TopologyVertex, TopologyEdge> searchForClusters() { @@ -334,6 +463,7 @@ public class DefaultTopology extends AbstractModel implements Topology { private ImmutableMap<ClusterId, TopologyCluster> buildTopologyClusters() { ImmutableMap.Builder<ClusterId, TopologyCluster> clusterBuilder = ImmutableMap.builder(); SCCResult<TopologyVertex, TopologyEdge> results = clusterResults.get(); + // Extract both vertexes and edges from the results; the lists form // pairs along the same index. List<Set<TopologyVertex>> clusterVertexes = results.clusterVertexes(); diff --git a/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java b/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java index 6bf46803..54e1146b 100644 --- a/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java +++ b/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/CriterionCodecTest.java @@ -38,10 +38,8 @@ import org.onosproject.net.flow.criteria.Criterion; import com.fasterxml.jackson.databind.node.ObjectNode; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.onlab.junit.TestUtils.getField; -import static org.onlab.junit.TestUtils.setField; import static org.onosproject.codec.impl.CriterionJsonMatcher.matchesCriterion; /** @@ -429,17 +427,4 @@ public class CriterionCodecTest { ObjectNode result = criterionCodec.encode(criterion, context); assertThat(result, matchesCriterion(criterion)); } - - /** - * Tests that an unimplemented criterion type only returns the type and - * no other data. - */ - @Test - public void matchUnknownTypeTest() throws Exception { - Criterion criterion = Criteria.matchOpticalSignalType((byte) 250); - setField(criterion, "type", Criterion.Type.UNASSIGNED_40); - ObjectNode result = criterionCodec.encode(criterion, context); - assertThat(result.get("type").textValue(), is(criterion.type().toString())); - assertThat(result.size(), is(1)); - } } diff --git a/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java b/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java index 72081e6c..c3cdca0f 100644 --- a/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java +++ b/framework/src/onos/core/common/src/test/java/org/onosproject/codec/impl/InstructionJsonMatcher.java @@ -416,6 +416,8 @@ public final class InstructionJsonMatcher extends TypeSafeDiagnosingMatcher<Json description); } else if (instruction instanceof ModMplsLabelInstruction) { return matchModMplsLabelInstruction(jsonInstruction, description); + } else if (instruction instanceof NoActionInstruction) { + return true; } return false; diff --git a/framework/src/onos/core/common/src/test/java/org/onosproject/common/DefaultTopologyTest.java b/framework/src/onos/core/common/src/test/java/org/onosproject/common/DefaultTopologyTest.java index 4d435cfe..ef0f3324 100644 --- a/framework/src/onos/core/common/src/test/java/org/onosproject/common/DefaultTopologyTest.java +++ b/framework/src/onos/core/common/src/test/java/org/onosproject/common/DefaultTopologyTest.java @@ -73,7 +73,7 @@ public class DefaultTopologyTest { link("1", 3, "4", 3), link("4", 3, "1", 3), link("3", 4, "4", 4), link("4", 4, "3", 4)); GraphDescription graphDescription = - new DefaultGraphDescription(now, devices, links); + new DefaultGraphDescription(now, System.currentTimeMillis(), devices, links); dt = new DefaultTopology(PID, graphDescription); assertEquals("incorrect supplier", PID, dt.providerId()); diff --git a/framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimpleFlowRuleStore.java b/framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimpleFlowRuleStore.java index c8c92aa5..bed32a2d 100644 --- a/framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimpleFlowRuleStore.java +++ b/framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimpleFlowRuleStore.java @@ -20,8 +20,10 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.RemovalListener; import com.google.common.cache.RemovalNotification; import com.google.common.collect.FluentIterable; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import com.google.common.util.concurrent.SettableFuture; + import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; @@ -44,6 +46,7 @@ import org.onosproject.net.flow.FlowRuleEvent.Type; import org.onosproject.net.flow.FlowRuleStore; import org.onosproject.net.flow.FlowRuleStoreDelegate; import org.onosproject.net.flow.StoredFlowEntry; +import org.onosproject.net.flow.TableStatisticsEntry; import org.onosproject.store.AbstractStore; import org.slf4j.Logger; @@ -79,6 +82,9 @@ public class SimpleFlowRuleStore private final ConcurrentMap<DeviceId, ConcurrentMap<FlowId, List<StoredFlowEntry>>> flowEntries = new ConcurrentHashMap<>(); + private final ConcurrentMap<DeviceId, List<TableStatisticsEntry>> + deviceTableStats = new ConcurrentHashMap<>(); + private final AtomicInteger localBatchIdGen = new AtomicInteger(); // TODO: make this configurable @@ -97,6 +103,7 @@ public class SimpleFlowRuleStore @Deactivate public void deactivate() { + deviceTableStats.clear(); flowEntries.clear(); log.info("Stopped"); } @@ -315,4 +322,20 @@ public class SimpleFlowRuleStore } } } + + @Override + public FlowRuleEvent updateTableStatistics(DeviceId deviceId, + List<TableStatisticsEntry> tableStats) { + deviceTableStats.put(deviceId, tableStats); + return null; + } + + @Override + public Iterable<TableStatisticsEntry> getTableStatistics(DeviceId deviceId) { + List<TableStatisticsEntry> tableStats = deviceTableStats.get(deviceId); + if (tableStats == null) { + return Collections.emptyList(); + } + return ImmutableList.copyOf(tableStats); + } } diff --git a/framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimpleHostStore.java b/framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimpleHostStore.java index 264d0493..72ec98ca 100644 --- a/framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimpleHostStore.java +++ b/framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimpleHostStore.java @@ -160,6 +160,11 @@ public class SimpleHostStore } @Override + public HostEvent removeIp(HostId hostId, IpAddress ipAddress) { + return null; + } + + @Override public int getHostCount() { return hosts.size(); } diff --git a/framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimplePacketStore.java b/framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimplePacketStore.java index f8359262..7dda12c8 100644 --- a/framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimplePacketStore.java +++ b/framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimplePacketStore.java @@ -15,10 +15,13 @@ */ package org.onosproject.store.trivial; -import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.common.collect.Sets; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Service; +import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.packet.OutboundPacket; import org.onosproject.net.packet.PacketEvent; import org.onosproject.net.packet.PacketEvent.Type; @@ -27,7 +30,9 @@ import org.onosproject.net.packet.PacketStore; import org.onosproject.net.packet.PacketStoreDelegate; import org.onosproject.store.AbstractStore; +import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -39,7 +44,7 @@ public class SimplePacketStore extends AbstractStore<PacketEvent, PacketStoreDelegate> implements PacketStore { - private Set<PacketRequest> requests = Sets.newConcurrentHashSet(); + private Map<TrafficSelector, Set<PacketRequest>> requests = Maps.newConcurrentMap(); @Override public void emit(OutboundPacket packet) { @@ -47,18 +52,50 @@ public class SimplePacketStore } @Override - public boolean requestPackets(PacketRequest request) { - return requests.add(request); + public void requestPackets(PacketRequest request) { + requests.compute(request.selector(), (s, existingRequests) -> { + if (existingRequests == null) { + return ImmutableSet.of(request); + } else if (!existingRequests.contains(request)) { + if (delegate != null) { + delegate.requestPackets(request); + } + return ImmutableSet.<PacketRequest>builder() + .addAll(existingRequests) + .add(request) + .build(); + } else { + return existingRequests; + } + }); } @Override - public boolean cancelPackets(PacketRequest request) { - return requests.remove(request); + public void cancelPackets(PacketRequest request) { + requests.computeIfPresent(request.selector(), (s, existingRequests) -> { + if (existingRequests.contains(request)) { + HashSet<PacketRequest> newRequests = Sets.newHashSet(existingRequests); + newRequests.remove(request); + if (newRequests.size() > 0) { + return ImmutableSet.copyOf(newRequests); + } else { + if (delegate != null) { + delegate.cancelPackets(request); + } + return null; + } + } else { + return existingRequests; + } + }); } @Override public List<PacketRequest> existingRequests() { - return ImmutableList.copyOf(requests); + List<PacketRequest> list = Lists.newArrayList(); + requests.values().forEach(list::addAll); + list.sort((o1, o2) -> o1.priority().priorityValue() - o2.priority().priorityValue()); + return list; } } diff --git a/framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimpleTopologyStore.java b/framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimpleTopologyStore.java index 6a89c019..29c5d844 100644 --- a/framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimpleTopologyStore.java +++ b/framework/src/onos/core/common/src/test/java/org/onosproject/store/trivial/SimpleTopologyStore.java @@ -23,6 +23,7 @@ import org.onosproject.common.DefaultTopology; import org.onosproject.event.Event; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; +import org.onosproject.net.DisjointPath; import org.onosproject.net.Link; import org.onosproject.net.Path; import org.onosproject.net.provider.ProviderId; @@ -39,6 +40,7 @@ import org.onosproject.store.AbstractStore; import org.slf4j.Logger; import java.util.List; +import java.util.Map; import java.util.Set; import static org.slf4j.LoggerFactory.getLogger; @@ -114,6 +116,29 @@ public class SimpleTopologyStore } @Override + public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst) { + return defaultTopology(topology).getDisjointPaths(src, dst); + } + + @Override + public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst, + LinkWeight weight) { + return defaultTopology(topology).getDisjointPaths(src, dst, weight); + } + + @Override + public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst, + Map<Link, Object> riskProfile) { + return defaultTopology(topology).getDisjointPaths(src, dst, riskProfile); + } + + @Override + public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst, + LinkWeight weight, Map<Link, Object> riskProfile) { + return defaultTopology(topology).getDisjointPaths(src, dst, weight, riskProfile); + } + + @Override public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) { return defaultTopology(topology).isInfrastructure(connectPoint); } diff --git a/framework/src/onos/core/net/pom.xml b/framework/src/onos/core/net/pom.xml index 9ea00070..c5d31263 100644 --- a/framework/src/onos/core/net/pom.xml +++ b/framework/src/onos/core/net/pom.xml @@ -52,6 +52,20 @@ <dependency> <groupId>org.onosproject</groupId> + <version>${project.version}</version> + <artifactId>onos-cli</artifactId> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-cli</artifactId> + <version>${project.version}</version> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> <artifactId>onos-core-common</artifactId> <version>${project.version}</version> <classifier>tests</classifier> diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ComponentConfigLoader.java b/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ComponentConfigLoader.java new file mode 100644 index 00000000..6678db27 --- /dev/null +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ComponentConfigLoader.java @@ -0,0 +1,74 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.cfg.impl; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.onosproject.cfg.ComponentConfigService; +import org.slf4j.Logger; + +import java.io.File; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Component responsible for automatically loading configuration file from + * configuration directory. + */ +@Component(immediate = true) +public class ComponentConfigLoader { + + private static final String CFG_JSON = "../config/component-cfg.json"; + static File cfgFile = new File(CFG_JSON); + + private final Logger log = getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ComponentConfigService configService; + + private ObjectNode root; + + @Activate + protected void activate() { + this.loadConfigs(); + log.info("Started"); + } + + // Loads the configurations for each component from the file in + // ../config/component-cfg.json, using the preSetProperty method. + private void loadConfigs() { + try { + if (cfgFile.exists()) { + root = (ObjectNode) new ObjectMapper().readTree(cfgFile); + root.fieldNames(). + forEachRemaining(component -> root.path(component).fieldNames() + .forEachRemaining(k -> configService + .preSetProperty(component, k, + root.path(component).path(k) + .asText()))); + log.info("Loaded initial component configuration from {}", cfgFile); + } + } catch (Exception e) { + log.warn("Unable to load initial component configuration from {}", + cfgFile, e); + } + } +} diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ComponentConfigManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ComponentConfigManager.java index 1933ee55..b3b22c76 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ComponentConfigManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ComponentConfigManager.java @@ -61,6 +61,9 @@ public class ComponentConfigManager implements ComponentConfigService { private static final String COMPONENT_NULL = "Component name cannot be null"; private static final String PROPERTY_NULL = "Property name cannot be null"; + private static final String COMPONENT_MISSING = "Component %s is not registered"; + private static final String PROPERTY_MISSING = "Property %s does not exist for %s"; + //Symbolic constants for use with the accumulator private static final int MAX_ITEMS = 100; @@ -160,6 +163,22 @@ public class ComponentConfigManager implements ComponentConfigService { checkNotNull(componentName, COMPONENT_NULL); checkNotNull(name, PROPERTY_NULL); + + checkArgument(properties.containsKey(componentName), + COMPONENT_MISSING, componentName); + checkArgument(properties.get(componentName).containsKey(name), + PROPERTY_MISSING, name, componentName); + store.setProperty(componentName, name, value); + } + + @Override + public void preSetProperty(String componentName, String name, String value) { + + checkPermission(CONFIG_WRITE); + + checkNotNull(componentName, COMPONENT_NULL); + checkNotNull(name, PROPERTY_NULL); + store.setProperty(componentName, name, value); } @@ -169,6 +188,11 @@ public class ComponentConfigManager implements ComponentConfigService { checkNotNull(componentName, COMPONENT_NULL); checkNotNull(name, PROPERTY_NULL); + + checkArgument(properties.containsKey(componentName), + COMPONENT_MISSING, componentName); + checkArgument(properties.get(componentName).containsKey(name), + PROPERTY_MISSING, name, componentName); store.unsetProperty(componentName, name); } diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/CoreManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/CoreManager.java index 8a441f61..f4d560a4 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/CoreManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/CoreManager.java @@ -24,7 +24,6 @@ import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.Service; import org.onlab.util.SharedExecutors; -import org.onlab.util.Tools; import org.onosproject.cfg.ComponentConfigService; import org.onosproject.core.ApplicationId; import org.onosproject.core.ApplicationIdStore; @@ -38,6 +37,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Dictionary; import java.util.List; import java.util.Set; @@ -87,9 +90,15 @@ public class CoreManager implements CoreService { public void activate() { registerApplication(CORE_APP_NAME); cfgService.registerProperties(getClass()); - List<String> versionLines = Tools.slurp(VERSION_FILE); - if (versionLines != null && !versionLines.isEmpty()) { - version = Version.version(versionLines.get(0)); + try { + Path path = Paths.get(VERSION_FILE.getPath()); + List<String> versionLines = Files.readAllLines(path); + if (versionLines != null && !versionLines.isEmpty()) { + version = Version.version(versionLines.get(0)); + } + } catch (IOException e) { + // version file not found, using default + log.trace("Version file not found", e); } } diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigManager.java index 5cd96cab..db484eea 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigManager.java @@ -96,7 +96,7 @@ public class NetworkConfigManager configClasses.put(identifier(configFactory), configFactory.configClass()); SubjectFactory subjectFactory = configFactory.subjectFactory(); - subjectClasses.putIfAbsent(subjectFactory.subjectKey(), subjectFactory); + subjectClasses.putIfAbsent(subjectFactory.subjectClassKey(), subjectFactory); subjectClassKeys.putIfAbsent(subjectFactory.subjectClass(), subjectFactory); store.addConfigFactory(configFactory); @@ -145,8 +145,8 @@ public class NetworkConfigManager } @Override - public SubjectFactory getSubjectFactory(String subjectKey) { - return subjectClasses.get(subjectKey); + public SubjectFactory getSubjectFactory(String subjectClassKey) { + return subjectClasses.get(subjectClassKey); } @Override @@ -155,8 +155,8 @@ public class NetworkConfigManager } @Override - public Class<? extends Config> getConfigClass(String subjectKey, String configKey) { - return configClasses.get(new ConfigIdentifier(subjectKey, configKey)); + public Class<? extends Config> getConfigClass(String subjectClassKey, String configKey) { + return configClasses.get(new ConfigIdentifier(subjectClassKey, configKey)); } @Override @@ -255,7 +255,7 @@ public class NetworkConfigManager } private static ConfigIdentifier identifier(ConfigFactory factory) { - return new ConfigIdentifier(factory.subjectFactory().subjectKey(), factory.configKey()); + return new ConfigIdentifier(factory.subjectFactory().subjectClassKey(), factory.configKey()); } static final class ConfigIdentifier { diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/BasicDeviceOperator.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/BasicDeviceOperator.java index 7900d185..fa90eb65 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/BasicDeviceOperator.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/BasicDeviceOperator.java @@ -15,21 +15,21 @@ */ package org.onosproject.net.device.impl; -import static org.slf4j.LoggerFactory.getLogger; -import static com.google.common.base.Preconditions.checkNotNull; - -import org.onosproject.net.config.ConfigOperator; -import org.onosproject.net.config.basics.BasicDeviceConfig; import org.onosproject.net.AnnotationKeys; import org.onosproject.net.DefaultAnnotations; import org.onosproject.net.Device; import org.onosproject.net.SparseAnnotations; +import org.onosproject.net.config.ConfigOperator; +import org.onosproject.net.config.basics.BasicDeviceConfig; import org.onosproject.net.device.DefaultDeviceDescription; import org.onosproject.net.device.DeviceDescription; import org.slf4j.Logger; import java.util.Objects; +import static com.google.common.base.Preconditions.checkNotNull; +import static org.slf4j.LoggerFactory.getLogger; + /** * Implementations of merge policies for various sources of device configuration * information. This includes applications, provides, and network configurations. @@ -46,7 +46,7 @@ public final class BasicDeviceOperator implements ConfigOperator { * Generates a DeviceDescription containing fields from a DeviceDescription and * a DeviceConfig. * - * @param bdc the device config entity from network config + * @param bdc the device config entity from network config * @param descr a DeviceDescription * @return DeviceDescription based on both sources */ @@ -70,7 +70,7 @@ public final class BasicDeviceOperator implements ConfigOperator { * Generates an annotation from an existing annotation and DeviceConfig. * * @param bdc the device config entity from network config - * @param an the annotation + * @param an the annotation * @return annotation combining both sources */ public static SparseAnnotations combine(BasicDeviceConfig bdc, SparseAnnotations an) { @@ -93,6 +93,9 @@ public final class BasicDeviceOperator implements ConfigOperator { if (bdc.owner() != null) { newBuilder.set(AnnotationKeys.OWNER, bdc.owner()); } + if (bdc.managementAddress() != null) { + newBuilder.set(AnnotationKeys.MANAGEMENT_ADDRESS, bdc.managementAddress()); + } DefaultAnnotations newAnnotations = newBuilder.build(); return DefaultAnnotations.union(an, newAnnotations); } diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java index b0b3abe2..e35dc0c5 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java @@ -15,8 +15,26 @@ */ package org.onosproject.net.device.impl; -import com.google.common.collect.Lists; -import com.google.common.util.concurrent.Futures; +import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; +import static org.onlab.util.Tools.groupedThreads; +import static org.onosproject.net.MastershipRole.MASTER; +import static org.onosproject.net.MastershipRole.NONE; +import static org.onosproject.net.MastershipRole.STANDBY; +import static org.onosproject.security.AppGuard.checkPermission; +import static org.onosproject.security.AppPermission.Type.DEVICE_READ; +import static org.slf4j.LoggerFactory.getLogger; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; @@ -26,12 +44,6 @@ import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.Service; import org.onosproject.cluster.ClusterService; import org.onosproject.cluster.NodeId; -import org.onosproject.net.provider.AbstractListenerProviderRegistry; -import org.onosproject.net.config.NetworkConfigEvent; -import org.onosproject.net.config.NetworkConfigListener; -import org.onosproject.net.config.NetworkConfigService; -import org.onosproject.net.config.basics.BasicDeviceConfig; -import org.onosproject.net.config.basics.OpticalPortConfig; import org.onosproject.mastership.MastershipEvent; import org.onosproject.mastership.MastershipListener; import org.onosproject.mastership.MastershipService; @@ -44,6 +56,11 @@ import org.onosproject.net.DeviceId; import org.onosproject.net.MastershipRole; import org.onosproject.net.Port; import org.onosproject.net.PortNumber; +import org.onosproject.net.config.NetworkConfigEvent; +import org.onosproject.net.config.NetworkConfigListener; +import org.onosproject.net.config.NetworkConfigService; +import org.onosproject.net.config.basics.BasicDeviceConfig; +import org.onosproject.net.config.basics.OpticalPortConfig; import org.onosproject.net.device.DefaultDeviceDescription; import org.onosproject.net.device.DefaultPortDescription; import org.onosproject.net.device.DeviceAdminService; @@ -58,27 +75,11 @@ import org.onosproject.net.device.DeviceStore; import org.onosproject.net.device.DeviceStoreDelegate; import org.onosproject.net.device.PortDescription; import org.onosproject.net.device.PortStatistics; +import org.onosproject.net.provider.AbstractListenerProviderRegistry; import org.onosproject.net.provider.AbstractProviderService; import org.slf4j.Logger; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import static com.google.common.base.Preconditions.checkNotNull; -import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; -import static org.onlab.util.Tools.groupedThreads; -import static org.onosproject.net.MastershipRole.*; -import static org.onosproject.security.AppGuard.checkPermission; -import static org.slf4j.LoggerFactory.getLogger; -import static org.onosproject.security.AppPermission.Type.*; +import com.google.common.util.concurrent.Futures; /** * Provides implementation of the device SB & NB APIs. @@ -347,11 +348,15 @@ public class DeviceManager log.info("Device {} disconnected from this node", deviceId); List<Port> ports = store.getPorts(deviceId); - List<PortDescription> descs = Lists.newArrayList(); - ports.forEach(port -> - descs.add(new DefaultPortDescription(port.number(), - false, port.type(), - port.portSpeed()))); + final Device device = getDevice(deviceId); + + List<PortDescription> descs = ports.stream().map( + port -> (!(Device.Type.ROADM.equals(device.type()))) ? + new DefaultPortDescription(port.number(), false, + port.type(), port.portSpeed()) : + OpticalPortOperator.descriptionOf(port, false) + ).collect(Collectors.toList()); + store.updatePorts(this.provider().id(), deviceId, descs); try { if (mastershipService.isLocalMaster(deviceId)) { @@ -430,6 +435,12 @@ public class DeviceManager portDescription); return; } + final Device device = getDevice(deviceId); + if ((Device.Type.ROADM.equals(device.type()))) { + Port port = getPort(deviceId, portDescription.portNumber()); + portDescription = OpticalPortOperator.descriptionOf(port, portDescription.isEnabled()); + } + portDescription = consolidate(deviceId, portDescription); final DeviceEvent event = store.updatePortStatus(this.provider().id(), deviceId, portDescription); diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java index b2fd02c7..8f2bda01 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java @@ -151,8 +151,25 @@ public final class OpticalPortOperator implements ConfigOperator { */ public static PortDescription descriptionOf(Port port) { checkNotNull(port, "Must supply non-null Port"); + final boolean isUp = port.isEnabled(); + return descriptionOfPort(port, isUp); + } + + /** + * Returns a description built from an existing port and reported status. + * + * @param port + * @param isEnabled + * @return a PortDescription based on the port + */ + static PortDescription descriptionOf(Port port, boolean isEnabled) { + checkNotNull(port, "Must supply non-null Port"); + final boolean isup = isEnabled; + return descriptionOfPort(port, isup); + } + + private static PortDescription descriptionOfPort(Port port, final boolean isup) { final PortNumber ptn = port.number(); - final boolean isup = port.isEnabled(); final SparseAnnotations an = (SparseAnnotations) port.annotations(); switch (port.type()) { case OMS: diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java index a1d046c5..5958d1f5 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java @@ -22,6 +22,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; 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; @@ -58,6 +59,7 @@ import org.onosproject.net.flow.FlowRuleProviderService; import org.onosproject.net.flow.FlowRuleService; import org.onosproject.net.flow.FlowRuleStore; import org.onosproject.net.flow.FlowRuleStoreDelegate; +import org.onosproject.net.flow.TableStatisticsEntry; import org.onosproject.net.provider.AbstractProviderService; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; @@ -388,6 +390,16 @@ public class FlowRuleManager @Override public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries) { + pushFlowMetricsInternal(deviceId, flowEntries, true); + } + + @Override + public void pushFlowMetricsWithoutFlowMissing(DeviceId deviceId, Iterable<FlowEntry> flowEntries) { + pushFlowMetricsInternal(deviceId, flowEntries, false); + } + + private void pushFlowMetricsInternal(DeviceId deviceId, Iterable<FlowEntry> flowEntries, + boolean useMissingFlow) { Map<FlowEntry, FlowEntry> storedRules = Maps.newHashMap(); store.getFlowEntries(deviceId).forEach(f -> storedRules.put(f, f)); @@ -415,17 +427,20 @@ public class FlowRuleManager continue; } } - for (FlowEntry rule : storedRules.keySet()) { - try { - // there are rules in the store that aren't on the switch - log.debug("Adding rule in store, but not on switch {}", rule); - flowMissing(rule); - } catch (Exception e) { - log.debug("Can't add missing flow rule {}", e.getMessage()); - continue; + + // DO NOT reinstall + if (useMissingFlow) { + for (FlowEntry rule : storedRules.keySet()) { + try { + // there are rules in the store that aren't on the switch + log.debug("Adding rule in store, but not on switch {}", rule); + flowMissing(rule); + } catch (Exception e) { + log.debug("Can't add missing flow rule {}", e.getMessage()); + continue; + } } } - } @Override @@ -435,6 +450,12 @@ public class FlowRuleManager operation )); } + + @Override + public void pushTableStatistics(DeviceId deviceId, + List<TableStatisticsEntry> tableStats) { + store.updateTableStatistics(deviceId, tableStats); + } } // Store delegate to re-post events emitted from the store. @@ -590,4 +611,10 @@ public class FlowRuleManager } } + + @Override + public Iterable<TableStatisticsEntry> getFlowTableStatistics(DeviceId deviceId) { + checkPermission(FLOWRULE_READ); + return store.getTableStatistics(deviceId); + } } diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java index 43f346b7..1473f33f 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java @@ -236,6 +236,16 @@ public class HostManager post(event); } } + + @Override + public void removeIpFromHost(HostId hostId, IpAddress ipAddress) { + checkNotNull(hostId, HOST_ID_NULL); + checkValidity(); + HostEvent event = store.removeIp(hostId, ipAddress); + if (event != null) { + post(event); + } + } } // Store delegate to re-post events emitted from the store. diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java index 44f8cbf0..4dc93a51 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java @@ -84,6 +84,7 @@ public class HostMonitor implements TimerTask { * @param hostManager host manager used to look up host information and * probe existing hosts * @param interfaceService interface service for interface information + * @param edgePortService edge port service */ public HostMonitor(PacketService packetService, HostManager hostManager, InterfaceService interfaceService, diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCleanup.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCleanup.java index d7fa3223..417627ad 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCleanup.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCleanup.java @@ -196,7 +196,7 @@ public class IntentCleanup implements Runnable, IntentListener { service.withdraw(intentData.intent()); break; default: - log.warn("Trying to resubmit pending intent {} in state {} with request {}", + log.warn("Failed to resubmit pending intent {} in state {} with request {}", intentData.key(), intentData.state(), intentData.request()); break; } diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java index 4c828e77..baa3bf4d 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java @@ -256,15 +256,16 @@ public class IntentManager submit(intent); } - // If required, compile all currently failed intents. - for (Intent intent : getIntents()) { - IntentState state = getIntentState(intent.key()); - if ((compileAllFailed && RECOMPILE.contains(state)) - || intentAllowsPartialFailure(intent)) { - if (WITHDRAW.contains(state)) { - withdraw(intent); - } else { - submit(intent); + if (compileAllFailed) { + // If required, compile all currently failed intents. + for (Intent intent : getIntents()) { + IntentState state = getIntentState(intent.key()); + if (RECOMPILE.contains(state) || intentAllowsPartialFailure(intent)) { + if (WITHDRAW.contains(state)) { + withdraw(intent); + } else { + submit(intent); + } } } } diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java index 5710aced..5ebc812e 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java @@ -16,8 +16,8 @@ package org.onosproject.net.intent.impl; import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import com.google.common.collect.SetMultimap; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; @@ -61,7 +61,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -91,8 +90,6 @@ public class ObjectiveTracker implements ObjectiveTrackerService { private final Logger log = getLogger(getClass()); - private final ConcurrentMap<Key, Intent> intents = Maps.newConcurrentMap(); - private final SetMultimap<LinkKey, Key> intentsByLink = //TODO this could be slow as a point of synchronization synchronizedSetMultimap(HashMultimap.<LinkKey, Key>create()); @@ -378,7 +375,12 @@ public class ObjectiveTracker implements ObjectiveTrackerService { } // TODO should we recompile on available==true? - delegate.triggerCompile(intentsByDevice.get(id), available); + + final ImmutableSet<Key> snapshot; + synchronized (intentsByDevice) { + snapshot = ImmutableSet.copyOf(intentsByDevice.get(id)); + } + delegate.triggerCompile(snapshot, available); } } @@ -415,9 +417,17 @@ public class ObjectiveTracker implements ObjectiveTrackerService { @Override public void event(HostEvent event) { HostId id = event.subject().id(); - HostEvent.Type type = event.type(); - boolean available = (type == HostEvent.Type.HOST_ADDED); - executorService.execute(new DeviceAvailabilityHandler(id, available)); + switch (event.type()) { + case HOST_ADDED: + case HOST_MOVED: + case HOST_REMOVED: + executorService.execute(new DeviceAvailabilityHandler(id, false)); + break; + case HOST_UPDATED: + default: + // DO NOTHING + break; + } } } diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java index 99f58df7..c6eb7c5a 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java @@ -15,10 +15,8 @@ */ package org.onosproject.net.intent.impl.compiler; -import com.google.common.collect.Sets; import org.apache.commons.lang3.tuple.Pair; import org.apache.felix.scr.annotations.Activate; -import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Modified; import org.apache.felix.scr.annotations.Property; @@ -50,7 +48,10 @@ import org.onosproject.net.intent.IntentService; import org.onosproject.net.intent.OpticalCircuitIntent; import org.onosproject.net.intent.OpticalConnectivityIntent; import org.onosproject.net.intent.impl.IntentCompilationException; -import org.onosproject.net.resource.device.DeviceResourceService; +import org.onosproject.net.newresource.ResourceAllocation; +import org.onosproject.net.newresource.ResourcePath; +import org.onosproject.net.newresource.ResourceService; +import org.onosproject.net.resource.device.IntentSetMultimap; import org.onosproject.net.resource.link.LinkResourceAllocations; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; @@ -60,6 +61,7 @@ import java.util.Collections; import java.util.Dictionary; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; @@ -67,7 +69,8 @@ import static com.google.common.base.Preconditions.checkArgument; /** * An intent compiler for {@link org.onosproject.net.intent.OpticalCircuitIntent}. */ -@Component(immediate = true) +// For now, remove component designation until dependency on the new resource manager is available. +// @Component(immediate = true) public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircuitIntent> { private static final Logger log = LoggerFactory.getLogger(OpticalCircuitIntentCompiler.class); @@ -92,7 +95,10 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu protected DeviceService deviceService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected DeviceResourceService deviceResourceService; + protected ResourceService resourceService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected IntentSetMultimap intentSetMultimap; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected IntentService intentService; @@ -153,7 +159,10 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu log.debug("Compiling optical circuit intent between {} and {}", src, dst); // Reserve OduClt ports - if (!deviceResourceService.requestPorts(Sets.newHashSet(srcPort, dstPort), intent)) { + ResourcePath srcPortPath = new ResourcePath(src.deviceId(), src.port()); + ResourcePath dstPortPath = new ResourcePath(dst.deviceId(), dst.port()); + List<ResourceAllocation> allocation = resourceService.allocate(intent.id(), srcPortPath, dstPortPath); + if (allocation.isEmpty()) { throw new IntentCompilationException("Unable to reserve ports for intent " + intent); } @@ -199,7 +208,7 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu circuitIntent = new FlowRuleIntent(appId, rules, intent.resources()); // Save circuit to connectivity intent mapping - deviceResourceService.requestMapping(connIntent.id(), intent.id()); + intentSetMultimap.allocateMapping(connIntent.id(), intent.id()); intents.add(circuitIntent); return intents; @@ -209,16 +218,15 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu * Checks if current allocations on given resource can satisfy request. * If the resource is null, return true. * - * @param request the intent making the request * @param resource the resource on which to map the intent * @return true if the resource can accept the request, false otherwise */ - private boolean isAvailable(Intent request, IntentId resource) { + private boolean isAvailable(IntentId resource) { if (resource == null) { return true; } - Set<IntentId> mapping = deviceResourceService.getMapping(resource); + Set<IntentId> mapping = intentSetMultimap.getMapping(resource); if (mapping == null) { return true; @@ -271,7 +279,7 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu continue; } - if (isAvailable(circuitIntent, connIntent.id())) { + if (isAvailable(connIntent.id())) { return connIntent; } } @@ -296,14 +304,19 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu return null; } - private OchPort findAvailableOchPort(ConnectPoint oduPort, OpticalCircuitIntent circuitIntent) { + private OchPort findAvailableOchPort(ConnectPoint oduPort) { // First see if the port mappings are constrained ConnectPoint ochCP = staticPort(oduPort); if (ochCP != null) { OchPort ochPort = (OchPort) deviceService.getPort(ochCP.deviceId(), ochCP.port()); - IntentId intentId = deviceResourceService.getAllocations(ochPort); - if (isAvailable(circuitIntent, intentId)) { + Optional<IntentId> intentId = + resourceService.getResourceAllocation(new ResourcePath(ochCP.deviceId(), ochCP.port())) + .map(ResourceAllocation::consumer) + .filter(x -> x instanceof IntentId) + .map(x -> (IntentId) x); + + if (isAvailable(intentId.orElse(null))) { return ochPort; } } @@ -316,8 +329,12 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu continue; } - IntentId intentId = deviceResourceService.getAllocations(port); - if (isAvailable(circuitIntent, intentId)) { + Optional<IntentId> intentId = + resourceService.getResourceAllocation(new ResourcePath(oduPort.deviceId(), port.number())) + .map(ResourceAllocation::consumer) + .filter(x -> x instanceof IntentId) + .map(x -> (IntentId) x); + if (isAvailable(intentId.orElse(null))) { return (OchPort) port; } } @@ -327,12 +344,12 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu private Pair<OchPort, OchPort> findPorts(OpticalCircuitIntent intent) { - OchPort srcPort = findAvailableOchPort(intent.getSrc(), intent); + OchPort srcPort = findAvailableOchPort(intent.getSrc()); if (srcPort == null) { return null; } - OchPort dstPort = findAvailableOchPort(intent.getDst(), intent); + OchPort dstPort = findAvailableOchPort(intent.getDst()); if (dstPort == null) { return null; } diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java index 05a20f96..eb5b4af8 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java @@ -16,9 +16,7 @@ package org.onosproject.net.intent.impl.compiler; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; 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; @@ -40,9 +38,9 @@ import org.onosproject.net.intent.IntentExtensionService; import org.onosproject.net.intent.OpticalConnectivityIntent; import org.onosproject.net.intent.OpticalPathIntent; import org.onosproject.net.intent.impl.IntentCompilationException; -import org.onosproject.net.resource.ResourceAllocation; +import org.onosproject.net.newresource.ResourcePath; +import org.onosproject.net.newresource.ResourceService; import org.onosproject.net.resource.ResourceType; -import org.onosproject.net.resource.device.DeviceResourceService; import org.onosproject.net.resource.link.DefaultLinkResourceRequest; import org.onosproject.net.resource.link.LambdaResource; import org.onosproject.net.resource.link.LambdaResourceAllocation; @@ -57,13 +55,15 @@ import org.slf4j.LoggerFactory; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; /** * An intent compiler for {@link org.onosproject.net.intent.OpticalConnectivityIntent}. */ -@Component(immediate = true) +// For now, remove component designation until dependency on the new resource manager is available. +// @Component(immediate = true) public class OpticalConnectivityIntentCompiler implements IntentCompiler<OpticalConnectivityIntent> { protected static final Logger log = LoggerFactory.getLogger(OpticalConnectivityIntentCompiler.class); @@ -78,10 +78,10 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical protected DeviceService deviceService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected LinkResourceService linkResourceService; + protected ResourceService resourceService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected DeviceResourceService deviceResourceService; + protected LinkResourceService linkResourceService; @Activate public void activate() { @@ -108,7 +108,11 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical log.debug("Compiling optical connectivity intent between {} and {}", src, dst); // Reserve OCh ports - if (!deviceResourceService.requestPorts(ImmutableSet.of(srcPort, dstPort), intent)) { + ResourcePath srcPortPath = new ResourcePath(src.deviceId(), src.port()); + ResourcePath dstPortPath = new ResourcePath(dst.deviceId(), dst.port()); + List<org.onosproject.net.newresource.ResourceAllocation> allocation = + resourceService.allocate(intent.id(), srcPortPath, dstPortPath); + if (allocation.isEmpty()) { throw new IntentCompilationException("Unable to reserve ports for intent " + intent); } @@ -161,7 +165,7 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical } // Release port allocations if unsuccessful - deviceResourceService.releasePorts(intent.id()); + resourceService.release(intent.id()); throw new IntentCompilationException("Unable to find suitable lightpath for intent " + intent); } @@ -174,15 +178,12 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical * @return lambda allocated to the given path */ private LambdaResourceAllocation getWavelength(Path path, LinkResourceAllocations linkAllocs) { - for (Link link : path.links()) { - for (ResourceAllocation alloc : linkAllocs.getResourceAllocation(link)) { - if (alloc.type() == ResourceType.LAMBDA) { - return (LambdaResourceAllocation) alloc; - } - } - } - - return null; + return path.links().stream() + .flatMap(x -> linkAllocs.getResourceAllocation(x).stream()) + .filter(x -> x.type() == ResourceType.LAMBDA) + .findFirst() + .map(x -> (LambdaResourceAllocation) x) + .orElse(null); } /** @@ -215,23 +216,23 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical return false; } - LambdaResource lambda = null; + List<LambdaResource> lambdas = path.links().stream() + .flatMap(x -> allocations.getResourceAllocation(x).stream()) + .filter(x -> x.type() == ResourceType.LAMBDA) + .map(x -> ((LambdaResourceAllocation) x).lambda()) + .collect(Collectors.toList()); - for (Link link : path.links()) { - for (ResourceAllocation alloc : allocations.getResourceAllocation(link)) { - if (alloc.type() == ResourceType.LAMBDA) { - LambdaResource nextLambda = ((LambdaResourceAllocation) alloc).lambda(); - if (nextLambda == null) { - return false; - } - if (lambda == null) { - lambda = nextLambda; - continue; - } - if (!lambda.equals(nextLambda)) { - return false; - } - } + LambdaResource lambda = null; + for (LambdaResource nextLambda: lambdas) { + if (nextLambda == null) { + return false; + } + if (lambda == null) { + lambda = nextLambda; + continue; + } + if (!lambda.equals(nextLambda)) { + return false; } } diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompiler.java index ca9ae5cc..2cc45e79 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompiler.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompiler.java @@ -39,7 +39,6 @@ import org.onosproject.net.intent.IntentCompiler; import org.onosproject.net.intent.IntentExtensionService; import org.onosproject.net.intent.OpticalPathIntent; import org.onosproject.net.resource.link.LinkResourceAllocations; -import org.onosproject.net.resource.link.LinkResourceService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -59,9 +58,6 @@ public class OpticalPathIntentCompiler implements IntentCompiler<OpticalPathInte @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected CoreService coreService; - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected LinkResourceService resourceService; - private ApplicationId appId; @Activate diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java index 2cd1a2e0..5226967f 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java @@ -91,6 +91,14 @@ public final class ResourceManager implements ResourceService, ResourceAdminServ } @Override + public Optional<ResourceAllocation> getResourceAllocation(ResourcePath resource) { + checkNotNull(resource); + + Optional<ResourceConsumer> consumer = store.getConsumer(resource); + return consumer.map(x -> new ResourceAllocation(resource, x)); + } + + @Override public <T> Collection<ResourceAllocation> getResourceAllocations(ResourcePath parent, Class<T> cls) { checkNotNull(parent); checkNotNull(cls); diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java index a0bc693c..8e87a07d 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java @@ -15,7 +15,8 @@ */ package org.onosproject.net.packet.impl; -import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; @@ -43,6 +44,7 @@ import org.onosproject.net.packet.PacketContext; import org.onosproject.net.packet.PacketEvent; import org.onosproject.net.packet.PacketPriority; import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketProcessorEntry; import org.onosproject.net.packet.PacketProvider; import org.onosproject.net.packet.PacketProviderRegistry; import org.onosproject.net.packet.PacketProviderService; @@ -55,8 +57,6 @@ import org.onosproject.net.provider.AbstractProviderService; import org.slf4j.Logger; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -102,18 +102,18 @@ public class PacketManager private final DeviceListener deviceListener = new InternalDeviceListener(); - private final Map<Integer, PacketProcessor> processors = new ConcurrentHashMap<>(); + private final List<ProcessorEntry> processors = Lists.newCopyOnWriteArrayList(); private ApplicationId appId; @Activate public void activate() { eventHandlingExecutor = Executors.newSingleThreadExecutor( - groupedThreads("onos/net/packet", "event-handler")); + groupedThreads("onos/net/packet", "event-handler")); appId = coreService.getAppId(CoreService.CORE_APP_NAME); store.setDelegate(delegate); deviceService.addListener(deviceListener); - // TODO: Should we request packets for all existing devices? I believe we should. + store.existingRequests().forEach(this::pushToAllDevices); log.info("Started"); } @@ -129,19 +129,35 @@ public class PacketManager public void addProcessor(PacketProcessor processor, int priority) { checkPermission(PACKET_EVENT); checkNotNull(processor, "Processor cannot be null"); - processors.put(priority, processor); + ProcessorEntry entry = new ProcessorEntry(processor, priority); + + // Insert the new processor according to its priority. + int i = 0; + for (; i < processors.size(); i++) { + if (priority < processors.get(i).priority()) { + break; + } + } + processors.add(i, entry); } @Override public void removeProcessor(PacketProcessor processor) { checkPermission(PACKET_EVENT); checkNotNull(processor, "Processor cannot be null"); - processors.values().remove(processor); + + // Remove the processor entry. + for (int i = 0; i < processors.size(); i++) { + if (processors.get(i).processor() == processor) { + processors.remove(i); + break; + } + } } @Override - public Map<Integer, PacketProcessor> getProcessors() { - return ImmutableMap.copyOf(processors); + public List<PacketProcessorEntry> getProcessors() { + return ImmutableList.copyOf(processors); } @Override @@ -152,9 +168,7 @@ public class PacketManager checkNotNull(appId, "Application ID cannot be null"); PacketRequest request = new DefaultPacketRequest(selector, priority, appId); - if (store.requestPackets(request)) { - pushToAllDevices(request); - } + store.requestPackets(request); } @Override @@ -165,9 +179,7 @@ public class PacketManager checkNotNull(appId, "Application ID cannot be null"); PacketRequest request = new DefaultPacketRequest(selector, priority, appId); - if (store.cancelPackets(request)) { - removeFromAllDevices(request); - } + store.cancelPackets(request); } @Override @@ -176,6 +188,18 @@ public class PacketManager } /** + * Pushes all rules to the specified device. + * + * @param device device on which to install packet request flows + */ + private void pushRulesToDevice(Device device) { + log.debug("Pushing packet requests to device {}", device.id()); + for (PacketRequest request : store.existingRequests()) { + pushRule(device, request); + } + } + + /** * Pushes a packet request flow rule to all devices. * * @param request the packet request @@ -187,16 +211,13 @@ public class PacketManager } } - /** * Removes packet request flow rule from all devices. * * @param request the packet request */ private void removeFromAllDevices(PacketRequest request) { - for (Device device : deviceService.getDevices()) { - removeRule(device, request); - } + deviceService.getAvailableDevices().forEach(d -> removeRule(d, request)); } /** @@ -232,7 +253,6 @@ public class PacketManager if (!device.type().equals(Device.Type.SWITCH)) { return; } - ForwardingObjective forwarding = createBuilder(request) .remove(new ObjectiveContext() { @Override @@ -241,7 +261,6 @@ public class PacketManager request, device.id(), error); } }); - objectiveService.forward(device.id(), forwarding); } @@ -263,12 +282,10 @@ public class PacketManager } private void localEmit(OutboundPacket packet) { - final Device device = deviceService.getDevice(packet.sendThrough()); - + Device device = deviceService.getDevice(packet.sendThrough()); if (device == null) { return; } - PacketProvider packetProvider = getProvider(device.providerId()); if (packetProvider != null) { packetProvider.emit(packet); @@ -280,7 +297,9 @@ public class PacketManager return new InternalPacketProviderService(provider); } - // Personalized packet provider service issued to the supplied provider. + /** + * Personalized packet provider service issued to the supplied provider. + */ private class InternalPacketProviderService extends AbstractProviderService<PacketProvider> implements PacketProviderService { @@ -292,8 +311,10 @@ public class PacketManager @Override public void processPacket(PacketContext context) { // TODO filter packets sent to processors based on registrations - for (PacketProcessor processor : processors.values()) { - processor.process(context); + for (ProcessorEntry entry : processors) { + long start = System.nanoTime(); + entry.processor().process(context); + entry.addNanos(System.nanoTime() - start); } } @@ -307,6 +328,16 @@ public class PacketManager public void notify(PacketEvent event) { localEmit(event.subject()); } + + @Override + public void requestPackets(PacketRequest request) { + pushToAllDevices(request); + } + + @Override + public void cancelPackets(PacketRequest request) { + removeFromAllDevices(request); + } } /** @@ -319,17 +350,14 @@ public class PacketManager try { Device device = event.subject(); switch (event.type()) { - case DEVICE_ADDED: - case DEVICE_AVAILABILITY_CHANGED: - if (deviceService.isAvailable(event.subject().id())) { - log.debug("Pushing packet requests to device {}", event.subject().id()); - for (PacketRequest request : store.existingRequests()) { - pushRule(device, request); + case DEVICE_ADDED: + case DEVICE_AVAILABILITY_CHANGED: + if (deviceService.isAvailable(event.subject().id())) { + pushRulesToDevice(device); } - } - break; - default: - break; + break; + default: + break; } } catch (Exception e) { log.warn("Failed to process {}", event, e); @@ -338,4 +366,48 @@ public class PacketManager } } + /** + * Entity for tracking stats for a packet processor. + */ + private class ProcessorEntry implements PacketProcessorEntry { + private final PacketProcessor processor; + private final int priority; + private long invocations = 0; + private long nanos = 0; + + public ProcessorEntry(PacketProcessor processor, int priority) { + this.processor = processor; + this.priority = priority; + } + + @Override + public PacketProcessor processor() { + return processor; + } + + @Override + public int priority() { + return priority; + } + + @Override + public long invocations() { + return invocations; + } + + @Override + public long totalNanos() { + return nanos; + } + + @Override + public long averageNanos() { + return invocations > 0 ? nanos / invocations : 0; + } + + void addNanos(long nanos) { + this.nanos += nanos; + this.invocations++; + } + } } diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/statistic/impl/FlowStatisticManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/statistic/impl/FlowStatisticManager.java new file mode 100644 index 00000000..6515ef31 --- /dev/null +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/statistic/impl/FlowStatisticManager.java @@ -0,0 +1,634 @@ +/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.net.statistic.impl;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableSet;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.cli.Comparators;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTypedFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.FlowRuleListener;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TypedStoredFlowEntry;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.statistic.DefaultLoad;
+import org.onosproject.net.statistic.FlowStatisticService;
+import org.onosproject.net.statistic.Load;
+import org.onosproject.net.statistic.FlowStatisticStore;
+import org.onosproject.net.statistic.SummaryFlowEntryWithLoad;
+import org.onosproject.net.statistic.TypedFlowEntryWithLoad;
+
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.security.AppPermission.Type.*;
+
+/**
+ * Provides an implementation of the Flow Statistic Service.
+ */
+@Component(immediate = true, enabled = true)
+@Service
+public class FlowStatisticManager implements FlowStatisticService {
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowStatisticStore flowStatisticStore;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ private final InternalFlowRuleStatsListener frListener = new InternalFlowRuleStatsListener();
+
+ @Activate
+ public void activate() {
+ flowRuleService.addListener(frListener);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ flowRuleService.removeListener(frListener);
+ log.info("Stopped");
+ }
+
+ @Override
+ public Map<ConnectPoint, SummaryFlowEntryWithLoad> loadSummary(Device device) {
+ checkPermission(STATISTIC_READ);
+
+ Map<ConnectPoint, SummaryFlowEntryWithLoad> summaryLoad = new TreeMap<>(Comparators.CONNECT_POINT_COMPARATOR);
+
+ if (device == null) {
+ return summaryLoad;
+ }
+
+ List<Port> ports = new ArrayList<>(deviceService.getPorts(device.id()));
+
+ for (Port port : ports) {
+ ConnectPoint cp = new ConnectPoint(device.id(), port.number());
+ SummaryFlowEntryWithLoad sfe = loadSummaryPortInternal(cp);
+ summaryLoad.put(cp, sfe);
+ }
+
+ return summaryLoad;
+ }
+
+ @Override
+ public SummaryFlowEntryWithLoad loadSummary(Device device, PortNumber pNumber) {
+ checkPermission(STATISTIC_READ);
+
+ ConnectPoint cp = new ConnectPoint(device.id(), pNumber);
+ return loadSummaryPortInternal(cp);
+ }
+
+ @Override
+ public Map<ConnectPoint, List<TypedFlowEntryWithLoad>> loadAllByType(Device device,
+ TypedStoredFlowEntry.FlowLiveType liveType,
+ Instruction.Type instType) {
+ checkPermission(STATISTIC_READ);
+
+ Map<ConnectPoint, List<TypedFlowEntryWithLoad>> allLoad = new TreeMap<>(Comparators.CONNECT_POINT_COMPARATOR);
+
+ if (device == null) {
+ return allLoad;
+ }
+
+ List<Port> ports = new ArrayList<>(deviceService.getPorts(device.id()));
+
+ for (Port port : ports) {
+ ConnectPoint cp = new ConnectPoint(device.id(), port.number());
+ List<TypedFlowEntryWithLoad> tfel = loadAllPortInternal(cp, liveType, instType);
+ allLoad.put(cp, tfel);
+ }
+
+ return allLoad;
+ }
+
+ @Override
+ public List<TypedFlowEntryWithLoad> loadAllByType(Device device, PortNumber pNumber,
+ TypedStoredFlowEntry.FlowLiveType liveType,
+ Instruction.Type instType) {
+ checkPermission(STATISTIC_READ);
+
+ ConnectPoint cp = new ConnectPoint(device.id(), pNumber);
+ return loadAllPortInternal(cp, liveType, instType);
+ }
+
+ @Override
+ public Map<ConnectPoint, List<TypedFlowEntryWithLoad>> loadTopnByType(Device device,
+ TypedStoredFlowEntry.FlowLiveType liveType,
+ Instruction.Type instType,
+ int topn) {
+ checkPermission(STATISTIC_READ);
+
+ Map<ConnectPoint, List<TypedFlowEntryWithLoad>> allLoad = new TreeMap<>(Comparators.CONNECT_POINT_COMPARATOR);
+
+ if (device == null) {
+ return allLoad;
+ }
+
+ List<Port> ports = new ArrayList<>(deviceService.getPorts(device.id()));
+
+ for (Port port : ports) {
+ ConnectPoint cp = new ConnectPoint(device.id(), port.number());
+ List<TypedFlowEntryWithLoad> tfel = loadTopnPortInternal(cp, liveType, instType, topn);
+ allLoad.put(cp, tfel);
+ }
+
+ return allLoad;
+ }
+
+ @Override
+ public List<TypedFlowEntryWithLoad> loadTopnByType(Device device, PortNumber pNumber,
+ TypedStoredFlowEntry.FlowLiveType liveType,
+ Instruction.Type instType,
+ int topn) {
+ checkPermission(STATISTIC_READ);
+
+ ConnectPoint cp = new ConnectPoint(device.id(), pNumber);
+ return loadTopnPortInternal(cp, liveType, instType, topn);
+ }
+
+ private SummaryFlowEntryWithLoad loadSummaryPortInternal(ConnectPoint cp) {
+ checkPermission(STATISTIC_READ);
+
+ Set<FlowEntry> currentStats;
+ Set<FlowEntry> previousStats;
+
+ TypedStatistics typedStatistics;
+ synchronized (flowStatisticStore) {
+ currentStats = flowStatisticStore.getCurrentFlowStatistic(cp);
+ if (currentStats == null) {
+ return new SummaryFlowEntryWithLoad(cp, new DefaultLoad());
+ }
+ previousStats = flowStatisticStore.getPreviousFlowStatistic(cp);
+ if (previousStats == null) {
+ return new SummaryFlowEntryWithLoad(cp, new DefaultLoad());
+ }
+ // copy to local flow entry
+ typedStatistics = new TypedStatistics(currentStats, previousStats);
+
+ // Check for validity of this stats data
+ checkLoadValidity(currentStats, previousStats);
+ }
+
+ // current and previous set is not empty!
+ Set<FlowEntry> currentSet = typedStatistics.current();
+ Set<FlowEntry> previousSet = typedStatistics.previous();
+ Load totalLoad = new DefaultLoad(aggregateBytesSet(currentSet), aggregateBytesSet(previousSet),
+ TypedFlowEntryWithLoad.avgPollInterval());
+
+ Map<FlowRule, TypedStoredFlowEntry> currentMap;
+ Map<FlowRule, TypedStoredFlowEntry> previousMap;
+
+ currentMap = typedStatistics.currentImmediate();
+ previousMap = typedStatistics.previousImmediate();
+ Load immediateLoad = new DefaultLoad(aggregateBytesMap(currentMap), aggregateBytesMap(previousMap),
+ TypedFlowEntryWithLoad.shortPollInterval());
+
+ currentMap = typedStatistics.currentShort();
+ previousMap = typedStatistics.previousShort();
+ Load shortLoad = new DefaultLoad(aggregateBytesMap(currentMap), aggregateBytesMap(previousMap),
+ TypedFlowEntryWithLoad.shortPollInterval());
+
+ currentMap = typedStatistics.currentMid();
+ previousMap = typedStatistics.previousMid();
+ Load midLoad = new DefaultLoad(aggregateBytesMap(currentMap), aggregateBytesMap(previousMap),
+ TypedFlowEntryWithLoad.midPollInterval());
+
+ currentMap = typedStatistics.currentLong();
+ previousMap = typedStatistics.previousLong();
+ Load longLoad = new DefaultLoad(aggregateBytesMap(currentMap), aggregateBytesMap(previousMap),
+ TypedFlowEntryWithLoad.longPollInterval());
+
+ currentMap = typedStatistics.currentUnknown();
+ previousMap = typedStatistics.previousUnknown();
+ Load unknownLoad = new DefaultLoad(aggregateBytesMap(currentMap), aggregateBytesMap(previousMap),
+ TypedFlowEntryWithLoad.avgPollInterval());
+
+ return new SummaryFlowEntryWithLoad(cp, totalLoad, immediateLoad, shortLoad, midLoad, longLoad, unknownLoad);
+ }
+
+ private List<TypedFlowEntryWithLoad> loadAllPortInternal(ConnectPoint cp,
+ TypedStoredFlowEntry.FlowLiveType liveType,
+ Instruction.Type instType) {
+ checkPermission(STATISTIC_READ);
+
+ List<TypedFlowEntryWithLoad> retTFEL = new ArrayList<>();
+
+ Set<FlowEntry> currentStats;
+ Set<FlowEntry> previousStats;
+
+ TypedStatistics typedStatistics;
+ synchronized (flowStatisticStore) {
+ currentStats = flowStatisticStore.getCurrentFlowStatistic(cp);
+ if (currentStats == null) {
+ return retTFEL;
+ }
+ previousStats = flowStatisticStore.getPreviousFlowStatistic(cp);
+ if (previousStats == null) {
+ return retTFEL;
+ }
+ // copy to local flow entry set
+ typedStatistics = new TypedStatistics(currentStats, previousStats);
+
+ // Check for validity of this stats data
+ checkLoadValidity(currentStats, previousStats);
+ }
+
+ // current and previous set is not empty!
+ boolean isAllLiveType = (liveType == null ? true : false); // null is all live type
+ boolean isAllInstType = (instType == null ? true : false); // null is all inst type
+
+ Map<FlowRule, TypedStoredFlowEntry> currentMap;
+ Map<FlowRule, TypedStoredFlowEntry> previousMap;
+
+ if (isAllLiveType || liveType == TypedStoredFlowEntry.FlowLiveType.IMMEDIATE_FLOW) {
+ currentMap = typedStatistics.currentImmediate();
+ previousMap = typedStatistics.previousImmediate();
+
+ List<TypedFlowEntryWithLoad> fel = typedFlowEntryLoadByInstInternal(cp, currentMap, previousMap,
+ isAllInstType, instType, TypedFlowEntryWithLoad.shortPollInterval());
+ if (fel.size() > 0) {
+ retTFEL.addAll(fel);
+ }
+ }
+
+ if (isAllLiveType || liveType == TypedStoredFlowEntry.FlowLiveType.SHORT_FLOW) {
+ currentMap = typedStatistics.currentShort();
+ previousMap = typedStatistics.previousShort();
+
+ List<TypedFlowEntryWithLoad> fel = typedFlowEntryLoadByInstInternal(cp, currentMap, previousMap,
+ isAllInstType, instType, TypedFlowEntryWithLoad.shortPollInterval());
+ if (fel.size() > 0) {
+ retTFEL.addAll(fel);
+ }
+ }
+
+ if (isAllLiveType || liveType == TypedStoredFlowEntry.FlowLiveType.MID_FLOW) {
+ currentMap = typedStatistics.currentMid();
+ previousMap = typedStatistics.previousMid();
+
+ List<TypedFlowEntryWithLoad> fel = typedFlowEntryLoadByInstInternal(cp, currentMap, previousMap,
+ isAllInstType, instType, TypedFlowEntryWithLoad.midPollInterval());
+ if (fel.size() > 0) {
+ retTFEL.addAll(fel);
+ }
+ }
+
+ if (isAllLiveType || liveType == TypedStoredFlowEntry.FlowLiveType.LONG_FLOW) {
+ currentMap = typedStatistics.currentLong();
+ previousMap = typedStatistics.previousLong();
+
+ List<TypedFlowEntryWithLoad> fel = typedFlowEntryLoadByInstInternal(cp, currentMap, previousMap,
+ isAllInstType, instType, TypedFlowEntryWithLoad.longPollInterval());
+ if (fel.size() > 0) {
+ retTFEL.addAll(fel);
+ }
+ }
+
+ if (isAllLiveType || liveType == TypedStoredFlowEntry.FlowLiveType.UNKNOWN_FLOW) {
+ currentMap = typedStatistics.currentUnknown();
+ previousMap = typedStatistics.previousUnknown();
+
+ List<TypedFlowEntryWithLoad> fel = typedFlowEntryLoadByInstInternal(cp, currentMap, previousMap,
+ isAllInstType, instType, TypedFlowEntryWithLoad.avgPollInterval());
+ if (fel.size() > 0) {
+ retTFEL.addAll(fel);
+ }
+ }
+
+ return retTFEL;
+ }
+
+ private List<TypedFlowEntryWithLoad> typedFlowEntryLoadByInstInternal(ConnectPoint cp,
+ Map<FlowRule, TypedStoredFlowEntry> currentMap,
+ Map<FlowRule, TypedStoredFlowEntry> previousMap,
+ boolean isAllInstType,
+ Instruction.Type instType,
+ int liveTypePollInterval) {
+ List<TypedFlowEntryWithLoad> fel = new ArrayList<>();
+
+ for (TypedStoredFlowEntry tfe : currentMap.values()) {
+ if (isAllInstType ||
+ tfe.treatment().allInstructions().stream().
+ filter(i -> i.type() == instType).
+ findAny().isPresent()) {
+ long currentBytes = tfe.bytes();
+ long previousBytes = previousMap.getOrDefault(tfe, new DefaultTypedFlowEntry((FlowRule) tfe)).bytes();
+ Load fLoad = new DefaultLoad(currentBytes, previousBytes, liveTypePollInterval);
+ fel.add(new TypedFlowEntryWithLoad(cp, tfe, fLoad));
+ }
+ }
+
+ return fel;
+ }
+
+ private List<TypedFlowEntryWithLoad> loadTopnPortInternal(ConnectPoint cp,
+ TypedStoredFlowEntry.FlowLiveType liveType,
+ Instruction.Type instType,
+ int topn) {
+ List<TypedFlowEntryWithLoad> fel = loadAllPortInternal(cp, liveType, instType);
+
+ // Sort with descending order of load
+ List<TypedFlowEntryWithLoad> tfel =
+ fel.stream().sorted(Comparators.TYPEFLOWENTRY_WITHLOAD_COMPARATOR).
+ limit(topn).collect(Collectors.toList());
+
+ return tfel;
+ }
+
+ private long aggregateBytesSet(Set<FlowEntry> setFE) {
+ return setFE.stream().mapToLong(FlowEntry::bytes).sum();
+ }
+
+ private long aggregateBytesMap(Map<FlowRule, TypedStoredFlowEntry> mapFE) {
+ return mapFE.values().stream().mapToLong(FlowEntry::bytes).sum();
+ }
+
+ /**
+ * Internal data class holding two set of typed flow entries.
+ */
+ private static class TypedStatistics {
+ private final ImmutableSet<FlowEntry> currentAll;
+ private final ImmutableSet<FlowEntry> previousAll;
+
+ private final Map<FlowRule, TypedStoredFlowEntry> currentImmediate = new HashMap<>();
+ private final Map<FlowRule, TypedStoredFlowEntry> previousImmediate = new HashMap<>();
+
+ private final Map<FlowRule, TypedStoredFlowEntry> currentShort = new HashMap<>();
+ private final Map<FlowRule, TypedStoredFlowEntry> previousShort = new HashMap<>();
+
+ private final Map<FlowRule, TypedStoredFlowEntry> currentMid = new HashMap<>();
+ private final Map<FlowRule, TypedStoredFlowEntry> previousMid = new HashMap<>();
+
+ private final Map<FlowRule, TypedStoredFlowEntry> currentLong = new HashMap<>();
+ private final Map<FlowRule, TypedStoredFlowEntry> previousLong = new HashMap<>();
+
+ private final Map<FlowRule, TypedStoredFlowEntry> currentUnknown = new HashMap<>();
+ private final Map<FlowRule, TypedStoredFlowEntry> previousUnknown = new HashMap<>();
+
+ public TypedStatistics(Set<FlowEntry> current, Set<FlowEntry> previous) {
+ this.currentAll = ImmutableSet.copyOf(checkNotNull(current));
+ this.previousAll = ImmutableSet.copyOf(checkNotNull(previous));
+
+ currentAll.forEach(fe -> {
+ TypedStoredFlowEntry tfe = TypedFlowEntryWithLoad.newTypedStoredFlowEntry(fe);
+
+ switch (tfe.flowLiveType()) {
+ case IMMEDIATE_FLOW:
+ currentImmediate.put(fe, tfe);
+ break;
+ case SHORT_FLOW:
+ currentShort.put(fe, tfe);
+ break;
+ case MID_FLOW:
+ currentMid.put(fe, tfe);
+ break;
+ case LONG_FLOW:
+ currentLong.put(fe, tfe);
+ break;
+ default:
+ currentUnknown.put(fe, tfe);
+ break;
+ }
+ });
+
+ previousAll.forEach(fe -> {
+ TypedStoredFlowEntry tfe = TypedFlowEntryWithLoad.newTypedStoredFlowEntry(fe);
+
+ switch (tfe.flowLiveType()) {
+ case IMMEDIATE_FLOW:
+ if (currentImmediate.containsKey(fe)) {
+ previousImmediate.put(fe, tfe);
+ } else if (currentShort.containsKey(fe)) {
+ previousShort.put(fe, tfe);
+ } else if (currentMid.containsKey(fe)) {
+ previousMid.put(fe, tfe);
+ } else if (currentLong.containsKey(fe)) {
+ previousLong.put(fe, tfe);
+ } else {
+ previousUnknown.put(fe, tfe);
+ }
+ break;
+ case SHORT_FLOW:
+ if (currentShort.containsKey(fe)) {
+ previousShort.put(fe, tfe);
+ } else if (currentMid.containsKey(fe)) {
+ previousMid.put(fe, tfe);
+ } else if (currentLong.containsKey(fe)) {
+ previousLong.put(fe, tfe);
+ } else {
+ previousUnknown.put(fe, tfe);
+ }
+ break;
+ case MID_FLOW:
+ if (currentMid.containsKey(fe)) {
+ previousMid.put(fe, tfe);
+ } else if (currentLong.containsKey(fe)) {
+ previousLong.put(fe, tfe);
+ } else {
+ previousUnknown.put(fe, tfe);
+ }
+ break;
+ case LONG_FLOW:
+ if (currentLong.containsKey(fe)) {
+ previousLong.put(fe, tfe);
+ } else {
+ previousUnknown.put(fe, tfe);
+ }
+ break;
+ default:
+ previousUnknown.put(fe, tfe);
+ break;
+ }
+ });
+ }
+
+ /**
+ * Returns flow entries as the current value.
+ *
+ * @return flow entries as the current value
+ */
+ public ImmutableSet<FlowEntry> current() {
+ return currentAll;
+ }
+
+ /**
+ * Returns flow entries as the previous value.
+ *
+ * @return flow entries as the previous value
+ */
+ public ImmutableSet<FlowEntry> previous() {
+ return previousAll;
+ }
+
+ public Map<FlowRule, TypedStoredFlowEntry> currentImmediate() {
+ return currentImmediate;
+ }
+ public Map<FlowRule, TypedStoredFlowEntry> previousImmediate() {
+ return previousImmediate;
+ }
+ public Map<FlowRule, TypedStoredFlowEntry> currentShort() {
+ return currentShort;
+ }
+ public Map<FlowRule, TypedStoredFlowEntry> previousShort() {
+ return previousShort;
+ }
+ public Map<FlowRule, TypedStoredFlowEntry> currentMid() {
+ return currentMid;
+ }
+ public Map<FlowRule, TypedStoredFlowEntry> previousMid() {
+ return previousMid;
+ }
+ public Map<FlowRule, TypedStoredFlowEntry> currentLong() {
+ return currentLong;
+ }
+ public Map<FlowRule, TypedStoredFlowEntry> previousLong() {
+ return previousLong;
+ }
+ public Map<FlowRule, TypedStoredFlowEntry> currentUnknown() {
+ return currentUnknown;
+ }
+ public Map<FlowRule, TypedStoredFlowEntry> previousUnknown() {
+ return previousUnknown;
+ }
+
+ /**
+ * Validates values are not empty.
+ *
+ * @return false if either of the sets is empty. Otherwise, true.
+ */
+ public boolean isValid() {
+ return !(currentAll.isEmpty() || previousAll.isEmpty());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(currentAll, previousAll);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof TypedStatistics)) {
+ return false;
+ }
+ final TypedStatistics other = (TypedStatistics) obj;
+ return Objects.equals(this.currentAll, other.currentAll) &&
+ Objects.equals(this.previousAll, other.previousAll);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("current", currentAll)
+ .add("previous", previousAll)
+ .toString();
+ }
+ }
+
+ private void checkLoadValidity(Set<FlowEntry> current, Set<FlowEntry> previous) {
+ current.stream().forEach(c -> {
+ FlowEntry f = previous.stream().filter(p -> c.equals(p)).
+ findAny().orElse(null);
+ if (f != null && c.bytes() < f.bytes()) {
+ log.debug("FlowStatisticManager:checkLoadValidity():" +
+ "Error: " + c + " :Previous bytes=" + f.bytes() +
+ " is larger than current bytes=" + c.bytes() + " !!!");
+ }
+ });
+
+ }
+
+ /**
+ * Creates a predicate that checks the instruction type of a flow entry is the same as
+ * the specified instruction type.
+ *
+ * @param instType instruction type to be checked
+ * @return predicate
+ */
+ private static Predicate<FlowEntry> hasInstructionType(Instruction.Type instType) {
+ return new Predicate<FlowEntry>() {
+ @Override
+ public boolean apply(FlowEntry flowEntry) {
+ List<Instruction> allInstructions = flowEntry.treatment().allInstructions();
+
+ return allInstructions.stream().filter(i -> i.type() == instType).findAny().isPresent();
+ }
+ };
+ }
+
+ /**
+ * Internal flow rule event listener for FlowStatisticManager.
+ */
+ private class InternalFlowRuleStatsListener implements FlowRuleListener {
+
+ @Override
+ public void event(FlowRuleEvent event) {
+ FlowRule rule = event.subject();
+ switch (event.type()) {
+ case RULE_ADDED:
+ if (rule instanceof FlowEntry) {
+ flowStatisticStore.addFlowStatistic((FlowEntry) rule);
+ }
+ break;
+ case RULE_UPDATED:
+ flowStatisticStore.updateFlowStatistic((FlowEntry) rule);
+ break;
+ case RULE_ADD_REQUESTED:
+ break;
+ case RULE_REMOVE_REQUESTED:
+ break;
+ case RULE_REMOVED:
+ flowStatisticStore.removeFlowStatistic(rule);
+ break;
+ default:
+ log.warn("Unknown flow rule event {}", event);
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/PathManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/PathManager.java index a238c7fb..8347ee38 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/PathManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/PathManager.java @@ -27,6 +27,8 @@ import org.apache.felix.scr.annotations.Service; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DefaultEdgeLink; import org.onosproject.net.DefaultPath; +import org.onosproject.net.DisjointPath; +import org.onosproject.net.DefaultDisjointPath; import org.onosproject.net.DeviceId; import org.onosproject.net.EdgeLink; import org.onosproject.net.ElementId; @@ -46,6 +48,8 @@ import org.slf4j.Logger; import java.util.List; import java.util.Set; +import java.util.Map; + import static com.google.common.base.Preconditions.checkNotNull; import static org.slf4j.LoggerFactory.getLogger; @@ -128,6 +132,84 @@ public class PathManager implements PathService { return edgeToEdgePaths(srcEdge, dstEdge, paths); } + @Override + public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst) { + return getDisjointPaths(src, dst, (LinkWeight) null); + } + + @Override + public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst, LinkWeight weight) { + checkNotNull(src, ELEMENT_ID_NULL); + checkNotNull(dst, ELEMENT_ID_NULL); + + // Get the source and destination edge locations + EdgeLink srcEdge = getEdgeLink(src, true); + EdgeLink dstEdge = getEdgeLink(dst, false); + + // If either edge is null, bail with no paths. + if (srcEdge == null || dstEdge == null) { + return ImmutableSet.of(); + } + + DeviceId srcDevice = srcEdge != NOT_HOST ? srcEdge.dst().deviceId() : (DeviceId) src; + DeviceId dstDevice = dstEdge != NOT_HOST ? dstEdge.src().deviceId() : (DeviceId) dst; + + // If the source and destination are on the same edge device, there + // is just one path, so build it and return it. + if (srcDevice.equals(dstDevice)) { + return edgeToEdgePathsDisjoint(srcEdge, dstEdge); + } + + // Otherwise get all paths between the source and destination edge + // devices. + Topology topology = topologyService.currentTopology(); + Set<DisjointPath> paths = weight == null ? + topologyService.getDisjointPaths(topology, srcDevice, dstDevice) : + topologyService.getDisjointPaths(topology, srcDevice, dstDevice, weight); + + return edgeToEdgePathsDisjoint(srcEdge, dstEdge, paths); + } + + @Override + public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst, + Map<Link, Object> riskProfile) { + return getDisjointPaths(src, dst, null, riskProfile); + } + + @Override + public Set<DisjointPath> getDisjointPaths(ElementId src, ElementId dst, LinkWeight weight, + Map<Link, Object> riskProfile) { + checkNotNull(src, ELEMENT_ID_NULL); + checkNotNull(dst, ELEMENT_ID_NULL); + + // Get the source and destination edge locations + EdgeLink srcEdge = getEdgeLink(src, true); + EdgeLink dstEdge = getEdgeLink(dst, false); + + // If either edge is null, bail with no paths. + if (srcEdge == null || dstEdge == null) { + return ImmutableSet.of(); + } + + DeviceId srcDevice = srcEdge != NOT_HOST ? srcEdge.dst().deviceId() : (DeviceId) src; + DeviceId dstDevice = dstEdge != NOT_HOST ? dstEdge.src().deviceId() : (DeviceId) dst; + + // If the source and destination are on the same edge device, there + // is just one path, so build it and return it. + if (srcDevice.equals(dstDevice)) { + return edgeToEdgePathsDisjoint(srcEdge, dstEdge); + } + + // Otherwise get all paths between the source and destination edge + // devices. + Topology topology = topologyService.currentTopology(); + Set<DisjointPath> paths = weight == null ? + topologyService.getDisjointPaths(topology, srcDevice, dstDevice, riskProfile) : + topologyService.getDisjointPaths(topology, srcDevice, dstDevice, weight, riskProfile); + + return edgeToEdgePathsDisjoint(srcEdge, dstEdge, paths); + } + // Finds the host edge link if the element ID is a host id of an existing // host. Otherwise, if the host does not exist, it returns null and if // the element ID is not a host ID, returns NOT_HOST edge link. @@ -162,6 +244,20 @@ public class PathManager implements PathService { return endToEndPaths; } + private Set<DisjointPath> edgeToEdgePathsDisjoint(EdgeLink srcLink, EdgeLink dstLink) { + Set<DisjointPath> endToEndPaths = Sets.newHashSetWithExpectedSize(1); + endToEndPaths.add(edgeToEdgePathD(srcLink, dstLink, null)); + return endToEndPaths; + } + + private Set<DisjointPath> edgeToEdgePathsDisjoint(EdgeLink srcLink, EdgeLink dstLink, Set<DisjointPath> paths) { + Set<DisjointPath> endToEndPaths = Sets.newHashSetWithExpectedSize(paths.size()); + for (DisjointPath path : paths) { + endToEndPaths.add(edgeToEdgePathD(srcLink, dstLink, path)); + } + return endToEndPaths; + } + // Produces a direct edge-to-edge path. private Path edgeToEdgePath(EdgeLink srcLink, EdgeLink dstLink, Path path) { List<Link> links = Lists.newArrayListWithCapacity(2); @@ -179,6 +275,13 @@ public class PathManager implements PathService { return new DefaultPath(PID, links, 2); } + // Produces a direct edge-to-edge path. + private DisjointPath edgeToEdgePathD(EdgeLink srcLink, EdgeLink dstLink, DisjointPath path) { + return new DefaultDisjointPath(PID, (DefaultPath) edgeToEdgePath(srcLink, dstLink, path.primary()), + (DefaultPath) edgeToEdgePath(srcLink, dstLink, path.backup())); + } + + // Special value for edge link to represent that this is really not an // edge link since the src or dst are really an infrastructure device. private static class NotHost extends DefaultEdgeLink implements EdgeLink { diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/TopologyManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/TopologyManager.java index 04c4f1c1..4425e1c1 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/TopologyManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/TopologyManager.java @@ -21,6 +21,7 @@ import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.Service; +import org.onosproject.net.DisjointPath; import org.onosproject.net.provider.AbstractListenerProviderRegistry; import org.onosproject.event.Event; import org.onosproject.net.ConnectPoint; @@ -46,6 +47,7 @@ import org.slf4j.Logger; import java.util.List; import java.util.Set; +import java.util.Map; import static com.google.common.base.Preconditions.checkNotNull; import static org.onosproject.security.AppGuard.checkPermission; @@ -60,7 +62,7 @@ import static org.onosproject.security.AppPermission.Type.*; @Service public class TopologyManager extends AbstractListenerProviderRegistry<TopologyEvent, TopologyListener, - TopologyProvider, TopologyProviderService> + TopologyProvider, TopologyProviderService> implements TopologyService, TopologyProviderRegistry { public static final String TOPOLOGY_NULL = "Topology cannot be null"; @@ -68,6 +70,7 @@ public class TopologyManager private static final String CLUSTER_ID_NULL = "Cluster ID cannot be null"; private static final String CLUSTER_NULL = "Topology cluster cannot be null"; public static final String CONNECTION_POINT_NULL = "Connection point cannot be null"; + public static final String LINK_WEIGHT_NULL = "Link weight cannot be null"; private final Logger log = getLogger(getClass()); @@ -162,6 +165,44 @@ public class TopologyManager } @Override + public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst) { + checkNotNull(topology, TOPOLOGY_NULL); + checkNotNull(src, DEVICE_ID_NULL); + checkNotNull(dst, DEVICE_ID_NULL); + return store.getDisjointPaths(topology, src, dst); + } + + @Override + public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, + DeviceId dst, LinkWeight weight) { + checkNotNull(topology, TOPOLOGY_NULL); + checkNotNull(src, DEVICE_ID_NULL); + checkNotNull(dst, DEVICE_ID_NULL); + checkNotNull(weight, LINK_WEIGHT_NULL); + return store.getDisjointPaths(topology, src, dst, weight); + } + + @Override + public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst, + Map<Link, Object> riskProfile) { + checkNotNull(topology, TOPOLOGY_NULL); + checkNotNull(src, DEVICE_ID_NULL); + checkNotNull(dst, DEVICE_ID_NULL); + return store.getDisjointPaths(topology, src, dst, riskProfile); + } + + @Override + public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, + DeviceId dst, LinkWeight weight, + Map<Link, Object> riskProfile) { + checkNotNull(topology, TOPOLOGY_NULL); + checkNotNull(src, DEVICE_ID_NULL); + checkNotNull(dst, DEVICE_ID_NULL); + checkNotNull(weight, LINK_WEIGHT_NULL); + return store.getDisjointPaths(topology, src, dst, weight, riskProfile); + } + + @Override public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) { checkPermission(TOPOLOGY_READ); checkNotNull(topology, TOPOLOGY_NULL); diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/cfg/impl/ComponentConfigLoaderTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/cfg/impl/ComponentConfigLoaderTest.java new file mode 100644 index 00000000..0320cf77 --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/cfg/impl/ComponentConfigLoaderTest.java @@ -0,0 +1,126 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.cfg.impl; + +import com.google.common.collect.ImmutableSet; +import com.google.common.io.Files; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.cfg.ComponentConfigAdapter; +import org.slf4j.Logger; + +import java.io.File; +import java.io.IOException; +import java.util.Set; + +import static com.google.common.io.ByteStreams.toByteArray; +import static com.google.common.io.Files.write; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * UnitTest for ComponentLoader. + */ +public class ComponentConfigLoaderTest { + + static final File TEST_DIR = Files.createTempDir(); + + private static final String FOO_COMPONENT = "fooComponent"; + + private ComponentConfigLoader loader; + + private TestConfigService service; + + private final Logger log = getLogger(getClass()); + + /* + * Method to SetUp the test environment with test file, a config loader a service, + * and assign it to the loader.configService for the test. + */ + @Before + public void setUp() { + ComponentConfigLoader.cfgFile = new File(TEST_DIR, "test.json"); + loader = new ComponentConfigLoader(); + service = new TestConfigService(); + loader.configService = service; + } + + /* + * Tests that the component in the json receives the correct configuration. + */ + @Test + public void basics() throws IOException { + stageTestResource("basic.json"); + loader.activate(); + assertEquals("incorrect component", FOO_COMPONENT, service.component); + } + + /* + * Tests that the component is null if the file has a bad configuration format + * for which it yielded an exception. Can't test the exception because it happens + * in a different thread. + */ + @Test + public void badConfig() throws IOException { + stageTestResource("badConfig.json"); + loader.activate(); + assertNull("incorrect configuration", service.component); + + } + + /* + * Writes the necessary file for the tests in the temporary directory + */ + static void stageTestResource(String name) throws IOException { + byte[] bytes = toByteArray(ComponentConfigLoaderTest.class.getResourceAsStream(name)); + write(bytes, ComponentConfigLoader.cfgFile); + } + + /* + * Mockup class for the config service. + */ + private class TestConfigService extends ComponentConfigAdapter { + + protected String component; + protected String name; + protected String value; + + @Override + public Set<String> getComponentNames() { + return ImmutableSet.of(FOO_COMPONENT); + } + + @Override + public void preSetProperty(String componentName, String name, String value) { + log.info("preSet"); + this.component = componentName; + this.name = name; + this.value = value; + + } + + @Override + public void setProperty(String componentName, String name, String value) { + log.info("Set"); + this.component = componentName; + this.name = name; + this.value = value; + + } + } +}
\ No newline at end of file diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java index d167197a..3cd2ca2b 100644 --- a/framework/src/onos/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/host/impl/HostMonitorTest.java @@ -23,10 +23,12 @@ import org.junit.Before; import org.junit.Test; import org.onlab.packet.ARP; import org.onlab.packet.Ethernet; +import org.onlab.packet.IPv6; import org.onlab.packet.IpAddress; import org.onlab.packet.IpPrefix; import org.onlab.packet.MacAddress; import org.onlab.packet.VlanId; +import org.onlab.packet.ndp.NeighborSolicitation; import org.onosproject.incubator.net.intf.Interface; import org.onosproject.incubator.net.intf.InterfaceService; import org.onosproject.net.ConnectPoint; @@ -64,14 +66,22 @@ import static org.junit.Assert.assertTrue; public class HostMonitorTest { - private static final IpAddress TARGET_IP_ADDR = + private static final IpAddress TARGET_IPV4_ADDR = IpAddress.valueOf("10.0.0.1"); - private static final IpAddress SOURCE_ADDR = + private static final IpAddress SOURCE_IPV4_ADDR = IpAddress.valueOf("10.0.0.99"); private static final InterfaceIpAddress IA1 = - new InterfaceIpAddress(SOURCE_ADDR, IpPrefix.valueOf("10.0.0.0/24")); + new InterfaceIpAddress(SOURCE_IPV4_ADDR, IpPrefix.valueOf("10.0.0.0/24")); private MacAddress sourceMac = MacAddress.valueOf(1L); + private static final IpAddress TARGET_IPV6_ADDR = + IpAddress.valueOf("1000::1"); + private static final IpAddress SOURCE_IPV6_ADDR = + IpAddress.valueOf("1000::f"); + private static final InterfaceIpAddress IA2 = + new InterfaceIpAddress(SOURCE_IPV6_ADDR, IpPrefix.valueOf("1000::/64")); + private MacAddress sourceMac2 = MacAddress.valueOf(2L); + private EdgePortService edgePortService; private HostMonitor hostMonitor; @@ -90,7 +100,36 @@ public class HostMonitorTest { } @Test - public void testMonitorHostExists() throws Exception { + public void testMonitorIpv4HostExists() throws Exception { + ProviderId id = new ProviderId("fake://", "id"); + + Host host = createMock(Host.class); + expect(host.providerId()).andReturn(id); + replay(host); + + HostManager hostManager = createMock(HostManager.class); + expect(hostManager.getHostsByIp(TARGET_IPV4_ADDR)) + .andReturn(Collections.singleton(host)); + replay(hostManager); + + HostProvider hostProvider = createMock(HostProvider.class); + expect(hostProvider.id()).andReturn(id).anyTimes(); + hostProvider.triggerProbe(host); + expectLastCall().once(); + replay(hostProvider); + + hostMonitor = new HostMonitor(null, hostManager, null, edgePortService); + + hostMonitor.registerHostProvider(hostProvider); + hostMonitor.addMonitoringFor(TARGET_IPV4_ADDR); + + hostMonitor.run(null); + + verify(hostProvider); + } + + @Test + public void testMonitorIpv6HostExists() throws Exception { ProviderId id = new ProviderId("fake://", "id"); Host host = createMock(Host.class); @@ -98,7 +137,7 @@ public class HostMonitorTest { replay(host); HostManager hostManager = createMock(HostManager.class); - expect(hostManager.getHostsByIp(TARGET_IP_ADDR)) + expect(hostManager.getHostsByIp(TARGET_IPV6_ADDR)) .andReturn(Collections.singleton(host)); replay(hostManager); @@ -111,7 +150,7 @@ public class HostMonitorTest { hostMonitor = new HostMonitor(null, hostManager, null, edgePortService); hostMonitor.registerHostProvider(hostProvider); - hostMonitor.addMonitoringFor(TARGET_IP_ADDR); + hostMonitor.addMonitoringFor(TARGET_IPV6_ADDR); hostMonitor.run(null); @@ -119,7 +158,7 @@ public class HostMonitorTest { } @Test - public void testMonitorHostDoesNotExist() throws Exception { + public void testMonitorIpv4HostDoesNotExist() throws Exception { HostManager hostManager = createMock(HostManager.class); @@ -140,12 +179,12 @@ public class HostMonitorTest { ConnectPoint cp = new ConnectPoint(devId, portNum); - expect(hostManager.getHostsByIp(TARGET_IP_ADDR)) + expect(hostManager.getHostsByIp(TARGET_IPV4_ADDR)) .andReturn(Collections.emptySet()).anyTimes(); replay(hostManager); InterfaceService interfaceService = createMock(InterfaceService.class); - expect(interfaceService.getMatchingInterface(TARGET_IP_ADDR)) + expect(interfaceService.getMatchingInterface(TARGET_IPV4_ADDR)) .andReturn(new Interface(cp, Collections.singleton(IA1), sourceMac, VlanId.NONE)) .anyTimes(); replay(interfaceService); @@ -156,7 +195,7 @@ public class HostMonitorTest { // Run the test hostMonitor = new HostMonitor(packetService, hostManager, interfaceService, edgePortService); - hostMonitor.addMonitoringFor(TARGET_IP_ADDR); + hostMonitor.addMonitoringFor(TARGET_IPV4_ADDR); hostMonitor.run(null); @@ -178,16 +217,85 @@ public class HostMonitorTest { Ethernet eth = Ethernet.deserializer().deserialize(pktData, 0, pktData.length); assertEquals(Ethernet.VLAN_UNTAGGED, eth.getVlanID()); ARP arp = (ARP) eth.getPayload(); - assertArrayEquals(SOURCE_ADDR.toOctets(), + assertArrayEquals(SOURCE_IPV4_ADDR.toOctets(), arp.getSenderProtocolAddress()); assertArrayEquals(sourceMac.toBytes(), arp.getSenderHardwareAddress()); - assertArrayEquals(TARGET_IP_ADDR.toOctets(), + assertArrayEquals(TARGET_IPV4_ADDR.toOctets(), arp.getTargetProtocolAddress()); } @Test - public void testMonitorHostDoesNotExistWithVlan() throws Exception { + public void testMonitorIpv6HostDoesNotExist() throws Exception { + + HostManager hostManager = createMock(HostManager.class); + + DeviceId devId = DeviceId.deviceId("fake"); + + Device device = createMock(Device.class); + expect(device.id()).andReturn(devId).anyTimes(); + replay(device); + + PortNumber portNum = PortNumber.portNumber(2L); + + Port port = createMock(Port.class); + expect(port.number()).andReturn(portNum).anyTimes(); + replay(port); + + TestDeviceService deviceService = new TestDeviceService(); + deviceService.addDevice(device, Collections.singleton(port)); + + ConnectPoint cp = new ConnectPoint(devId, portNum); + + expect(hostManager.getHostsByIp(TARGET_IPV6_ADDR)) + .andReturn(Collections.emptySet()).anyTimes(); + replay(hostManager); + + InterfaceService interfaceService = createMock(InterfaceService.class); + expect(interfaceService.getMatchingInterface(TARGET_IPV6_ADDR)) + .andReturn(new Interface(cp, Collections.singleton(IA2), sourceMac2, VlanId.NONE)) + .anyTimes(); + replay(interfaceService); + + TestPacketService packetService = new TestPacketService(); + + + // Run the test + hostMonitor = new HostMonitor(packetService, hostManager, interfaceService, edgePortService); + + hostMonitor.addMonitoringFor(TARGET_IPV6_ADDR); + hostMonitor.run(null); + + + // Check that a packet was sent to our PacketService and that it has + // the properties we expect + assertEquals(1, packetService.packets.size()); + OutboundPacket packet = packetService.packets.get(0); + + // Check the output port is correct + assertEquals(1, packet.treatment().immediate().size()); + Instruction instruction = packet.treatment().immediate().get(0); + assertTrue(instruction instanceof OutputInstruction); + OutputInstruction oi = (OutputInstruction) instruction; + assertEquals(portNum, oi.port()); + + // Check the output packet is correct (well the important bits anyway) + final byte[] pktData = new byte[packet.data().remaining()]; + packet.data().get(pktData); + Ethernet eth = Ethernet.deserializer().deserialize(pktData, 0, pktData.length); + assertEquals(Ethernet.VLAN_UNTAGGED, eth.getVlanID()); + IPv6 ipv6 = (IPv6) eth.getPayload(); + assertArrayEquals(SOURCE_IPV6_ADDR.toOctets(), ipv6.getSourceAddress()); + + NeighborSolicitation ns = + (NeighborSolicitation) ipv6.getPayload().getPayload(); + assertArrayEquals(sourceMac2.toBytes(), ns.getOptions().get(0).data()); + + assertArrayEquals(TARGET_IPV6_ADDR.toOctets(), ns.getTargetAddress()); + } + + @Test + public void testMonitorIpv4HostDoesNotExistWithVlan() throws Exception { HostManager hostManager = createMock(HostManager.class); @@ -209,12 +317,12 @@ public class HostMonitorTest { ConnectPoint cp = new ConnectPoint(devId, portNum); - expect(hostManager.getHostsByIp(TARGET_IP_ADDR)) + expect(hostManager.getHostsByIp(TARGET_IPV4_ADDR)) .andReturn(Collections.emptySet()).anyTimes(); replay(hostManager); InterfaceService interfaceService = createMock(InterfaceService.class); - expect(interfaceService.getMatchingInterface(TARGET_IP_ADDR)) + expect(interfaceService.getMatchingInterface(TARGET_IPV4_ADDR)) .andReturn(new Interface(cp, Collections.singleton(IA1), sourceMac, VlanId.vlanId(vlan))) .anyTimes(); replay(interfaceService); @@ -225,7 +333,7 @@ public class HostMonitorTest { // Run the test hostMonitor = new HostMonitor(packetService, hostManager, interfaceService, edgePortService); - hostMonitor.addMonitoringFor(TARGET_IP_ADDR); + hostMonitor.addMonitoringFor(TARGET_IPV4_ADDR); hostMonitor.run(null); @@ -247,14 +355,84 @@ public class HostMonitorTest { Ethernet eth = Ethernet.deserializer().deserialize(pktData, 0, pktData.length); assertEquals(vlan, eth.getVlanID()); ARP arp = (ARP) eth.getPayload(); - assertArrayEquals(SOURCE_ADDR.toOctets(), + assertArrayEquals(SOURCE_IPV4_ADDR.toOctets(), arp.getSenderProtocolAddress()); assertArrayEquals(sourceMac.toBytes(), arp.getSenderHardwareAddress()); - assertArrayEquals(TARGET_IP_ADDR.toOctets(), + assertArrayEquals(TARGET_IPV4_ADDR.toOctets(), arp.getTargetProtocolAddress()); } + @Test + public void testMonitorIpv6HostDoesNotExistWithVlan() throws Exception { + + HostManager hostManager = createMock(HostManager.class); + + DeviceId devId = DeviceId.deviceId("fake"); + short vlan = 5; + + Device device = createMock(Device.class); + expect(device.id()).andReturn(devId).anyTimes(); + replay(device); + + PortNumber portNum = PortNumber.portNumber(1L); + + Port port = createMock(Port.class); + expect(port.number()).andReturn(portNum).anyTimes(); + replay(port); + + TestDeviceService deviceService = new TestDeviceService(); + deviceService.addDevice(device, Collections.singleton(port)); + + ConnectPoint cp = new ConnectPoint(devId, portNum); + + expect(hostManager.getHostsByIp(TARGET_IPV6_ADDR)) + .andReturn(Collections.emptySet()).anyTimes(); + replay(hostManager); + + InterfaceService interfaceService = createMock(InterfaceService.class); + expect(interfaceService.getMatchingInterface(TARGET_IPV6_ADDR)) + .andReturn(new Interface(cp, Collections.singleton(IA2), sourceMac2, VlanId.vlanId(vlan))) + .anyTimes(); + replay(interfaceService); + + TestPacketService packetService = new TestPacketService(); + + + // Run the test + hostMonitor = new HostMonitor(packetService, hostManager, interfaceService, edgePortService); + + hostMonitor.addMonitoringFor(TARGET_IPV6_ADDR); + hostMonitor.run(null); + + + // Check that a packet was sent to our PacketService and that it has + // the properties we expect + assertEquals(1, packetService.packets.size()); + OutboundPacket packet = packetService.packets.get(0); + + // Check the output port is correct + assertEquals(1, packet.treatment().immediate().size()); + Instruction instruction = packet.treatment().immediate().get(0); + assertTrue(instruction instanceof OutputInstruction); + OutputInstruction oi = (OutputInstruction) instruction; + assertEquals(portNum, oi.port()); + + // Check the output packet is correct (well the important bits anyway) + final byte[] pktData = new byte[packet.data().remaining()]; + packet.data().get(pktData); + Ethernet eth = Ethernet.deserializer().deserialize(pktData, 0, pktData.length); + assertEquals(vlan, eth.getVlanID()); + IPv6 ipv6 = (IPv6) eth.getPayload(); + assertArrayEquals(SOURCE_IPV6_ADDR.toOctets(), ipv6.getSourceAddress()); + + NeighborSolicitation ns = + (NeighborSolicitation) ipv6.getPayload().getPayload(); + assertArrayEquals(sourceMac2.toBytes(), ns.getOptions().get(0).data()); + + assertArrayEquals(TARGET_IPV6_ADDR.toOctets(), ns.getTargetAddress()); + } + class TestPacketService extends PacketServiceAdapter { List<OutboundPacket> packets = new ArrayList<>(); diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java index 4bf32f43..3f40de09 100644 --- a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/IntentManagerTest.java @@ -157,7 +157,7 @@ public class IntentManagerTest { private static class MockInstallableIntent extends FlowRuleIntent { public MockInstallableIntent() { - super(APPID, Collections.singletonList(new MockFlowRule(100))); + super(APPID, Collections.singletonList(new MockFlowRule(100)), Collections.emptyList()); } } diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompilerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompilerTest.java index eb7a3936..03d664d3 100644 --- a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompilerTest.java +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompilerTest.java @@ -31,8 +31,7 @@ import org.onosproject.net.intent.Intent; import org.onosproject.net.intent.IntentTestsMocks; import org.onosproject.net.intent.LinkCollectionIntent; import org.onosproject.net.intent.MultiPointToSinglePointIntent; -import org.onosproject.net.topology.LinkWeight; -import org.onosproject.net.topology.PathService; +import org.onosproject.net.topology.PathServiceAdapter; import java.util.HashSet; import java.util.List; @@ -60,7 +59,7 @@ public class MultiPointToSinglePointIntentCompilerTest extends AbstractIntentTes /** * Mock path service for creating paths within the test. */ - private static class MockPathService implements PathService { + private static class MockPathService extends PathServiceAdapter { final String[] pathHops; @@ -86,11 +85,6 @@ public class MultiPointToSinglePointIntentCompilerTest extends AbstractIntentTes return result; } - - @Override - public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeight weight) { - return null; - } } /** diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompilerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompilerTest.java index 2f40b37a..38a116dd 100644 --- a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompilerTest.java +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompilerTest.java @@ -27,18 +27,12 @@ import org.onosproject.net.DefaultLink; import org.onosproject.net.DefaultPath; import org.onosproject.net.Link; import org.onosproject.net.OchSignalType; -import org.onosproject.net.flow.DefaultTrafficSelector; -import org.onosproject.net.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.FlowRule; -import org.onosproject.net.flow.TrafficSelector; -import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.intent.FlowRuleIntent; import org.onosproject.net.intent.Intent; import org.onosproject.net.intent.IntentExtensionService; -import org.onosproject.net.intent.IntentTestsMocks; import org.onosproject.net.intent.MockIdGenerator; import org.onosproject.net.intent.OpticalPathIntent; -import org.onosproject.net.provider.ProviderId; import java.util.Arrays; import java.util.Collection; @@ -63,16 +57,11 @@ public class OpticalPathIntentCompilerTest { private final IdGenerator idGenerator = new MockIdGenerator(); private OpticalPathIntentCompiler sut; - private final TrafficSelector selector = DefaultTrafficSelector.builder().build(); - private final TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); private final ApplicationId appId = new TestApplicationId("test"); - private final ProviderId pid = new ProviderId("of", "test"); private final ConnectPoint d1p1 = connectPoint("s1", 0); private final ConnectPoint d2p0 = connectPoint("s2", 0); private final ConnectPoint d2p1 = connectPoint("s2", 1); private final ConnectPoint d3p1 = connectPoint("s3", 1); - private final ConnectPoint d3p0 = connectPoint("s3", 10); - private final ConnectPoint d1p0 = connectPoint("s1", 10); private final List<Link> links = Arrays.asList( new DefaultLink(PID, d1p1, d2p0, DIRECT), @@ -103,7 +92,6 @@ public class OpticalPathIntentCompilerTest { intentExtensionService.registerCompiler(OpticalPathIntent.class, sut); intentExtensionService.unregisterCompiler(OpticalPathIntent.class); sut.intentManager = intentExtensionService; - sut.resourceService = new IntentTestsMocks.MockResourceService(); replay(coreService, intentExtensionService); } diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/PathManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/PathManagerTest.java index 2a2d0b54..1911da56 100644 --- a/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/PathManagerTest.java +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/PathManagerTest.java @@ -23,13 +23,11 @@ import org.onosproject.net.ElementId; import org.onosproject.net.Host; import org.onosproject.net.HostId; import org.onosproject.net.Path; -import org.onosproject.net.host.HostService; import org.onosproject.net.host.HostServiceAdapter; import org.onosproject.net.provider.ProviderId; import org.onosproject.net.topology.LinkWeight; import org.onosproject.net.topology.PathService; import org.onosproject.net.topology.Topology; -import org.onosproject.net.topology.TopologyService; import org.onosproject.net.topology.TopologyServiceAdapter; import java.util.HashMap; @@ -137,7 +135,7 @@ public class PathManagerTest { } // Fake entity to give out paths. - private class FakeTopoMgr extends TopologyServiceAdapter implements TopologyService { + private class FakeTopoMgr extends TopologyServiceAdapter { Set<Path> paths = new HashSet<>(); @Override @@ -152,7 +150,7 @@ public class PathManagerTest { } // Fake entity to give out hosts. - private class FakeHostMgr extends HostServiceAdapter implements HostService { + private class FakeHostMgr extends HostServiceAdapter { private Map<HostId, Host> hosts = new HashMap<>(); @Override diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/TopologyManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/TopologyManagerTest.java index f3cd28df..56133a0f 100644 --- a/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/TopologyManagerTest.java +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/topology/impl/TopologyManagerTest.java @@ -114,7 +114,7 @@ public class TopologyManagerTest { link("c", 2, "d", 1), link("d", 1, "c", 2), link("d", 2, "a", 2), link("a", 2, "d", 2), link("e", 1, "f", 1), link("f", 1, "e", 1)); - GraphDescription data = new DefaultGraphDescription(4321L, devices, links); + GraphDescription data = new DefaultGraphDescription(4321L, System.currentTimeMillis(), devices, links); providerService.topologyChanged(data, null); } diff --git a/framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/badComponent.json b/framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/badComponent.json new file mode 100644 index 00000000..5c0ac35d --- /dev/null +++ b/framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/badComponent.json @@ -0,0 +1,5 @@ +{ + "org.onosproject.proxyarp.ProxyArp2": { + "testProperty": true + } +}
\ No newline at end of file diff --git a/framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/badConfig.json b/framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/badConfig.json new file mode 100644 index 00000000..a76552e5 --- /dev/null +++ b/framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/badConfig.json @@ -0,0 +1,5 @@ +{ + "fooComponent": { + badconfig + } +}
\ No newline at end of file diff --git a/framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/basic.json b/framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/basic.json new file mode 100644 index 00000000..dd329243 --- /dev/null +++ b/framework/src/onos/core/net/src/test/resources/org/onosproject/cfg/impl/basic.json @@ -0,0 +1,5 @@ +{ + "fooComponent": { + "testProperty": true + } +}
\ No newline at end of file diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/Database.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/Database.java index ff3e36ac..52a999a4 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/Database.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/Database.java @@ -38,7 +38,7 @@ public interface Database extends DatabaseProxy<String, byte[]>, Resource<Databa * options specified in {@code cluster.conf} will override those in {cluster-defaults.conf}.<p> * * Additionally, the database will be constructed with an database configuration that searches the classpath for - * three configuration files - {@code {name}}, {@code database}, {@code database-defaults}, {@code resource}, and + * three configuration files - {@code name}, {@code database}, {@code database-defaults}, {@code resource}, and * {@code resource-defaults} - in that order. The first resource is a configuration resource with the same name * as the map resource. If the resource is namespaced - e.g. `databases.my-database.conf` - then resource * configurations will be loaded according to namespaces as well; for example, `databases.conf`. @@ -54,7 +54,7 @@ public interface Database extends DatabaseProxy<String, byte[]>, Resource<Databa * Creates a new database.<p> * * The database will be constructed with an database configuration that searches the classpath for - * three configuration files - {@code {name}}, {@code database}, {@code database-defaults}, {@code resource}, and + * three configuration files - {@code name}, {@code database}, {@code database-defaults}, {@code resource}, and * {@code resource-defaults} - in that order. The first resource is a configuration resource with the same name * as the database resource. If the resource is namespaced - e.g. `databases.my-database.conf` - then resource * configurations will be loaded according to namespaces as well; for example, `databases.conf`. diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseManager.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseManager.java index fbc2c88d..6ea7c220 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseManager.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseManager.java @@ -443,7 +443,10 @@ public class DatabaseManager implements StorageService, StorageAdminService { public void event(ApplicationEvent event) { if (event.type() == APP_UNINSTALLED || event.type() == APP_DEACTIVATED) { ApplicationId appId = event.subject().id(); - List<DefaultAsyncConsistentMap> mapsToRemove = ImmutableList.copyOf(mapsByApplication.get(appId)); + List<DefaultAsyncConsistentMap> mapsToRemove; + synchronized (mapsByApplication) { + mapsToRemove = ImmutableList.copyOf(mapsByApplication.get(appId)); + } mapsToRemove.forEach(DatabaseManager.this::unregisterMap); if (event.type() == APP_UNINSTALLED) { mapsToRemove.stream().filter(map -> map.purgeOnUninstall()).forEach(map -> map.clear()); diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseProxy.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseProxy.java index 95f9e39a..1d81f998 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseProxy.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseProxy.java @@ -16,14 +16,14 @@ package org.onosproject.store.consistent.impl; +import org.onosproject.store.service.Transaction; +import org.onosproject.store.service.Versioned; + import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; -import org.onosproject.store.service.Transaction; -import org.onosproject.store.service.Versioned; - /** * Database proxy. */ @@ -45,6 +45,7 @@ public interface DatabaseProxy<K, V> { /** * Returns the number of entries in map. + * * @param mapName map name * @return A completable future to be completed with the result once complete. */ @@ -62,7 +63,7 @@ public interface DatabaseProxy<K, V> { * Checks whether the map contains a key. * * @param mapName map name - * @param key key to check. + * @param key key to check. * @return A completable future to be completed with the result once complete. */ CompletableFuture<Boolean> mapContainsKey(String mapName, K key); @@ -71,7 +72,7 @@ public interface DatabaseProxy<K, V> { * Checks whether the map contains a value. * * @param mapName map name - * @param value The value to check. + * @param value The value to check. * @return A completable future to be completed with the result once complete. */ CompletableFuture<Boolean> mapContainsValue(String mapName, V value); @@ -80,7 +81,7 @@ public interface DatabaseProxy<K, V> { * Gets a value from the map. * * @param mapName map name - * @param key The key to get. + * @param key The key to get. * @return A completable future to be completed with the result once complete. */ CompletableFuture<Versioned<V>> mapGet(String mapName, K key); @@ -88,11 +89,11 @@ public interface DatabaseProxy<K, V> { /** * Updates the map. * - * @param mapName map name - * @param key The key to set - * @param valueMatch match for checking existing value - * @param versionMatch match for checking existing version - * @param value new value + * @param mapName map name + * @param key The key to set + * @param valueMatch match for checking existing value + * @param versionMatch match for checking existing version + * @param value new value * @return A completable future to be completed with the result once complete */ CompletableFuture<Result<UpdateResult<K, V>>> mapUpdate( @@ -130,11 +131,11 @@ public interface DatabaseProxy<K, V> { */ CompletableFuture<Set<Map.Entry<K, Versioned<V>>>> mapEntrySet(String mapName); - /** + /** * Atomically add the given value to current value of the specified counter. * * @param counterName counter name - * @param delta value to add + * @param delta value to add * @return updated value */ CompletableFuture<Long> counterAddAndGet(String counterName, long delta); @@ -143,11 +144,31 @@ public interface DatabaseProxy<K, V> { * Atomically add the given value to current value of the specified counter. * * @param counterName counter name - * @param delta value to add + * @param delta value to add * @return previous value */ CompletableFuture<Long> counterGetAndAdd(String counterName, long delta); + + /** + * Atomically sets the given value to current value of the specified counter. + * + * @param counterName counter name + * @param value value to set + * @return void future + */ + CompletableFuture<Void> counterSet(String counterName, long value); + + /** + * Atomically sets the given counter to the specified update value if and only if the current value is equal to the + * expected value. + * @param counterName counter name + * @param expectedValue value to use for equivalence check + * @param update value to set if expected value is current value + * @return true if an update occurred, false otherwise + */ + CompletableFuture<Boolean> counterCompareAndSet(String counterName, long expectedValue, long update); + /** * Returns the current value of the specified atomic counter. * @@ -158,6 +179,7 @@ public interface DatabaseProxy<K, V> { /** * Returns the size of queue. + * * @param queueName queue name * @return queue size */ @@ -165,14 +187,16 @@ public interface DatabaseProxy<K, V> { /** * Inserts an entry into the queue. + * * @param queueName queue name - * @param entry queue entry + * @param entry queue entry * @return void future */ CompletableFuture<Void> queuePush(String queueName, byte[] entry); /** * Removes an entry from the queue if the queue is non-empty. + * * @param queueName queue name * @return entry future. Can be completed with null if queue is empty */ @@ -180,6 +204,7 @@ public interface DatabaseProxy<K, V> { /** * Returns but does not remove an entry from the queue. + * * @param queueName queue name * @return entry. Can be null if queue is empty */ diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseState.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseState.java index b3dd1c44..1136428b 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseState.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseState.java @@ -16,18 +16,17 @@ package org.onosproject.store.consistent.impl; -import java.util.Collection; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.onosproject.store.service.Transaction; -import org.onosproject.store.service.Versioned; - import net.kuujo.copycat.state.Command; import net.kuujo.copycat.state.Initializer; import net.kuujo.copycat.state.Query; import net.kuujo.copycat.state.StateContext; +import org.onosproject.store.service.Transaction; +import org.onosproject.store.service.Versioned; + +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; /** * Database state. @@ -83,6 +82,9 @@ public interface DatabaseState<K, V> { Long counterAddAndGet(String counterName, long delta); @Command + Boolean counterCompareAndSet(String counterName, long expectedValue, long updateValue); + + @Command Long counterGetAndAdd(String counterName, long delta); @Query diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAsyncAtomicCounter.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAsyncAtomicCounter.java index 7a439c34..d851eaa0 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAsyncAtomicCounter.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAsyncAtomicCounter.java @@ -18,6 +18,7 @@ package org.onosproject.store.consistent.impl; import org.onosproject.store.service.AsyncAtomicCounter; import java.util.concurrent.CompletableFuture; + import static com.google.common.base.Preconditions.checkNotNull; /** @@ -38,6 +39,8 @@ public class DefaultAsyncAtomicCounter implements AsyncAtomicCounter { private static final String GET_AND_ADD = "getAndAdd"; private static final String ADD_AND_GET = "addAndGet"; private static final String GET = "get"; + private static final String SET = "set"; + private static final String COMPARE_AND_SET = "compareAndSet"; public DefaultAsyncAtomicCounter(String name, Database database, @@ -72,13 +75,27 @@ public class DefaultAsyncAtomicCounter implements AsyncAtomicCounter { public CompletableFuture<Long> getAndAdd(long delta) { final MeteringAgent.Context timer = monitor.startTimer(GET_AND_ADD); return database.counterGetAndAdd(name, delta) - .whenComplete((r, e) -> timer.stop(e)); + .whenComplete((r, e) -> timer.stop(e)); } @Override public CompletableFuture<Long> addAndGet(long delta) { final MeteringAgent.Context timer = monitor.startTimer(ADD_AND_GET); return database.counterAddAndGet(name, delta) - .whenComplete((r, e) -> timer.stop(e)); + .whenComplete((r, e) -> timer.stop(e)); + } + + @Override + public CompletableFuture<Void> set(long value) { + final MeteringAgent.Context timer = monitor.startTimer(SET); + return database.counterSet(name, value) + .whenComplete((r, e) -> timer.stop(e)); + } + + @Override + public CompletableFuture<Boolean> compareAndSet(long expectedValue, long updateValue) { + final MeteringAgent.Context timer = monitor.startTimer(COMPARE_AND_SET); + return database.counterCompareAndSet(name, expectedValue, updateValue) + .whenComplete((r, e) -> timer.stop(e)); } } diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAtomicCounter.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAtomicCounter.java index 64886e41..2d6a956c 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAtomicCounter.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultAtomicCounter.java @@ -63,6 +63,16 @@ public class DefaultAtomicCounter implements AtomicCounter { } @Override + public void set(long value) { + complete(asyncCounter.set(value)); + } + + @Override + public boolean compareAndSet(long expectedValue, long updateValue) { + return complete(asyncCounter.compareAndSet(expectedValue, updateValue)); + } + + @Override public long get() { return complete(asyncCounter.get()); } diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabase.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabase.java index 4d9776ee..2a50fbd6 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabase.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabase.java @@ -16,12 +16,15 @@ package org.onosproject.store.consistent.impl; -import net.kuujo.copycat.state.StateMachine; +import com.google.common.collect.Sets; import net.kuujo.copycat.resource.internal.AbstractResource; import net.kuujo.copycat.resource.internal.ResourceManager; +import net.kuujo.copycat.state.StateMachine; import net.kuujo.copycat.state.internal.DefaultStateMachine; import net.kuujo.copycat.util.concurrent.Futures; import net.kuujo.copycat.util.function.TriConsumer; +import org.onosproject.store.service.Transaction; +import org.onosproject.store.service.Versioned; import java.util.Collection; import java.util.Map; @@ -30,11 +33,6 @@ import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Supplier; -import org.onosproject.store.service.Transaction; -import org.onosproject.store.service.Versioned; - -import com.google.common.collect.Sets; - /** * Default database. */ @@ -44,7 +42,7 @@ public class DefaultDatabase extends AbstractResource<Database> implements Datab private final Set<Consumer<StateMachineUpdate>> consumers = Sets.newCopyOnWriteArraySet(); private final TriConsumer<String, Object, Object> watcher = new InternalStateMachineWatcher(); - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) public DefaultDatabase(ResourceManager context) { super(context); this.stateMachine = new DefaultStateMachine(context, @@ -66,7 +64,7 @@ public class DefaultDatabase extends AbstractResource<Database> implements Datab * return the completed future result. * * @param supplier The supplier to call if the database is open. - * @param <T> The future result type. + * @param <T> The future result type. * @return A completable future that if this database is closed is immediately failed. */ protected <T> CompletableFuture<T> checkOpen(Supplier<CompletableFuture<T>> supplier) { @@ -153,6 +151,16 @@ public class DefaultDatabase extends AbstractResource<Database> implements Datab } @Override + public CompletableFuture<Void> counterSet(String counterName, long value) { + return checkOpen(() -> proxy.counterSet(counterName, value)); + } + + @Override + public CompletableFuture<Boolean> counterCompareAndSet(String counterName, long expectedValue, long update) { + return checkOpen(() -> proxy.counterCompareAndSet(counterName, expectedValue, update)); + } + + @Override public CompletableFuture<Long> queueSize(String queueName) { return checkOpen(() -> proxy.queueSize(queueName)); } diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabaseState.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabaseState.java index 9a55ffb1..8943fc87 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabaseState.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabaseState.java @@ -16,27 +16,26 @@ package org.onosproject.store.consistent.impl; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import net.kuujo.copycat.state.Initializer; +import net.kuujo.copycat.state.StateContext; +import org.onosproject.store.service.DatabaseUpdate; +import org.onosproject.store.service.Transaction; +import org.onosproject.store.service.Versioned; + import java.util.Arrays; import java.util.Collection; import java.util.LinkedList; import java.util.Map; import java.util.Map.Entry; import java.util.Queue; +import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; -import java.util.Set; - -import org.onosproject.store.service.DatabaseUpdate; -import org.onosproject.store.service.Transaction; -import org.onosproject.store.service.Versioned; -import com.google.common.base.Objects; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - -import net.kuujo.copycat.state.Initializer; -import net.kuujo.copycat.state.StateContext; /** * Default database state. @@ -195,6 +194,11 @@ public class DefaultDatabaseState implements DatabaseState<String, byte[]> { } @Override + public Boolean counterCompareAndSet(String counterName, long expectedValue, long updateValue) { + return getCounter(counterName).compareAndSet(expectedValue, updateValue); + } + + @Override public Long counterGet(String counterName) { return getCounter(counterName).get(); } diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/MutexExecutionManager.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/MutexExecutionManager.java new file mode 100644 index 00000000..d8593e37 --- /dev/null +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/MutexExecutionManager.java @@ -0,0 +1,315 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.consistent.impl; + +import static org.slf4j.LoggerFactory.getLogger; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.Service; +import org.onlab.util.Tools; +import org.onosproject.cluster.ClusterEvent; +import org.onosproject.cluster.ClusterEventListener; +import org.onosproject.cluster.ClusterService; +import org.onosproject.cluster.ControllerNode.State; +import org.onosproject.cluster.NodeId; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.ConsistentMap; +import org.onosproject.store.service.ConsistentMapException; +import org.onosproject.store.service.MapEvent; +import org.onosproject.store.service.MapEventListener; +import org.onosproject.store.service.MutexExecutionService; +import org.onosproject.store.service.MutexTask; +import org.onosproject.store.service.Serializer; +import org.onosproject.store.service.StorageService; +import org.onosproject.store.service.Versioned; +import org.slf4j.Logger; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +/** + * Implementation of a MutexExecutionService. + */ +@Component(immediate = true) +@Service +public class MutexExecutionManager implements MutexExecutionService { + + private final Logger log = getLogger(getClass()); + + protected ConsistentMap<String, MutexState> lockMap; + protected NodeId localNodeId; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ClusterService clusterService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected StorageService storageService; + + private final MapEventListener<String, MutexState> mapEventListener = new InternalLockMapEventListener(); + private final ClusterEventListener clusterEventListener = new InternalClusterEventListener(); + + private Map<String, CompletableFuture<MutexState>> pending = Maps.newConcurrentMap(); + private Map<String, InnerMutexTask> activeTasks = Maps.newConcurrentMap(); + + @Activate + public void activate() { + localNodeId = clusterService.getLocalNode().id(); + lockMap = storageService.<String, MutexState>consistentMapBuilder() + .withName("onos-mutexes") + .withSerializer(Serializer.using(Arrays.asList(KryoNamespaces.API), MutexState.class)) + .withPartitionsDisabled() + .build(); + lockMap.addListener(mapEventListener); + clusterService.addListener(clusterEventListener); + releaseOldLocks(); + log.info("Started"); + } + + @Deactivate + public void deactivate() { + lockMap.removeListener(mapEventListener); + pending.values().forEach(future -> future.cancel(true)); + activeTasks.forEach((k, v) -> { + v.stop(); + unlock(k); + }); + clusterService.removeListener(clusterEventListener); + log.info("Stopped"); + } + + @Override + public CompletableFuture<Void> execute(MutexTask task, String exclusionPath, Executor executor) { + return lock(exclusionPath) + .thenApply(state -> activeTasks.computeIfAbsent(exclusionPath, + k -> new InnerMutexTask(exclusionPath, + task, + state.term()))) + .thenAcceptAsync(t -> t.start(), executor) + .whenComplete((r, e) -> unlock(exclusionPath)); + } + + protected CompletableFuture<MutexState> lock(String exclusionPath) { + CompletableFuture<MutexState> future = + pending.computeIfAbsent(exclusionPath, k -> new CompletableFuture<>()); + tryLock(exclusionPath); + return future; + } + + /** + * Attempts to acquire lock for a path. If lock is held by some other node, adds this node to + * the wait list. + * @param exclusionPath exclusion path + */ + protected void tryLock(String exclusionPath) { + Tools.retryable(() -> lockMap.asJavaMap() + .compute(exclusionPath, + (k, v) -> MutexState.admit(v, localNodeId)), + ConsistentMapException.ConcurrentModification.class, + Integer.MAX_VALUE, + 100).get(); + } + + /** + * Releases lock for the specific path. This operation is idempotent. + * @param exclusionPath exclusion path + */ + protected void unlock(String exclusionPath) { + Tools.retryable(() -> lockMap.asJavaMap() + .compute(exclusionPath, (k, v) -> MutexState.evict(v, localNodeId)), + ConsistentMapException.ConcurrentModification.class, + Integer.MAX_VALUE, + 100).get(); + } + + /** + * Detects and releases all locks held by this node. + */ + private void releaseOldLocks() { + Maps.filterValues(lockMap.asJavaMap(), state -> localNodeId.equals(state.holder())) + .keySet() + .forEach(path -> { + log.info("Detected zombie task still holding lock for {}. Releasing lock.", path); + unlock(path); + }); + } + + private class InternalLockMapEventListener implements MapEventListener<String, MutexState> { + + @Override + public void event(MapEvent<String, MutexState> event) { + log.debug("Received {}", event); + if (event.type() == MapEvent.Type.UPDATE || event.type() == MapEvent.Type.INSERT) { + pending.computeIfPresent(event.key(), (k, future) -> { + MutexState state = Versioned.valueOrElse(event.value(), null); + if (state != null && localNodeId.equals(state.holder())) { + log.debug("Local node is now owner for {}", event.key()); + future.complete(state); + return null; + } else { + return future; + } + }); + InnerMutexTask task = activeTasks.get(event.key()); + if (task != null && task.term() < Versioned.valueOrElse(event.value(), null).term()) { + task.stop(); + } + } + } + } + + private class InternalClusterEventListener implements ClusterEventListener { + + @Override + public void event(ClusterEvent event) { + if (event.type() == ClusterEvent.Type.INSTANCE_DEACTIVATED || + event.type() == ClusterEvent.Type.INSTANCE_REMOVED) { + NodeId nodeId = event.subject().id(); + log.debug("{} is no longer active. Attemping to clean up its locks.", nodeId); + lockMap.asJavaMap().forEach((k, v) -> { + if (v.contains(nodeId)) { + lockMap.compute(k, (path, state) -> MutexState.evict(v, nodeId)); + } + }); + } + long activeNodes = clusterService.getNodes() + .stream() + .map(node -> clusterService.getState(node.id())) + .filter(State.ACTIVE::equals) + .count(); + if (clusterService.getNodes().size() > 1 && activeNodes == 1) { + log.info("This node is partitioned away from the cluster. Stopping all inflight executions"); + activeTasks.forEach((k, v) -> { + v.stop(); + }); + } + } + } + + private static final class MutexState { + + private final NodeId holder; + private final List<NodeId> waitList; + private final long term; + + public static MutexState admit(MutexState state, NodeId nodeId) { + if (state == null) { + return new MutexState(nodeId, 1L, Lists.newArrayList()); + } else if (state.holder() == null) { + return new MutexState(nodeId, state.term() + 1, Lists.newArrayList()); + } else { + if (!state.contains(nodeId)) { + NodeId newHolder = state.holder(); + List<NodeId> newWaitList = Lists.newArrayList(state.waitList()); + newWaitList.add(nodeId); + return new MutexState(newHolder, state.term(), newWaitList); + } else { + return state; + } + } + } + + public static MutexState evict(MutexState state, NodeId nodeId) { + return state.evict(nodeId); + } + + public MutexState evict(NodeId nodeId) { + if (nodeId.equals(holder)) { + if (waitList.isEmpty()) { + return new MutexState(null, term, waitList); + } + List<NodeId> newWaitList = Lists.newArrayList(waitList); + NodeId newHolder = newWaitList.remove(0); + return new MutexState(newHolder, term + 1, newWaitList); + } else { + NodeId newHolder = holder; + List<NodeId> newWaitList = Lists.newArrayList(waitList); + newWaitList.remove(nodeId); + return new MutexState(newHolder, term, newWaitList); + } + } + + public NodeId holder() { + return holder; + } + + public List<NodeId> waitList() { + return waitList; + } + + public long term() { + return term; + } + + private boolean contains(NodeId nodeId) { + return (nodeId.equals(holder) || waitList.contains(nodeId)); + } + + private MutexState(NodeId holder, long term, List<NodeId> waitList) { + this.holder = holder; + this.term = term; + this.waitList = Lists.newArrayList(waitList); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(getClass()) + .add("holder", holder) + .add("term", term) + .add("waitList", waitList) + .toString(); + } + } + + private class InnerMutexTask implements MutexTask { + private final MutexTask task; + private final String mutexPath; + private final long term; + + public InnerMutexTask(String mutexPath, MutexTask task, long term) { + this.mutexPath = mutexPath; + this.term = term; + this.task = task; + } + + public long term() { + return term; + } + + @Override + public void start() { + log.debug("Starting execution for mutex task guarded by {}", mutexPath); + task.start(); + log.debug("Finished execution for mutex task guarded by {}", mutexPath); + } + + @Override + public void stop() { + log.debug("Stopping execution for mutex task guarded by {}", mutexPath); + task.stop(); + } + } +}
\ No newline at end of file diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/PartitionedDatabase.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/PartitionedDatabase.java index a294681e..f741b367 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/PartitionedDatabase.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/PartitionedDatabase.java @@ -16,6 +16,17 @@ package org.onosproject.store.consistent.impl; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import net.kuujo.copycat.Task; +import net.kuujo.copycat.cluster.Cluster; +import net.kuujo.copycat.resource.ResourceState; +import org.onosproject.store.service.DatabaseUpdate; +import org.onosproject.store.service.Transaction; +import org.onosproject.store.service.Versioned; + import java.util.Collection; import java.util.List; import java.util.Map; @@ -28,18 +39,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.stream.Collectors; -import org.onosproject.store.service.DatabaseUpdate; -import org.onosproject.store.service.Transaction; -import org.onosproject.store.service.Versioned; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - -import net.kuujo.copycat.Task; -import net.kuujo.copycat.cluster.Cluster; -import net.kuujo.copycat.resource.ResourceState; import static com.google.common.base.Preconditions.checkState; /** @@ -100,10 +99,10 @@ public class PartitionedDatabase implements Database { return CompletableFuture.allOf(partitions .stream() .map(db -> db.counters() - .thenApply(m -> { - counters.putAll(m); - return null; - })) + .thenApply(m -> { + counters.putAll(m); + return null; + })) .toArray(CompletableFuture[]::new)) .thenApply(v -> counters); } @@ -113,9 +112,9 @@ public class PartitionedDatabase implements Database { checkState(isOpen.get(), DB_NOT_OPEN); AtomicInteger totalSize = new AtomicInteger(0); return CompletableFuture.allOf(partitions - .stream() - .map(p -> p.mapSize(mapName).thenApply(totalSize::addAndGet)) - .toArray(CompletableFuture[]::new)) + .stream() + .map(p -> p.mapSize(mapName).thenApply(totalSize::addAndGet)) + .toArray(CompletableFuture[]::new)) .thenApply(v -> totalSize.get()); } @@ -136,10 +135,10 @@ public class PartitionedDatabase implements Database { checkState(isOpen.get(), DB_NOT_OPEN); AtomicBoolean containsValue = new AtomicBoolean(false); return CompletableFuture.allOf(partitions - .stream() - .map(p -> p.mapContainsValue(mapName, value) - .thenApply(v -> containsValue.compareAndSet(false, v))) - .toArray(CompletableFuture[]::new)) + .stream() + .map(p -> p.mapContainsValue(mapName, value) + .thenApply(v -> containsValue.compareAndSet(false, v))) + .toArray(CompletableFuture[]::new)) .thenApply(v -> containsValue.get()); } @@ -196,9 +195,9 @@ public class PartitionedDatabase implements Database { checkState(isOpen.get(), DB_NOT_OPEN); Set<Entry<String, Versioned<byte[]>>> entrySet = Sets.newConcurrentHashSet(); return CompletableFuture.allOf(partitions - .stream() - .map(p -> p.mapEntrySet(mapName).thenApply(entrySet::addAll)) - .toArray(CompletableFuture[]::new)) + .stream() + .map(p -> p.mapEntrySet(mapName).thenApply(entrySet::addAll)) + .toArray(CompletableFuture[]::new)) .thenApply(v -> entrySet); } @@ -220,6 +219,19 @@ public class PartitionedDatabase implements Database { return partitioner.getPartition(counterName, counterName).counterGetAndAdd(counterName, delta); } + @Override + public CompletableFuture<Void> counterSet(String counterName, long value) { + checkState(isOpen.get(), DB_NOT_OPEN); + return partitioner.getPartition(counterName, counterName).counterSet(counterName, value); + } + + @Override + public CompletableFuture<Boolean> counterCompareAndSet(String counterName, long expectedValue, long updateValue) { + checkState(isOpen.get(), DB_NOT_OPEN); + return partitioner.getPartition(counterName, counterName). + counterCompareAndSet(counterName, expectedValue, updateValue); + + } @Override public CompletableFuture<Long> queueSize(String queueName) { @@ -268,8 +280,8 @@ public class PartitionedDatabase implements Database { AtomicBoolean status = new AtomicBoolean(true); return CompletableFuture.allOf(subTransactions.entrySet() .stream() - .map(entry -> entry - .getKey() + .map(entry -> entry + .getKey() .prepare(entry.getValue()) .thenApply(v -> status.compareAndSet(true, v))) .toArray(CompletableFuture[]::new)) @@ -282,15 +294,15 @@ public class PartitionedDatabase implements Database { AtomicBoolean success = new AtomicBoolean(true); List<UpdateResult<String, byte[]>> allUpdates = Lists.newArrayList(); return CompletableFuture.allOf(subTransactions.entrySet() - .stream() - .map(entry -> entry.getKey().commit(entry.getValue()) - .thenAccept(response -> { - success.set(success.get() && response.success()); - if (success.get()) { - allUpdates.addAll(response.updates()); - } - })) - .toArray(CompletableFuture[]::new)) + .stream() + .map(entry -> entry.getKey().commit(entry.getValue()) + .thenAccept(response -> { + success.set(success.get() && response.success()); + if (success.get()) { + allUpdates.addAll(response.updates()); + } + })) + .toArray(CompletableFuture[]::new)) .thenApply(v -> success.get() ? CommitResponse.success(allUpdates) : CommitResponse.failure()); } @@ -301,7 +313,7 @@ public class PartitionedDatabase implements Database { return CompletableFuture.allOf(subTransactions.entrySet() .stream() .map(entry -> entry.getKey().rollback(entry.getValue())) - .toArray(CompletableFuture[]::new)) + .toArray(CompletableFuture[]::new)) .thenApply(v -> true); } @@ -384,3 +396,4 @@ public class PartitionedDatabase implements Database { partitions.forEach(p -> p.unregisterConsumer(consumer)); } } + diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/ecmap/EventuallyConsistentMapImpl.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/ecmap/EventuallyConsistentMapImpl.java index 2859b62f..f1e0dbd4 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/ecmap/EventuallyConsistentMapImpl.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/ecmap/EventuallyConsistentMapImpl.java @@ -21,7 +21,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; - import org.apache.commons.lang3.tuple.Pair; import org.onlab.util.AbstractAccumulator; import org.onlab.util.KryoNamespace; @@ -33,18 +32,15 @@ import org.onosproject.store.Timestamp; import org.onosproject.store.cluster.messaging.ClusterCommunicationService; import org.onosproject.store.cluster.messaging.MessageSubject; import org.onosproject.store.impl.LogicalTimestamp; -import org.onosproject.store.service.WallClockTimestamp; import org.onosproject.store.serializers.KryoNamespaces; import org.onosproject.store.serializers.KryoSerializer; import org.onosproject.store.service.EventuallyConsistentMap; import org.onosproject.store.service.EventuallyConsistentMapEvent; import org.onosproject.store.service.EventuallyConsistentMapListener; +import org.onosproject.store.service.WallClockTimestamp; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.PUT; -import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.REMOVE; - import java.util.Collection; import java.util.Collections; import java.util.List; @@ -67,6 +63,8 @@ import static com.google.common.base.Preconditions.checkState; import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; import static org.onlab.util.BoundedThreadPool.newFixedThreadPool; import static org.onlab.util.Tools.groupedThreads; +import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.PUT; +import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.REMOVE; /** * Distributed Map implementation which uses optimistic replication and gossip @@ -359,7 +357,7 @@ public class EventuallyConsistentMapImpl<K, V> valueMatches = Objects.equals(value.get(), existing.get()); } if (existing == null) { - log.debug("ECMap Remove: Existing value for key {} is already null", k); + log.trace("ECMap Remove: Existing value for key {} is already null", k); } if (valueMatches) { if (existing == null) { @@ -523,7 +521,7 @@ public class EventuallyConsistentMapImpl<K, V> return; } peers.forEach(node -> - senderPending.computeIfAbsent(node, unusedKey -> new EventAccumulator(node)).add(event) + senderPending.computeIfAbsent(node, unusedKey -> new EventAccumulator(node)).add(event) ); } @@ -576,8 +574,10 @@ public class EventuallyConsistentMapImpl<K, V> return; } try { - log.debug("Received anti-entropy advertisement from {} for {} with {} entries in it", - mapName, ad.sender(), ad.digest().size()); + if (log.isTraceEnabled()) { + log.trace("Received anti-entropy advertisement from {} for {} with {} entries in it", + mapName, ad.sender(), ad.digest().size()); + } antiEntropyCheckLocalItems(ad).forEach(this::notifyListeners); if (!lightweightAntiEntropy) { @@ -675,4 +675,4 @@ public class EventuallyConsistentMapImpl<K, V> }); } } -}
\ No newline at end of file +} diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/flow/impl/NewDistributedFlowRuleStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/flow/impl/NewDistributedFlowRuleStore.java index de7a3ac3..8cd63e7d 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/flow/impl/NewDistributedFlowRuleStore.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/flow/impl/NewDistributedFlowRuleStore.java @@ -16,6 +16,7 @@ package org.onosproject.store.flow.impl; import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; @@ -57,6 +58,7 @@ import org.onosproject.net.flow.FlowRuleService; import org.onosproject.net.flow.FlowRuleStore; import org.onosproject.net.flow.FlowRuleStoreDelegate; import org.onosproject.net.flow.StoredFlowEntry; +import org.onosproject.net.flow.TableStatisticsEntry; import org.onosproject.store.AbstractStore; import org.onosproject.store.cluster.messaging.ClusterCommunicationService; import org.onosproject.store.cluster.messaging.ClusterMessage; @@ -64,9 +66,16 @@ import org.onosproject.store.cluster.messaging.ClusterMessageHandler; import org.onosproject.store.flow.ReplicaInfoEvent; import org.onosproject.store.flow.ReplicaInfoEventListener; import org.onosproject.store.flow.ReplicaInfoService; +import org.onosproject.store.impl.MastershipBasedTimestamp; +import org.onosproject.store.serializers.KryoNamespaces; import org.onosproject.store.serializers.KryoSerializer; import org.onosproject.store.serializers.StoreSerializer; import org.onosproject.store.serializers.custom.DistributedStoreSerializers; +import org.onosproject.store.service.EventuallyConsistentMap; +import org.onosproject.store.service.EventuallyConsistentMapEvent; +import org.onosproject.store.service.EventuallyConsistentMapListener; +import org.onosproject.store.service.StorageService; +import org.onosproject.store.service.WallClockTimestamp; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; @@ -151,6 +160,13 @@ public class NewDistributedFlowRuleStore private final ScheduledExecutorService backupSenderExecutor = Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/flow", "backup-sender")); + private EventuallyConsistentMap<DeviceId, List<TableStatisticsEntry>> deviceTableStats; + private final EventuallyConsistentMapListener<DeviceId, List<TableStatisticsEntry>> tableStatsListener = + new InternalTableStatsListener(); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected StorageService storageService; + protected static final StoreSerializer SERIALIZER = new KryoSerializer() { @Override protected void setupKryoPool() { @@ -161,6 +177,11 @@ public class NewDistributedFlowRuleStore } }; + protected static final KryoNamespace.Builder SERIALIZER_BUILDER = KryoNamespace.newBuilder() + .register(KryoNamespaces.API) + .register(MastershipBasedTimestamp.class); + + private IdGenerator idGenerator; private NodeId local; @@ -186,6 +207,15 @@ public class NewDistributedFlowRuleStore TimeUnit.MILLISECONDS); } + deviceTableStats = storageService.<DeviceId, List<TableStatisticsEntry>>eventuallyConsistentMapBuilder() + .withName("onos-flow-table-stats") + .withSerializer(SERIALIZER_BUILDER) + .withAntiEntropyPeriod(5, TimeUnit.SECONDS) + .withTimestampProvider((k, v) -> new WallClockTimestamp()) + .withTombstonesDisabled() + .build(); + deviceTableStats.addListener(tableStatsListener); + logConfig("Started"); } @@ -197,6 +227,8 @@ public class NewDistributedFlowRuleStore } configService.unregisterProperties(getClass(), false); unregisterMessageHandlers(); + deviceTableStats.removeListener(tableStatsListener); + deviceTableStats.destroy(); messageHandlingExecutor.shutdownNow(); backupSenderExecutor.shutdownNow(); log.info("Stopped"); @@ -786,4 +818,36 @@ public class NewDistributedFlowRuleStore return backedupDevices; } } + + @Override + public FlowRuleEvent updateTableStatistics(DeviceId deviceId, + List<TableStatisticsEntry> tableStats) { + deviceTableStats.put(deviceId, tableStats); + return null; + } + + @Override + public Iterable<TableStatisticsEntry> getTableStatistics(DeviceId deviceId) { + NodeId master = mastershipService.getMasterFor(deviceId); + + if (master == null) { + log.debug("Failed to getTableStats: No master for {}", deviceId); + return Collections.emptyList(); + } + + List<TableStatisticsEntry> tableStats = deviceTableStats.get(deviceId); + if (tableStats == null) { + return Collections.emptyList(); + } + return ImmutableList.copyOf(tableStats); + } + + private class InternalTableStatsListener + implements EventuallyConsistentMapListener<DeviceId, List<TableStatisticsEntry>> { + @Override + public void event(EventuallyConsistentMapEvent<DeviceId, + List<TableStatisticsEntry>> event) { + //TODO: Generate an event to listeners (do we need?) + } + } } diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/group/impl/DistributedGroupStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/group/impl/DistributedGroupStore.java index 97333ebf..a999ee7f 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/group/impl/DistributedGroupStore.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/group/impl/DistributedGroupStore.java @@ -28,19 +28,11 @@ import org.apache.felix.scr.annotations.Service; import org.onlab.util.KryoNamespace; import org.onlab.util.NewConcurrentHashMap; import org.onosproject.cluster.ClusterService; -import org.onosproject.core.DefaultApplicationId; import org.onosproject.core.DefaultGroupId; import org.onosproject.core.GroupId; import org.onosproject.mastership.MastershipService; import org.onosproject.net.DeviceId; import org.onosproject.net.MastershipRole; -import org.onosproject.net.PortNumber; -import org.onosproject.net.flow.DefaultTrafficTreatment; -import org.onosproject.net.flow.FlowRule; -import org.onosproject.net.flow.instructions.Instructions; -import org.onosproject.net.flow.instructions.L0ModificationInstruction; -import org.onosproject.net.flow.instructions.L2ModificationInstruction; -import org.onosproject.net.flow.instructions.L3ModificationInstruction; import org.onosproject.net.group.DefaultGroup; import org.onosproject.net.group.DefaultGroupBucket; import org.onosproject.net.group.DefaultGroupDescription; @@ -61,9 +53,7 @@ import org.onosproject.net.group.StoredGroupEntry; import org.onosproject.store.AbstractStore; import org.onosproject.store.cluster.messaging.ClusterCommunicationService; import org.onosproject.store.service.MultiValuedTimestamp; -import org.onosproject.store.serializers.DeviceIdSerializer; import org.onosproject.store.serializers.KryoNamespaces; -import org.onosproject.store.serializers.URISerializer; import org.onosproject.store.service.EventuallyConsistentMap; import org.onosproject.store.service.EventuallyConsistentMapBuilder; import org.onosproject.store.service.EventuallyConsistentMapEvent; @@ -71,7 +61,6 @@ import org.onosproject.store.service.EventuallyConsistentMapListener; import org.onosproject.store.service.StorageService; import org.slf4j.Logger; -import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -139,9 +128,12 @@ public class DistributedGroupStore private final AtomicLong sequenceNumber = new AtomicLong(0); + private KryoNamespace clusterMsgSerializer; + @Activate public void activate() { kryoBuilder = new KryoNamespace.Builder() + .register(KryoNamespaces.API) .register(DefaultGroup.class, DefaultGroupBucket.class, DefaultGroupDescription.class, @@ -158,38 +150,9 @@ public class DistributedGroupStore GroupStoreKeyMapKey.class, GroupStoreIdMapKey.class, GroupStoreMapKey.class - ) - .register(new URISerializer(), URI.class) - .register(new DeviceIdSerializer(), DeviceId.class) - .register(PortNumber.class) - .register(DefaultApplicationId.class) - .register(DefaultTrafficTreatment.class, - Instructions.DropInstruction.class, - Instructions.OutputInstruction.class, - Instructions.GroupInstruction.class, - Instructions.TableTypeTransition.class, - FlowRule.Type.class, - L0ModificationInstruction.class, - L0ModificationInstruction.L0SubType.class, - L0ModificationInstruction.ModLambdaInstruction.class, - L2ModificationInstruction.class, - L2ModificationInstruction.L2SubType.class, - L2ModificationInstruction.ModEtherInstruction.class, - L2ModificationInstruction.PushHeaderInstructions.class, - L2ModificationInstruction.ModVlanIdInstruction.class, - L2ModificationInstruction.ModVlanPcpInstruction.class, - L2ModificationInstruction.ModMplsLabelInstruction.class, - L2ModificationInstruction.ModMplsTtlInstruction.class, - L3ModificationInstruction.class, - L3ModificationInstruction.L3SubType.class, - L3ModificationInstruction.ModIPInstruction.class, - L3ModificationInstruction.ModIPv6FlowLabelInstruction.class, - L3ModificationInstruction.ModTtlInstruction.class, - org.onlab.packet.MplsLabel.class - ) - .register(org.onosproject.cluster.NodeId.class) - .register(KryoNamespaces.BASIC) - .register(KryoNamespaces.MISC); + ); + + clusterMsgSerializer = kryoBuilder.build(); messageHandlingExecutor = Executors. newFixedThreadPool(MESSAGE_HANDLER_THREAD_POOL_SIZE, @@ -197,7 +160,7 @@ public class DistributedGroupStore "message-handlers")); clusterCommunicator.addSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST, - kryoBuilder.build()::deserialize, + clusterMsgSerializer::deserialize, this::process, messageHandlingExecutor); @@ -233,6 +196,7 @@ public class DistributedGroupStore @Deactivate public void deactivate() { + clusterCommunicator.removeSubscriber(GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST); groupStoreEntriesByKey.destroy(); auditPendingReqQueue.destroy(); log.info("Stopped"); @@ -313,8 +277,6 @@ public class DistributedGroupStore @Override public Iterable<Group> getGroups(DeviceId deviceId) { // flatten and make iterator unmodifiable - log.debug("getGroups: for device {} total number of groups {}", - deviceId, getGroupStoreKeyMap().values().size()); return FluentIterable.from(getGroupStoreKeyMap().values()) .filter(input -> input.deviceId().equals(deviceId)) .transform(input -> input); @@ -322,8 +284,6 @@ public class DistributedGroupStore private Iterable<StoredGroupEntry> getStoredGroups(DeviceId deviceId) { // flatten and make iterator unmodifiable - log.debug("getGroups: for device {} total number of groups {}", - deviceId, getGroupStoreKeyMap().values().size()); return FluentIterable.from(getGroupStoreKeyMap().values()) .filter(input -> input.deviceId().equals(deviceId)); } @@ -411,7 +371,7 @@ public class DistributedGroupStore clusterCommunicator.unicast(groupOp, GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST, - m -> kryoBuilder.build().serialize(m), + clusterMsgSerializer::serialize, mastershipService.getMasterFor(groupDesc.deviceId())).whenComplete((result, error) -> { if (error != null) { log.warn("Failed to send request to master: {} to {}", @@ -609,7 +569,7 @@ public class DistributedGroupStore clusterCommunicator.unicast(groupOp, GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST, - m -> kryoBuilder.build().serialize(m), + clusterMsgSerializer::serialize, mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> { if (error != null) { log.warn("Failed to send request to master: {} to {}", @@ -741,7 +701,7 @@ public class DistributedGroupStore clusterCommunicator.unicast(groupOp, GroupStoreMessageSubjects.REMOTE_GROUP_OP_REQUEST, - m -> kryoBuilder.build().serialize(m), + clusterMsgSerializer::serialize, mastershipService.getMasterFor(deviceId)).whenComplete((result, error) -> { if (error != null) { log.warn("Failed to send request to master: {} to {}", diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/host/impl/ECHostStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/host/impl/ECHostStore.java index d0b827cd..f9c96891 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/host/impl/ECHostStore.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/host/impl/ECHostStore.java @@ -27,6 +27,7 @@ import static org.onosproject.store.service.EventuallyConsistentMapEvent.Type.RE import static org.slf4j.LoggerFactory.getLogger; import java.util.Collection; +import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; @@ -67,7 +68,6 @@ import org.onosproject.store.service.StorageService; import org.slf4j.Logger; import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimaps; import com.google.common.collect.SetMultimap; @@ -197,6 +197,35 @@ public class ECHostStore } @Override + public HostEvent removeIp(HostId hostId, IpAddress ipAddress) { + DefaultHost host = hosts.compute(hostId, (id, existingHost) -> { + if (existingHost != null) { + checkState(Objects.equals(hostId.mac(), existingHost.mac()), + "Existing and new MAC addresses differ."); + checkState(Objects.equals(hostId.vlanId(), existingHost.vlan()), + "Existing and new VLANs differ."); + + Set<IpAddress> addresses = existingHost.ipAddresses(); + if (addresses != null && addresses.contains(ipAddress)) { + addresses = new HashSet<>(existingHost.ipAddresses()); + addresses.remove(ipAddress); + return new DefaultHost(existingHost.providerId(), + hostId, + existingHost.mac(), + existingHost.vlan(), + existingHost.location(), + ImmutableSet.copyOf(addresses), + existingHost.annotations()); + } else { + return existingHost; + } + } + return null; + }); + return host != null ? new HostEvent(HOST_UPDATED, host) : null; + } + + @Override public int getHostCount() { return hosts.size(); } @@ -228,17 +257,23 @@ public class ECHostStore @Override public Set<Host> getConnectedHosts(ConnectPoint connectPoint) { - return ImmutableSet.copyOf(locations.get(connectPoint)); + synchronized (locations) { + return ImmutableSet.copyOf(locations.get(connectPoint)); + } } @Override public Set<Host> getConnectedHosts(DeviceId deviceId) { - return ImmutableMultimap.copyOf(locations) - .entries() - .stream() - .filter(entry -> entry.getKey().deviceId().equals(deviceId)) - .map(entry -> entry.getValue()) - .collect(Collectors.toSet()); + Set<Host> filtered; + synchronized (locations) { + filtered = locations + .entries() + .stream() + .filter(entry -> entry.getKey().deviceId().equals(deviceId)) + .map(entry -> entry.getValue()) + .collect(Collectors.toSet()); + } + return ImmutableSet.copyOf(filtered); } private Set<Host> filter(Collection<DefaultHost> collection, Predicate<DefaultHost> predicate) { diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/intent/impl/GossipIntentStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/intent/impl/GossipIntentStore.java index fa3a0751..1e5db99c 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/intent/impl/GossipIntentStore.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/intent/impl/GossipIntentStore.java @@ -193,7 +193,7 @@ public class GossipIntentStore private Collection<NodeId> getPeerNodes(Key key, IntentData data) { NodeId master = partitionService.getLeader(key); NodeId origin = (data != null) ? data.origin() : null; - if (master == null || origin == null) { + if (data != null && (master == null || origin == null)) { log.debug("Intent {} missing master and/or origin; master = {}, origin = {}", key, master, origin); } diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/link/impl/GossipLinkStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/link/impl/GossipLinkStore.java index 105c77df..47aa85c5 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/link/impl/GossipLinkStore.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/link/impl/GossipLinkStore.java @@ -826,7 +826,7 @@ public class GossipLinkStore public void handle(ClusterMessage message) { log.trace("Received link event from peer: {}", message.sender()); - InternalLinkEvent event = (InternalLinkEvent) SERIALIZER.decode(message.payload()); + InternalLinkEvent event = SERIALIZER.decode(message.payload()); ProviderId providerId = event.providerId(); Timestamped<LinkDescription> linkDescription = event.linkDescription(); @@ -845,7 +845,7 @@ public class GossipLinkStore public void handle(ClusterMessage message) { log.trace("Received link removed event from peer: {}", message.sender()); - InternalLinkRemovedEvent event = (InternalLinkRemovedEvent) SERIALIZER.decode(message.payload()); + InternalLinkRemovedEvent event = SERIALIZER.decode(message.payload()); LinkKey linkKey = event.linkKey(); Timestamp timestamp = event.timestamp(); diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/packet/impl/DistributedPacketStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/packet/impl/DistributedPacketStore.java index d4c89c93..f0f3eb5e 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/packet/impl/DistributedPacketStore.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/packet/impl/DistributedPacketStore.java @@ -15,7 +15,9 @@ */ package org.onosproject.store.packet.impl; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; +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; @@ -41,14 +43,13 @@ import org.onosproject.store.serializers.KryoSerializer; import org.onosproject.store.service.ConsistentMap; 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.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; import static org.onlab.util.Tools.groupedThreads; import static org.slf4j.LoggerFactory.getLogger; @@ -117,6 +118,7 @@ public class DistributedPacketStore public void deactivate() { communicationService.removeSubscriber(PACKET_OUT_SUBJECT); messageHandlingExecutor.shutdown(); + tracker = null; log.info("Stopped"); } @@ -143,13 +145,13 @@ public class DistributedPacketStore } @Override - public boolean requestPackets(PacketRequest request) { - return tracker.add(request); + public void requestPackets(PacketRequest request) { + tracker.add(request); } @Override - public boolean cancelPackets(PacketRequest request) { - return tracker.remove(request); + public void cancelPackets(PacketRequest request) { + tracker.remove(request); } @Override @@ -169,33 +171,50 @@ public class DistributedPacketStore .build(); } - public boolean add(PacketRequest request) { - Versioned<Set<PacketRequest>> old = requests.get(request.selector()); - if (old != null && old.value().contains(request)) { - return false; + public void add(PacketRequest request) { + AtomicBoolean firstRequest = new AtomicBoolean(false); + requests.compute(request.selector(), (s, existingRequests) -> { + if (existingRequests == null) { + firstRequest.set(true); + return ImmutableSet.of(request); + } else if (!existingRequests.contains(request)) { + return ImmutableSet.<PacketRequest>builder() + .addAll(existingRequests) + .add(request) + .build(); + } else { + return existingRequests; + } + }); + + if (firstRequest.get() && delegate != null) { + // The instance that makes the first request will push to all devices + delegate.requestPackets(request); } - // FIXME: add retry logic using a random delay - Set<PacketRequest> newSet = new HashSet<>(); - newSet.add(request); - if (old == null) { - return requests.putIfAbsent(request.selector(), newSet) == null; - } - newSet.addAll(old.value()); - return requests.replace(request.selector(), old.version(), newSet); } - public boolean remove(PacketRequest request) { - Versioned<Set<PacketRequest>> old = requests.get(request.selector()); - if (old == null || !old.value().contains(request)) { - return false; - } - // FIXME: add retry logic using a random delay - Set<PacketRequest> newSet = new HashSet<>(old.value()); - newSet.remove(request); - if (newSet.isEmpty()) { - return requests.remove(request.selector(), old.version()); + public void remove(PacketRequest request) { + AtomicBoolean removedLast = new AtomicBoolean(false); + requests.computeIfPresent(request.selector(), (s, existingRequests) -> { + if (existingRequests.contains(request)) { + Set<PacketRequest> newRequests = Sets.newHashSet(existingRequests); + newRequests.remove(request); + if (newRequests.size() > 0) { + return ImmutableSet.copyOf(newRequests); + } else { + removedLast.set(true); + return null; + } + } else { + return existingRequests; + } + }); + + if (removedLast.get() && delegate != null) { + // The instance that removes the last request will remove from all devices + delegate.cancelPackets(request); } - return requests.replace(request.selector(), old.version(), newSet); + } public List<PacketRequest> requests() { @@ -204,6 +223,5 @@ public class DistributedPacketStore list.sort((o1, o2) -> o1.priority().priorityValue() - o2.priority().priorityValue()); return list; } - } } diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentIntentSetMultimap.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentIntentSetMultimap.java new file mode 100644 index 00000000..87e67215 --- /dev/null +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentIntentSetMultimap.java @@ -0,0 +1,111 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.resource.impl; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.Service; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.intent.IntentId; +import org.onosproject.net.resource.device.IntentSetMultimap; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.ConsistentMap; +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.HashSet; +import java.util.Set; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * A collection that maps Intent IDs as keys to values as Intent IDs, + * where each key may associated with multiple values without duplication. + */ +@Component(immediate = true, enabled = true) +@Service +public class ConsistentIntentSetMultimap implements IntentSetMultimap { + private final Logger log = getLogger(getClass()); + + private static final String INTENT_MAPPING = "IntentMapping"; + + private static final Serializer SERIALIZER = Serializer.using(KryoNamespaces.API); + + private ConsistentMap<IntentId, Set<IntentId>> intentMapping; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected StorageService storageService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DeviceService deviceService; + + @Activate + public void activate() { + intentMapping = storageService.<IntentId, Set<IntentId>>consistentMapBuilder() + .withName(INTENT_MAPPING) + .withSerializer(SERIALIZER) + .build(); + log.info("Started"); + } + + @Deactivate + public void deactivate() { + log.info("Stopped"); + } + + @Override + public Set<IntentId> getMapping(IntentId intentId) { + Versioned<Set<IntentId>> result = intentMapping.get(intentId); + + if (result != null) { + return result.value(); + } + + return null; + } + + @Override + public boolean allocateMapping(IntentId keyIntentId, IntentId valIntentId) { + Versioned<Set<IntentId>> versionedIntents = intentMapping.get(keyIntentId); + + if (versionedIntents == null) { + Set<IntentId> newSet = new HashSet<>(); + newSet.add(valIntentId); + intentMapping.put(keyIntentId, newSet); + } else { + versionedIntents.value().add(valIntentId); + } + + return true; + } + + @Override + public void releaseMapping(IntentId intentId) { + for (IntentId intent : intentMapping.keySet()) { + // TODO: optimize by checking for identical src & dst + Set<IntentId> mapping = intentMapping.get(intent).value(); + if (mapping.remove(intentId)) { + return; + } + } + } + +} diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentLinkResourceStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentLinkResourceStore.java index 3a296353..11137aa2 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentLinkResourceStore.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/resource/impl/ConsistentLinkResourceStore.java @@ -40,7 +40,6 @@ import org.onosproject.net.Link; import org.onosproject.net.LinkKey; import org.onosproject.net.Port; import org.onosproject.net.intent.IntentId; -import org.onosproject.net.link.LinkService; import org.onosproject.net.resource.link.BandwidthResource; import org.onosproject.net.resource.link.BandwidthResourceAllocation; import org.onosproject.net.resource.link.LambdaResource; @@ -69,7 +68,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; import static org.slf4j.LoggerFactory.getLogger; import static org.onosproject.net.AnnotationKeys.BANDWIDTH; @@ -108,9 +106,6 @@ public class ConsistentLinkResourceStore extends protected StorageService storageService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected LinkService linkService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected DeviceService deviceService; @Activate @@ -139,29 +134,30 @@ public class ConsistentLinkResourceStore extends return storageService.transactionContextBuilder().build(); } - private Set<? extends ResourceAllocation> getResourceCapacity(ResourceType type, Link link) { - if (type == ResourceType.BANDWIDTH) { - return ImmutableSet.of(getBandwidthResourceCapacity(link)); - } - if (type == ResourceType.LAMBDA) { - return getLambdaResourceCapacity(link); + private Set<ResourceAllocation> getResourceCapacity(ResourceType type, Link link) { + switch (type) { + case BANDWIDTH: + return ImmutableSet.of(getBandwidthResourceCapacity(link)); + case LAMBDA: + return getLambdaResourceCapacity(link); + case MPLS_LABEL: + return getMplsResourceCapacity(); + default: + return ImmutableSet.of(); } - if (type == ResourceType.MPLS_LABEL) { - return getMplsResourceCapacity(); - } - return ImmutableSet.of(); } - private Set<LambdaResourceAllocation> getLambdaResourceCapacity(Link link) { - Set<LambdaResourceAllocation> allocations = new HashSet<>(); + private Set<ResourceAllocation> getLambdaResourceCapacity(Link link) { Port port = deviceService.getPort(link.src().deviceId(), link.src().port()); - if (port instanceof OmsPort) { - OmsPort omsPort = (OmsPort) port; + if (!(port instanceof OmsPort)) { + return Collections.emptySet(); + } - // Assume fixed grid for now - for (int i = 0; i < omsPort.totalChannels(); i++) { - allocations.add(new LambdaResourceAllocation(LambdaResource.valueOf(i))); - } + OmsPort omsPort = (OmsPort) port; + Set<ResourceAllocation> allocations = new HashSet<>(); + // Assume fixed grid for now + for (int i = 0; i < omsPort.totalChannels(); i++) { + allocations.add(new LambdaResourceAllocation(LambdaResource.valueOf(i))); } return allocations; } @@ -170,26 +166,23 @@ public class ConsistentLinkResourceStore extends // if Link annotation exist, use them // if all fails, use DEFAULT_BANDWIDTH - BandwidthResource bandwidth = null; + BandwidthResource bandwidth = DEFAULT_BANDWIDTH; String strBw = link.annotations().value(BANDWIDTH); - if (strBw != null) { - try { - bandwidth = new BandwidthResource(Bandwidth.mbps(Double.parseDouble(strBw))); - } catch (NumberFormatException e) { - // do nothings - bandwidth = null; - } + if (strBw == null) { + return new BandwidthResourceAllocation(bandwidth); } - if (bandwidth == null) { - // fall back, use fixed default + try { + bandwidth = new BandwidthResource(Bandwidth.mbps(Double.parseDouble(strBw))); + } catch (NumberFormatException e) { + // do nothings, use default bandwidth bandwidth = DEFAULT_BANDWIDTH; } return new BandwidthResourceAllocation(bandwidth); } - private Set<MplsLabelResourceAllocation> getMplsResourceCapacity() { - Set<MplsLabelResourceAllocation> allocations = new HashSet<>(); + private Set<ResourceAllocation> getMplsResourceCapacity() { + Set<ResourceAllocation> allocations = new HashSet<>(); //Ignoring reserved labels of 0 through 15 for (int i = MIN_UNRESERVED_LABEL; i <= MAX_UNRESERVED_LABEL; i++) { allocations.add(new MplsLabelResourceAllocation(MplsLabel @@ -199,13 +192,11 @@ public class ConsistentLinkResourceStore extends return allocations; } - private Map<ResourceType, Set<? extends ResourceAllocation>> getResourceCapacity(Link link) { - Map<ResourceType, Set<? extends ResourceAllocation>> caps = new HashMap<>(); + private Map<ResourceType, Set<ResourceAllocation>> getResourceCapacity(Link link) { + Map<ResourceType, Set<ResourceAllocation>> caps = new HashMap<>(); for (ResourceType type : ResourceType.values()) { - Set<? extends ResourceAllocation> cap = getResourceCapacity(type, link); - if (cap != null) { - caps.put(type, cap); - } + Set<ResourceAllocation> cap = getResourceCapacity(type, link); + caps.put(type, cap); } return caps; } @@ -216,106 +207,80 @@ public class ConsistentLinkResourceStore extends tx.begin(); try { - Map<ResourceType, Set<? extends ResourceAllocation>> freeResources = getFreeResourcesEx(tx, link); - Set<ResourceAllocation> allFree = new HashSet<>(); - freeResources.values().forEach(allFree::addAll); - return allFree; + Map<ResourceType, Set<ResourceAllocation>> freeResources = getFreeResourcesEx(tx, link); + return freeResources.values().stream() + .flatMap(Collection::stream) + .collect(Collectors.toSet()); } finally { tx.abort(); } } - private Map<ResourceType, Set<? extends ResourceAllocation>> getFreeResourcesEx(TransactionContext tx, Link link) { + private Map<ResourceType, Set<ResourceAllocation>> getFreeResourcesEx(TransactionContext tx, Link link) { checkNotNull(tx); checkNotNull(link); - Map<ResourceType, Set<? extends ResourceAllocation>> free = new HashMap<>(); - final Map<ResourceType, Set<? extends ResourceAllocation>> caps = getResourceCapacity(link); - final Iterable<LinkResourceAllocations> allocations = getAllocations(tx, link); + Map<ResourceType, Set<ResourceAllocation>> free = new HashMap<>(); + final Map<ResourceType, Set<ResourceAllocation>> caps = getResourceCapacity(link); + final List<LinkResourceAllocations> allocations = ImmutableList.copyOf(getAllocations(tx, link)); - for (ResourceType type : ResourceType.values()) { - // there should be class/category of resources + Set<ResourceAllocation> bw = caps.get(ResourceType.BANDWIDTH); + Set<ResourceAllocation> value = getFreeBandwidthResources(link, bw, allocations); + free.put(ResourceType.BANDWIDTH, value); - switch (type) { - case BANDWIDTH: - Set<? extends ResourceAllocation> bw = caps.get(type); - if (bw == null || bw.isEmpty()) { - bw = Sets.newHashSet(new BandwidthResourceAllocation(EMPTY_BW)); - } + Set<ResourceAllocation> lmd = caps.get(ResourceType.LAMBDA); + Set<ResourceAllocation> freeL = getFreeResources(link, lmd, allocations, + LambdaResourceAllocation.class); + free.put(ResourceType.LAMBDA, freeL); - BandwidthResourceAllocation cap = (BandwidthResourceAllocation) bw.iterator().next(); - double freeBw = cap.bandwidth().toDouble(); - - // enumerate current allocations, subtracting resources - for (LinkResourceAllocations alloc : allocations) { - Set<ResourceAllocation> types = alloc.getResourceAllocation(link); - for (ResourceAllocation a : types) { - if (a instanceof BandwidthResourceAllocation) { - BandwidthResourceAllocation bwA = (BandwidthResourceAllocation) a; - freeBw -= bwA.bandwidth().toDouble(); - } - } - } + Set<ResourceAllocation> mpls = caps.get(ResourceType.MPLS_LABEL); + Set<ResourceAllocation> freeLabel = getFreeResources(link, mpls, allocations, + MplsLabelResourceAllocation.class); + free.put(ResourceType.MPLS_LABEL, freeLabel); - free.put(type, Sets.newHashSet( - new BandwidthResourceAllocation(new BandwidthResource(Bandwidth.bps(freeBw))))); - break; - case LAMBDA: - Set<? extends ResourceAllocation> lmd = caps.get(type); - if (lmd == null || lmd.isEmpty()) { - // nothing left - break; - } - Set<LambdaResourceAllocation> freeL = new HashSet<>(); - for (ResourceAllocation r : lmd) { - if (r instanceof LambdaResourceAllocation) { - freeL.add((LambdaResourceAllocation) r); - } - } - - // enumerate current allocations, removing resources - for (LinkResourceAllocations alloc : allocations) { - Set<ResourceAllocation> types = alloc.getResourceAllocation(link); - for (ResourceAllocation a : types) { - if (a instanceof LambdaResourceAllocation) { - freeL.remove(a); - } - } - } + return free; + } - free.put(type, freeL); - break; - case MPLS_LABEL: - Set<? extends ResourceAllocation> mpls = caps.get(type); - if (mpls == null || mpls.isEmpty()) { - // nothing left - break; - } - Set<MplsLabelResourceAllocation> freeLabel = new HashSet<>(); - for (ResourceAllocation r : mpls) { - if (r instanceof MplsLabelResourceAllocation) { - freeLabel.add((MplsLabelResourceAllocation) r); - } - } + private Set<ResourceAllocation> getFreeBandwidthResources(Link link, Set<ResourceAllocation> bw, + List<LinkResourceAllocations> allocations) { + if (bw == null || bw.isEmpty()) { + bw = Sets.newHashSet(new BandwidthResourceAllocation(EMPTY_BW)); + } - // enumerate current allocations, removing resources - for (LinkResourceAllocations alloc : allocations) { - Set<ResourceAllocation> types = alloc.getResourceAllocation(link); - for (ResourceAllocation a : types) { - if (a instanceof MplsLabelResourceAllocation) { - freeLabel.remove(a); - } - } - } + BandwidthResourceAllocation cap = (BandwidthResourceAllocation) bw.iterator().next(); + double freeBw = cap.bandwidth().toDouble(); + + // enumerate current allocations, subtracting resources + double allocatedBw = allocations.stream() + .flatMap(x -> x.getResourceAllocation(link).stream()) + .filter(x -> x instanceof BandwidthResourceAllocation) + .map(x -> (BandwidthResourceAllocation) x) + .mapToDouble(x -> x.bandwidth().toDouble()) + .sum(); + freeBw -= allocatedBw; + return Sets.newHashSet( + new BandwidthResourceAllocation(new BandwidthResource(Bandwidth.bps(freeBw)))); + } - free.put(type, freeLabel); - break; - default: - log.debug("unsupported ResourceType {}", type); - break; - } + private Set<ResourceAllocation> getFreeResources(Link link, + Set<ResourceAllocation> resources, + List<LinkResourceAllocations> allocations, + Class<? extends ResourceAllocation> cls) { + if (resources == null || resources.isEmpty()) { + // nothing left + return Collections.emptySet(); } - return free; + Set<ResourceAllocation> freeL = resources.stream() + .filter(cls::isInstance) + .collect(Collectors.toSet()); + + // enumerate current allocations, removing resources + List<ResourceAllocation> allocated = allocations.stream() + .flatMap(x -> x.getResourceAllocation(link).stream()) + .filter(cls::isInstance) + .collect(Collectors.toList()); + freeL.removeAll(allocated); + return freeL; } @Override @@ -329,6 +294,9 @@ public class ConsistentLinkResourceStore extends intentAllocs.put(allocations.intentId(), allocations); allocations.links().forEach(link -> allocateLinkResource(tx, link, allocations)); tx.commit(); + } catch (TransactionException | ResourceAllocationException e) { + log.error("Exception thrown, rolling back", e); + tx.abort(); } catch (Exception e) { log.error("Exception thrown, rolling back", e); tx.abort(); @@ -340,15 +308,13 @@ public class ConsistentLinkResourceStore extends LinkResourceAllocations allocations) { // requested resources Set<ResourceAllocation> reqs = allocations.getResourceAllocation(link); - Map<ResourceType, Set<? extends ResourceAllocation>> available = getFreeResourcesEx(tx, link); + Map<ResourceType, Set<ResourceAllocation>> available = getFreeResourcesEx(tx, link); for (ResourceAllocation req : reqs) { - Set<? extends ResourceAllocation> avail = available.get(req.type()); + Set<ResourceAllocation> avail = available.get(req.type()); if (req instanceof BandwidthResourceAllocation) { // check if allocation should be accepted if (avail.isEmpty()) { - checkState(!avail.isEmpty(), - "There's no Bandwidth resource on %s?", - link); + throw new ResourceAllocationException(String.format("There's no Bandwidth resource on %s?", link)); } BandwidthResourceAllocation bw = (BandwidthResourceAllocation) avail.iterator().next(); double bwLeft = bw.bandwidth().toDouble(); @@ -395,12 +361,7 @@ public class ConsistentLinkResourceStore extends if (before == null) { List<LinkResourceAllocations> after = new ArrayList<>(); after.add(allocations); - before = linkAllocs.putIfAbsent(linkKey, after); - if (before != null) { - // concurrent allocation detected, retry transaction : is this needed? - log.warn("Concurrent Allocation, retrying"); - throw new TransactionException(); - } + linkAllocs.putIfAbsent(linkKey, after); } else { List<LinkResourceAllocations> after = new ArrayList<>(before.size() + 1); after.addAll(before); @@ -500,19 +461,18 @@ public class ConsistentLinkResourceStore extends checkNotNull(link); final LinkKey key = LinkKey.linkKey(link); TransactionalMap<LinkKey, List<LinkResourceAllocations>> linkAllocs = getLinkAllocs(tx); - List<LinkResourceAllocations> res = null; - res = linkAllocs.get(key); - if (res == null) { - res = linkAllocs.putIfAbsent(key, new ArrayList<>()); + List<LinkResourceAllocations> res = linkAllocs.get(key); + if (res != null) { + return res; + } - if (res == null) { - return Collections.emptyList(); - } else { - return res; - } + res = linkAllocs.putIfAbsent(key, new ArrayList<>()); + if (res == null) { + return Collections.emptyList(); + } else { + return res; } - return res; } } diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/statistic/impl/DistributedFlowStatisticStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/statistic/impl/DistributedFlowStatisticStore.java new file mode 100644 index 00000000..0cd4a831 --- /dev/null +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/statistic/impl/DistributedFlowStatisticStore.java @@ -0,0 +1,289 @@ +/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.store.statistic.impl;
+
+import com.google.common.base.Objects;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
+import org.onlab.util.Tools;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.statistic.FlowStatisticStore;
+import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.serializers.KryoSerializer;
+import org.slf4j.Logger;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.store.statistic.impl.StatisticStoreMessageSubjects.GET_CURRENT;
+import static org.onosproject.store.statistic.impl.StatisticStoreMessageSubjects.GET_PREVIOUS;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Maintains flow statistics using RPC calls to collect stats from remote instances
+ * on demand.
+ */
+@Component(immediate = true)
+@Service
+public class DistributedFlowStatisticStore implements FlowStatisticStore {
+ private final Logger log = getLogger(getClass());
+
+ // TODO: Make configurable.
+ private static final int MESSAGE_HANDLER_THREAD_POOL_SIZE = 4;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterCommunicationService clusterCommunicator;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ private Map<ConnectPoint, Set<FlowEntry>> previous =
+ new ConcurrentHashMap<>();
+
+ private Map<ConnectPoint, Set<FlowEntry>> current =
+ new ConcurrentHashMap<>();
+
+ protected static final KryoSerializer SERIALIZER = new KryoSerializer() {
+ @Override
+ protected void setupKryoPool() {
+ serializerPool = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
+ // register this store specific classes here
+ .build();
+ }
+ };
+
+ private NodeId local;
+ private ExecutorService messageHandlingExecutor;
+
+ private static final long STATISTIC_STORE_TIMEOUT_MILLIS = 3000;
+
+ @Activate
+ public void activate() {
+ local = clusterService.getLocalNode().id();
+
+ messageHandlingExecutor = Executors.newFixedThreadPool(
+ MESSAGE_HANDLER_THREAD_POOL_SIZE,
+ groupedThreads("onos/store/statistic", "message-handlers"));
+
+ clusterCommunicator.addSubscriber(
+ GET_CURRENT, SERIALIZER::decode, this::getCurrentStatisticInternal, SERIALIZER::encode,
+ messageHandlingExecutor);
+
+ clusterCommunicator.addSubscriber(
+ GET_CURRENT, SERIALIZER::decode, this::getPreviousStatisticInternal, SERIALIZER::encode,
+ messageHandlingExecutor);
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ clusterCommunicator.removeSubscriber(GET_PREVIOUS);
+ clusterCommunicator.removeSubscriber(GET_CURRENT);
+ messageHandlingExecutor.shutdown();
+ log.info("Stopped");
+ }
+
+ @Override
+ public synchronized void removeFlowStatistic(FlowRule rule) {
+ ConnectPoint cp = buildConnectPoint(rule);
+ if (cp == null) {
+ return;
+ }
+
+ // remove this rule if present from current map
+ current.computeIfPresent(cp, (c, e) -> { e.remove(rule); return e; });
+
+ // remove this on if present from previous map
+ previous.computeIfPresent(cp, (c, e) -> { e.remove(rule); return e; });
+ }
+
+ @Override
+ public synchronized void addFlowStatistic(FlowEntry rule) {
+ ConnectPoint cp = buildConnectPoint(rule);
+ if (cp == null) {
+ return;
+ }
+
+ // create one if absent and add this rule
+ current.putIfAbsent(cp, new HashSet<>());
+ current.computeIfPresent(cp, (c, e) -> { e.add(rule); return e; });
+
+ // remove previous one if present
+ previous.computeIfPresent(cp, (c, e) -> { e.remove(rule); return e; });
+ }
+
+ public synchronized void updateFlowStatistic(FlowEntry rule) {
+ ConnectPoint cp = buildConnectPoint(rule);
+ if (cp == null) {
+ return;
+ }
+
+ Set<FlowEntry> curr = current.get(cp);
+ if (curr == null) {
+ addFlowStatistic(rule);
+ } else {
+ Optional<FlowEntry> f = curr.stream().filter(c -> rule.equals(c)).
+ findAny();
+ if (f.isPresent() && rule.bytes() < f.get().bytes()) {
+ log.debug("DistributedFlowStatisticStore:updateFlowStatistic():" +
+ " Invalid Flow Update! Will be removed!!" +
+ " curr flowId=" + Long.toHexString(rule.id().value()) +
+ ", prev flowId=" + Long.toHexString(f.get().id().value()) +
+ ", curr bytes=" + rule.bytes() + ", prev bytes=" + f.get().bytes() +
+ ", curr life=" + rule.life() + ", prev life=" + f.get().life() +
+ ", curr lastSeen=" + rule.lastSeen() + ", prev lastSeen=" + f.get().lastSeen());
+ // something is wrong! invalid flow entry, so delete it
+ removeFlowStatistic(rule);
+ return;
+ }
+ Set<FlowEntry> prev = previous.get(cp);
+ if (prev == null) {
+ prev = new HashSet<>();
+ previous.put(cp, prev);
+ }
+
+ // previous one is exist
+ if (f.isPresent()) {
+ // remove old one and add new one
+ prev.remove(rule);
+ if (!prev.add(f.get())) {
+ log.debug("DistributedFlowStatisticStore:updateFlowStatistic():" +
+ " flowId={}, add failed into previous.",
+ Long.toHexString(rule.id().value()));
+ }
+ }
+
+ // remove old one and add new one
+ curr.remove(rule);
+ if (!curr.add(rule)) {
+ log.debug("DistributedFlowStatisticStore:updateFlowStatistic():" +
+ " flowId={}, add failed into current.",
+ Long.toHexString(rule.id().value()));
+ }
+ }
+ }
+
+ @Override
+ public Set<FlowEntry> getCurrentFlowStatistic(ConnectPoint connectPoint) {
+ final DeviceId deviceId = connectPoint.deviceId();
+
+ NodeId master = mastershipService.getMasterFor(deviceId);
+ if (master == null) {
+ log.warn("No master for {}", deviceId);
+ return Collections.emptySet();
+ }
+
+ if (Objects.equal(local, master)) {
+ return getCurrentStatisticInternal(connectPoint);
+ } else {
+ return Tools.futureGetOrElse(clusterCommunicator.sendAndReceive(
+ connectPoint,
+ GET_CURRENT,
+ SERIALIZER::encode,
+ SERIALIZER::decode,
+ master),
+ STATISTIC_STORE_TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS,
+ Collections.emptySet());
+ }
+ }
+
+ private synchronized Set<FlowEntry> getCurrentStatisticInternal(ConnectPoint connectPoint) {
+ return current.get(connectPoint);
+ }
+
+ @Override
+ public Set<FlowEntry> getPreviousFlowStatistic(ConnectPoint connectPoint) {
+ final DeviceId deviceId = connectPoint.deviceId();
+
+ NodeId master = mastershipService.getMasterFor(deviceId);
+ if (master == null) {
+ log.warn("No master for {}", deviceId);
+ return Collections.emptySet();
+ }
+
+ if (Objects.equal(local, master)) {
+ return getPreviousStatisticInternal(connectPoint);
+ } else {
+ return Tools.futureGetOrElse(clusterCommunicator.sendAndReceive(
+ connectPoint,
+ GET_PREVIOUS,
+ SERIALIZER::encode,
+ SERIALIZER::decode,
+ master),
+ STATISTIC_STORE_TIMEOUT_MILLIS,
+ TimeUnit.MILLISECONDS,
+ Collections.emptySet());
+ }
+ }
+
+ private synchronized Set<FlowEntry> getPreviousStatisticInternal(ConnectPoint connectPoint) {
+ return previous.get(connectPoint);
+ }
+
+ private ConnectPoint buildConnectPoint(FlowRule rule) {
+ PortNumber port = getOutput(rule);
+
+ if (port == null) {
+ return null;
+ }
+ ConnectPoint cp = new ConnectPoint(rule.deviceId(), port);
+ return cp;
+ }
+
+ private PortNumber getOutput(FlowRule rule) {
+ for (Instruction i : rule.treatment().allInstructions()) {
+ if (i.type() == Instruction.Type.OUTPUT) {
+ Instructions.OutputInstruction out = (Instructions.OutputInstruction) i;
+ return out.port();
+ }
+ if (i.type() == Instruction.Type.DROP) {
+ return PortNumber.P0;
+ }
+ }
+ return null;
+ }
+}
\ No newline at end of file diff --git a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/topology/impl/DistributedTopologyStore.java b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/topology/impl/DistributedTopologyStore.java index 487fad9b..da4e3cc4 100644 --- a/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/topology/impl/DistributedTopologyStore.java +++ b/framework/src/onos/core/store/dist/src/main/java/org/onosproject/store/topology/impl/DistributedTopologyStore.java @@ -21,6 +21,7 @@ import static org.onosproject.net.topology.TopologyEvent.Type.TOPOLOGY_CHANGED; import static org.slf4j.LoggerFactory.getLogger; import java.util.Collections; +import java.util.Map; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -40,6 +41,7 @@ import org.onosproject.net.Device; import org.onosproject.net.DeviceId; import org.onosproject.net.Link; import org.onosproject.net.Path; +import org.onosproject.net.DisjointPath; import org.onosproject.net.provider.ProviderId; import org.onosproject.net.topology.ClusterId; import org.onosproject.net.topology.DefaultGraphDescription; @@ -74,7 +76,6 @@ public class DistributedTopologyStore implements TopologyStore { private final Logger log = getLogger(getClass()); - private volatile DefaultTopology current = new DefaultTopology(ProviderId.NONE, new DefaultGraphDescription(0L, System.currentTimeMillis(), @@ -167,6 +168,29 @@ public class DistributedTopologyStore } @Override + public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst) { + return defaultTopology(topology).getDisjointPaths(src, dst); + } + + @Override + public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst, + LinkWeight weight) { + return defaultTopology(topology).getDisjointPaths(src, dst, weight); + } + + @Override + public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst, + Map<Link, Object> riskProfile) { + return defaultTopology(topology).getDisjointPaths(src, dst, riskProfile); + } + + @Override + public Set<DisjointPath> getDisjointPaths(Topology topology, DeviceId src, DeviceId dst, + LinkWeight weight, Map<Link, Object> riskProfile) { + return defaultTopology(topology).getDisjointPaths(src, dst, weight, riskProfile); + } + + @Override public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) { return defaultTopology(topology).isInfrastructure(connectPoint); } diff --git a/framework/src/onos/core/store/dist/src/test/java/org/onosproject/store/host/impl/ECHostStoreTest.java b/framework/src/onos/core/store/dist/src/test/java/org/onosproject/store/host/impl/ECHostStoreTest.java new file mode 100644 index 00000000..a7077a81 --- /dev/null +++ b/framework/src/onos/core/store/dist/src/test/java/org/onosproject/store/host/impl/ECHostStoreTest.java @@ -0,0 +1,95 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.store.host.impl; + +import junit.framework.TestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.IpAddress; +import org.onlab.packet.MacAddress; +import org.onosproject.net.Host; +import org.onosproject.net.HostId; +import org.onosproject.net.HostLocation; +import org.onosproject.net.host.DefaultHostDescription; +import org.onosproject.net.host.HostDescription; +import org.onosproject.net.provider.ProviderId; +import org.onosproject.store.Timestamp; +import org.onosproject.store.service.LogicalClockService; +import org.onosproject.store.service.TestStorageService; + +import java.util.HashSet; +import java.util.Set; + +/** + * Tests for the ECHostStore. + */ +public class ECHostStoreTest extends TestCase { + + private ECHostStore ecXHostStore; + + private static final HostId HOSTID = HostId.hostId(MacAddress.valueOf("1a:1a:1a:1a:1a:1a")); + + private static final IpAddress IP1 = IpAddress.valueOf("10.2.0.2"); + private static final IpAddress IP2 = IpAddress.valueOf("10.2.0.3"); + + private static final ProviderId PID = new ProviderId("of", "foo"); + + @Before + public void setUp() { + ecXHostStore = new ECHostStore(); + + ecXHostStore.storageService = new TestStorageService(); + ecXHostStore.clockService = new TestLogicalClockService(); + ecXHostStore.activate(); + } + + @After + public void tearDown() { + ecXHostStore.deactivate(); + } + + /** + * Tests the removeIp method call. + */ + @Test + public void testRemoveIp() { + Set<IpAddress> ips = new HashSet<>(); + ips.add(IP1); + ips.add(IP2); + + HostDescription description = new DefaultHostDescription(HOSTID.mac(), + HOSTID.vlanId(), + HostLocation.NONE, + ips); + ecXHostStore.createOrUpdateHost(PID, HOSTID, description, false); + ecXHostStore.removeIp(HOSTID, IP1); + Host host = ecXHostStore.getHost(HOSTID); + + assertFalse(host.ipAddresses().contains(IP1)); + assertTrue(host.ipAddresses().contains(IP2)); + } + + /** + * Mocks the LogicalClockService class. + */ + class TestLogicalClockService implements LogicalClockService { + @Override + public Timestamp getTimestamp() { + return null; + } + } +}
\ No newline at end of file diff --git a/framework/src/onos/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java b/framework/src/onos/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java index 66ee7be7..5b5056cb 100644 --- a/framework/src/onos/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java +++ b/framework/src/onos/core/store/serializers/src/main/java/org/onosproject/store/serializers/KryoNamespaces.java @@ -19,7 +19,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; - import org.onlab.packet.ChassisId; import org.onlab.packet.EthType; import org.onlab.packet.Ip4Address; @@ -85,11 +84,11 @@ import org.onosproject.net.device.PortStatistics; import org.onosproject.net.flow.CompletedBatchOperation; import org.onosproject.net.flow.DefaultFlowEntry; import org.onosproject.net.flow.DefaultFlowRule; +import org.onosproject.net.flow.DefaultTableStatisticsEntry; import org.onosproject.net.flow.DefaultTrafficSelector; import org.onosproject.net.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.FlowEntry; import org.onosproject.net.flow.FlowId; -import org.onosproject.net.flow.FlowRule; import org.onosproject.net.flow.FlowRuleBatchEntry; import org.onosproject.net.flow.FlowRuleBatchEvent; import org.onosproject.net.flow.FlowRuleBatchOperation; @@ -97,6 +96,7 @@ import org.onosproject.net.flow.FlowRuleBatchRequest; import org.onosproject.net.flow.FlowRuleEvent; import org.onosproject.net.flow.FlowRuleExtPayLoad; import org.onosproject.net.flow.StoredFlowEntry; +import org.onosproject.net.flow.TableStatisticsEntry; import org.onosproject.net.flow.criteria.Criterion; import org.onosproject.net.flow.criteria.EthCriterion; import org.onosproject.net.flow.criteria.EthTypeCriterion; @@ -118,7 +118,6 @@ import org.onosproject.net.flow.criteria.MetadataCriterion; import org.onosproject.net.flow.criteria.MplsCriterion; import org.onosproject.net.flow.criteria.OchSignalCriterion; import org.onosproject.net.flow.criteria.OchSignalTypeCriterion; -import org.onosproject.net.flow.criteria.OpticalSignalTypeCriterion; import org.onosproject.net.flow.criteria.PortCriterion; import org.onosproject.net.flow.criteria.SctpPortCriterion; import org.onosproject.net.flow.criteria.TcpPortCriterion; @@ -302,7 +301,6 @@ public final class KryoNamespaces { DefaultHostDescription.class, DefaultFlowEntry.class, StoredFlowEntry.class, - FlowRule.Type.class, DefaultFlowRule.class, DefaultFlowEntry.class, DefaultPacketRequest.class, @@ -339,11 +337,11 @@ public final class KryoNamespaces { IndexedLambdaCriterion.class, OchSignalCriterion.class, OchSignalTypeCriterion.class, - OpticalSignalTypeCriterion.class, Criterion.class, Criterion.Type.class, DefaultTrafficTreatment.class, Instructions.DropInstruction.class, + Instructions.NoActionInstruction.class, Instructions.OutputInstruction.class, Instructions.GroupInstruction.class, Instructions.TableTypeTransition.class, @@ -425,7 +423,9 @@ public final class KryoNamespaces { DefaultAnnotations.class, PortStatistics.class, DefaultPortStatistics.class, - IntentDomainId.class + IntentDomainId.class, + TableStatisticsEntry.class, + DefaultTableStatisticsEntry.class ) .register(new DefaultApplicationIdSerializer(), DefaultApplicationId.class) .register(new URISerializer(), URI.class) diff --git a/framework/src/onos/docs/external-excludes b/framework/src/onos/docs/external-excludes index 34329f19..1890de18 100644 --- a/framework/src/onos/docs/external-excludes +++ b/framework/src/onos/docs/external-excludes @@ -46,6 +46,9 @@ org.onosproject.pcep* org.onosproject.aaa org.onosproject.acl* org.onosproject.cip* -org.onos.acl* +org.onosproject.acl* org.onosproject.vtn* +org.onosproject.cord* +org.onosproject.mfwd* +org.onosproject.mcast* org.onosproject.flowanalyzer diff --git a/framework/src/onos/docs/internal-apps b/framework/src/onos/docs/internal-apps index 8c4a493f..71aacd03 100644 --- a/framework/src/onos/docs/internal-apps +++ b/framework/src/onos/docs/internal-apps @@ -1,6 +1,5 @@ org.onosproject.app.* -org.onos.acl* org.onosproject.acl* org.onosproject.aaa org.onosproject.fwd @@ -25,3 +24,7 @@ org.onosproject.cordfabric* org.onosproject.xosintegration* org.onosproject.cip* org.onosproject.vtn* +org.onosproject.cord* +org.onosproject.mcast* +org.onosproject.mfwd* +org.onosproject.igmp.impl diff --git a/framework/src/onos/drivers/pom.xml b/framework/src/onos/drivers/pom.xml index 749a68c4..56a39a8e 100644 --- a/framework/src/onos/drivers/pom.xml +++ b/framework/src/onos/drivers/pom.xml @@ -55,8 +55,8 @@ <dependency> <groupId>org.onosproject</groupId> <artifactId>onos-core-serializers</artifactId> - <version>1.4.0-SNAPSHOT</version> - </dependency> + <version>${project.version}</version> + </dependency> <dependency> <groupId>org.onosproject</groupId> <artifactId>onos-ovsdb-api</artifactId> @@ -72,6 +72,25 @@ <groupId>org.apache.felix</groupId> <artifactId>org.apache.felix.scr.annotations</artifactId> </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + <version>${project.version}</version> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-ovsdb-api</artifactId> + <version>${project.version}</version> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> </dependencies> <build> diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitch13.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitch13.java new file mode 100644 index 00000000..a62b93c8 --- /dev/null +++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/handshaker/OFOpticalSwitch13.java @@ -0,0 +1,170 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.driver.handshaker; + +import org.projectfloodlight.openflow.protocol.OFExpPort; +import org.projectfloodlight.openflow.protocol.OFExpPortDescReply; +import org.projectfloodlight.openflow.protocol.OFExpPortDescRequest; +import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFPortDesc; +import org.projectfloodlight.openflow.protocol.OFStatsReply; +import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags; +import org.projectfloodlight.openflow.protocol.OFStatsType; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + +import org.onosproject.net.Device; +import org.onosproject.openflow.controller.OpenFlowOpticalSwitch; +import org.onosproject.openflow.controller.PortDescPropertyType; +import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch; +import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeAlreadyStarted; +import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeCompleted; +import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted; +import org.projectfloodlight.openflow.protocol.OFObject; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + + +/** + * Open Flow Optical Switch handshaker - for Open Flow 13. + */ +public class OFOpticalSwitch13 extends AbstractOpenFlowSwitch implements OpenFlowOpticalSwitch { + + private final AtomicBoolean driverHandshakeComplete = new AtomicBoolean(false); + private List<OFExpPort> expPortDes = new ArrayList<>(); + + @Override + public Boolean supportNxRole() { + return false; + } + + @Override + public void startDriverHandshake() { + log.info("Starting driver handshake for sw {}", getStringId()); + if (startDriverHandshakeCalled) { + throw new SwitchDriverSubHandshakeAlreadyStarted(); + } + startDriverHandshakeCalled = true; + + log.debug("sendHandshakeOFExperimenterPortDescRequest for sw {}", getStringId()); + + try { + sendHandshakeOFExperimenterPortDescRequest(); + } catch (IOException e) { + log.error("Failed to send handshaker message OFExperimenterPortDescRequestfor sw {}, {}", + getStringId(), e.getMessage()); + e.printStackTrace(); + } + } + + @Override + public void processDriverHandshakeMessage(OFMessage m) { + if (!startDriverHandshakeCalled) { + throw new SwitchDriverSubHandshakeNotStarted(); + } + if (driverHandshakeComplete.get()) { + throw new SwitchDriverSubHandshakeCompleted(m); + } + + log.debug("processDriverHandshakeMessage for sw {}", getStringId()); + + switch (m.getType()) { + case STATS_REPLY: // multipart message is reported as STAT + processOFMultipartReply((OFStatsReply) m); + break; + default: + log.warn("Received message {} during switch-driver " + + "subhandshake " + "from switch {} ... " + + "Ignoring message", m, + getStringId()); + } + } + + private void processOFMultipartReply(OFStatsReply stats) { + log.debug("Received message {} during switch-driver " + + "subhandshake " + "from switch {} ... " + + stats, + getStringId()); + + if (stats.getStatsType() == OFStatsType.EXPERIMENTER) { + try { + OFExpPortDescReply expPortDescReply = (OFExpPortDescReply) stats; + expPortDes.addAll(expPortDescReply.getEntries()); + if (!expPortDescReply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) { + driverHandshakeComplete.set(true); + return; + } + } catch (ClassCastException e) { + log.error("Unexspected Experimenter Multipart message type {} " + , stats.getClass().getName()); + } + } + } + + + @Override + public boolean isDriverHandshakeComplete() { + return driverHandshakeComplete.get(); + } + + private void sendHandshakeOFExperimenterPortDescRequest() throws + IOException { + + OFExpPortDescRequest preq = factory() + .buildExpPortDescRequest() + .setXid(getNextTransactionId()) + .build(); + + log.debug("Sending experimented port description " + + "message " + + "{}", + preq.toString()); + + this.sendHandshakeMessage(preq); + } + + @Override + public Device.Type deviceType() { + return Device.Type.ROADM; + } + + /* + * OduClt ports are reported as regular ETH ports. + */ + @Override + public List<OFPortDesc> getPorts() { + return ImmutableList.copyOf( + ports.stream().flatMap(p -> p.getEntries().stream()) + .collect(Collectors.toList())); + } + + @Override + public List<? extends OFObject> getPortsOf(PortDescPropertyType type) { + return ImmutableList.copyOf(expPortDes); + } + + @Override + public Set<PortDescPropertyType> getPortTypes() { + return ImmutableSet.of(PortDescPropertyType.OPTICAL_TRANSPORT); + } + +} diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbControllerConfig.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbControllerConfig.java new file mode 100644 index 00000000..a00d3dbc --- /dev/null +++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/ovsdb/OvsdbControllerConfig.java @@ -0,0 +1,102 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.driver.ovsdb; + +import org.onlab.packet.IpAddress; +import org.onlab.packet.TpPort; +import org.onosproject.net.AnnotationKeys; +import org.onosproject.net.DeviceId; +import org.onosproject.net.behaviour.ControllerConfig; +import org.onosproject.net.behaviour.ControllerInfo; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.driver.AbstractHandlerBehaviour; +import org.onosproject.net.driver.DriverHandler; +import org.onosproject.ovsdb.controller.OvsdbBridge; +import org.onosproject.ovsdb.controller.OvsdbClientService; +import org.onosproject.ovsdb.controller.OvsdbController; +import org.onosproject.ovsdb.controller.OvsdbNodeId; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.google.common.base.Preconditions.checkState; +import static org.onlab.util.Tools.delay; + +/** + * Implementation of controller config which allows to get and set controllers. + */ +public class OvsdbControllerConfig extends AbstractHandlerBehaviour implements ControllerConfig { + @Override + public List<ControllerInfo> getControllers() { + DriverHandler handler = handler(); + OvsdbClientService clientService = getOvsdbClientService(handler); + Set<ControllerInfo> controllers = clientService.getControllers( + handler().data().deviceId()); + return new ArrayList<>(controllers); + } + + @Override + public void setControllers(List<ControllerInfo> controllers) { + DriverHandler handler = handler(); + OvsdbClientService clientService = getOvsdbClientService(handler); + if (!clientService.getControllers(handler().data().deviceId()) + .equals(controllers)) { + clientService.setControllersWithDeviceId(handler(). + data().deviceId(), controllers); + } + } + + // Used for getting OvsdbClientService. + private OvsdbClientService getOvsdbClientService(DriverHandler handler) { + OvsdbController ovsController = handler.get(OvsdbController.class); + DeviceService deviceService = handler.get(DeviceService.class); + DeviceId ofDeviceId = handler.data().deviceId(); + String[] mgmtAddress = deviceService.getDevice(ofDeviceId) + .annotations().value(AnnotationKeys.MANAGEMENT_ADDRESS).split(":"); + String targetIp = mgmtAddress[0]; + TpPort targetPort = null; + if (mgmtAddress.length > 1) { + targetPort = TpPort.tpPort(Integer.parseInt(mgmtAddress[1])); + } + + List<OvsdbNodeId> nodeIds = ovsController.getNodeIds().stream() + .filter(nodeId -> nodeId.getIpAddress().equals(targetIp)) + .collect(Collectors.toList()); + if (nodeIds.size() == 0) { + //TODO decide what port? + ovsController.connect(IpAddress.valueOf(targetIp), + targetPort == null ? TpPort.tpPort(6640) : targetPort); + delay(1000); //FIXME... connect is async + } + List<OvsdbClientService> clientServices = ovsController.getNodeIds().stream() + .filter(nodeId -> nodeId.getIpAddress().equals(targetIp)) + .map(ovsController::getOvsdbClient) + .filter(cs -> cs.getBridges().stream().anyMatch(b -> dpidMatches(b, ofDeviceId))) + .collect(Collectors.toList()); + checkState(clientServices.size() > 0, "No clientServices found"); + //FIXME add connection to management address if null --> done ? + return clientServices.size() > 0 ? clientServices.get(0) : null; + } + + private static boolean dpidMatches(OvsdbBridge bridge, DeviceId deviceId) { + String bridgeDpid = "of:" + bridge.datapathId().value(); + String ofDpid = deviceId.toString(); + return bridgeDpid.equals(ofDpid); + } +}
\ No newline at end of file diff --git a/framework/src/onos/drivers/src/main/resources/onos-drivers.xml b/framework/src/onos/drivers/src/main/resources/onos-drivers.xml index ac307c28..5059d4bf 100644 --- a/framework/src/onos/drivers/src/main/resources/onos-drivers.xml +++ b/framework/src/onos/drivers/src/main/resources/onos-drivers.xml @@ -30,6 +30,8 @@ manufacturer="Nicira, Inc\." hwVersion="Open vSwitch" swVersion="2\..*"> <behaviour api="org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver" impl="org.onosproject.driver.handshaker.NiciraSwitchHandshaker"/> + <behaviour api="org.onosproject.net.behaviour.ControllerConfig" + impl="org.onosproject.driver.ovsdb.OvsdbControllerConfig"/> </driver> <driver name="ovs-corsa" extends="ovs" manufacturer="Corsa" hwVersion="emulation" swVersion="0.0.0"> @@ -120,5 +122,10 @@ <behaviour api="org.onosproject.net.behaviour.Pipeliner" impl="org.onosproject.driver.pipeline.OpenVSwitchPipeline"/> </driver> + <driver name="eci" extends="default" + manufacturer="ECI Telecom" hwVersion="optical" swVersion="V_1_0"> + <behaviour api="org.onosproject.openflow.controller.driver.OpenFlowSwitchDriver" + impl="org.onosproject.driver.handshaker.OFOpticalSwitch13"/> + </driver> </drivers> diff --git a/framework/src/onos/drivers/src/test/java/org/onosproject/driver/ovsdb/OvsdbControllerConfigTest.java b/framework/src/onos/drivers/src/test/java/org/onosproject/driver/ovsdb/OvsdbControllerConfigTest.java new file mode 100644 index 00000000..4a91efcd --- /dev/null +++ b/framework/src/onos/drivers/src/test/java/org/onosproject/driver/ovsdb/OvsdbControllerConfigTest.java @@ -0,0 +1,95 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.driver.ovsdb; + +import com.google.common.collect.ImmutableMap; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.net.DeviceId; +import org.onosproject.net.behaviour.ControllerConfig; +import org.onosproject.net.device.DeviceServiceAdapter; +import org.onosproject.net.driver.DefaultDriver; +import org.onosproject.net.driver.DefaultDriverData; +import org.onosproject.net.driver.DefaultDriverHandler; +import org.onosproject.ovsdb.controller.driver.OvsdbClientServiceAdapter; +import org.onosproject.ovsdb.controller.driver.OvsdbControllerAdapter; + +/** + * Created by Andrea on 10/7/15. + */ +public class OvsdbControllerConfigTest { + + + private static final DeviceId DEVICE_ID = DeviceId.deviceId("foo"); + + private DefaultDriver ddc; + private DefaultDriverData data; + private DefaultDriverHandler handler; + + private TestDeviceService deviceService = new TestDeviceService(); + private TestOvsdbController controller = new TestOvsdbController(); + private TestOvsdbClient client = new TestOvsdbClient(); + + private OvsdbControllerConfig controllerConfig; + + + @Before + public void setUp() { + controllerConfig = new OvsdbControllerConfig(); + + ddc = new DefaultDriver("foo.bar", null, "Circus", "lux", "1.2a", + ImmutableMap.of(ControllerConfig.class, + OvsdbControllerConfig.class), + ImmutableMap.of("foo", "bar")); + data = new DefaultDriverData(ddc, DEVICE_ID); + handler = new DefaultDriverHandler(data); + //handler.controllerConfig.setHandler(handler); + //TODO setTestService directory on handler + //TODO setup ovsdb fake controller with fake ovsdbclient + //TODO setup fake device service + } + + @Test + public void testGetControllers() throws Exception { +// DriverService driverService = new Driv +// AbstractBehaviour ab = new AbstractBehaviour(); +// DriverHandler handler = handler(); +// List<ControllerInfo> controllersList = +// controllerConfig.getControllers(DeviceId.deviceId("0000000000000018")); +// log.info("controllers " + controllersList); + + } + + @Test + public void testSetControllers() throws Exception { + + } + + + private class TestDeviceService extends DeviceServiceAdapter { + + } + + private class TestOvsdbController extends OvsdbControllerAdapter { + + + } + + private class TestOvsdbClient extends OvsdbClientServiceAdapter { + + } +}
\ No newline at end of file diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/config/basics/InterfaceConfig.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/config/basics/InterfaceConfig.java index af2b47d8..592336c2 100644 --- a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/config/basics/InterfaceConfig.java +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/config/basics/InterfaceConfig.java @@ -17,13 +17,15 @@ package org.onosproject.incubator.net.config.basics; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.annotations.Beta; import com.google.common.collect.Sets; import org.onlab.packet.MacAddress; import org.onlab.packet.VlanId; -import org.onosproject.net.config.Config; import org.onosproject.incubator.net.intf.Interface; import org.onosproject.net.ConnectPoint; +import org.onosproject.net.config.Config; import org.onosproject.net.host.InterfaceIpAddress; import java.util.Set; @@ -37,7 +39,6 @@ public class InterfaceConfig extends Config<ConnectPoint> { public static final String MAC = "mac"; public static final String VLAN = "vlan"; - public static final String IP_MISSING_ERROR = "Must have at least one IP address"; public static final String MAC_MISSING_ERROR = "Must have a MAC address for each interface"; public static final String CONFIG_VALUE_ERROR = "Error parsing config value"; @@ -53,9 +54,6 @@ public class InterfaceConfig extends Config<ConnectPoint> { try { for (JsonNode intfNode : array) { Set<InterfaceIpAddress> ips = getIps(intfNode); - if (ips.isEmpty()) { - throw new ConfigException(IP_MISSING_ERROR); - } if (intfNode.path(MAC).isMissingNode()) { throw new ConfigException(MAC_MISSING_ERROR); @@ -63,10 +61,7 @@ public class InterfaceConfig extends Config<ConnectPoint> { MacAddress mac = MacAddress.valueOf(intfNode.path(MAC).asText()); - VlanId vlan = VlanId.NONE; - if (!intfNode.path(VLAN).isMissingNode()) { - vlan = VlanId.vlanId(Short.valueOf(intfNode.path(VLAN).asText())); - } + VlanId vlan = getVlan(intfNode); interfaces.add(new Interface(subject, ips, mac, vlan)); } @@ -77,13 +72,64 @@ public class InterfaceConfig extends Config<ConnectPoint> { return interfaces; } + /** + * Adds an interface to the config. + * + * @param intf interface to add + */ + public void addInterface(Interface intf) { + ObjectNode intfNode = array.addObject(); + intfNode.put(MAC, intf.mac().toString()); + + if (!intf.ipAddresses().isEmpty()) { + intfNode.set(IPS, putIps(intf.ipAddresses())); + } + + if (!intf.vlan().equals(VlanId.NONE)) { + intfNode.put(VLAN, intf.vlan().toString()); + } + } + + /** + * Removes an interface from the config. + * + * @param intf interface to remove + */ + public void removeInterface(Interface intf) { + for (int i = 0; i < array.size(); i++) { + if (intf.vlan().equals(getVlan(node))) { + array.remove(i); + break; + } + } + } + + private VlanId getVlan(JsonNode node) { + VlanId vlan = VlanId.NONE; + if (!node.path(VLAN).isMissingNode()) { + vlan = VlanId.vlanId(Short.valueOf(node.path(VLAN).asText())); + } + return vlan; + } + private Set<InterfaceIpAddress> getIps(JsonNode node) { Set<InterfaceIpAddress> ips = Sets.newHashSet(); JsonNode ipsNode = node.get(IPS); - ipsNode.forEach(jsonNode -> ips.add(InterfaceIpAddress.valueOf(jsonNode.asText()))); + if (ipsNode != null) { + ipsNode.forEach(jsonNode -> + ips.add(InterfaceIpAddress.valueOf(jsonNode.asText()))); + } return ips; } + private ArrayNode putIps(Set<InterfaceIpAddress> intfIpAddresses) { + ArrayNode ipArray = mapper.createArrayNode(); + + intfIpAddresses.forEach(i -> ipArray.add(i.toString())); + + return ipArray; + } + } diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/DomainIntentResource.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/DomainIntentResource.java new file mode 100644 index 00000000..ea1660e7 --- /dev/null +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/DomainIntentResource.java @@ -0,0 +1,84 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.incubator.net.domain; + +import org.onosproject.core.ApplicationId; +import org.onosproject.incubator.net.tunnel.DomainTunnelId; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.Path; + +/** + * A variant of intent resource specialized for use on the intra-domain level. It contains a lower level path. + */ +public class DomainIntentResource extends IntentResource { + + private final Path domainPath; + + private final DomainTunnelId domainTunnelId; + + private final IntentDomainId intentDomainId; + + /** + * Constructor for a domain intent resource. + * + * @param primitive the primitive associated with this resource + * @param domainTunnelId the id of this tunnel (used as a sorting mechanism) + * @param domainId the ID of the intent domain containing this tunnel + * @param appId the id of the application which created this tunnel + * @param ingress the fist connect point associated with this tunnel (order is irrelevant as long as it is + * consistent with the path) + * @param egress the second connect point associated with this tunnel (order is irrelevant as long as it is + * consistent with the path) + * @param path the path followed through the domain + */ + public DomainIntentResource(IntentPrimitive primitive, DomainTunnelId domainTunnelId, IntentDomainId domainId, + ApplicationId appId, ConnectPoint ingress, ConnectPoint egress, Path path) { + super(primitive, appId, ingress, egress); + + this.domainPath = path; + this.domainTunnelId = domainTunnelId; + this.intentDomainId = domainId; + } + + /** + * Returns the domain path associated with this resource at creation. + * + * @return this resource's domain level path or if this resource backs a network tunnel then null. + */ + public Path path() { + return domainPath; + } + + /** + * Returns the tunnel ID associated with this domain at creation. + * + * @return this resource's tunnel ID. + */ + public DomainTunnelId tunnelId() { + return domainTunnelId; + } + + /** + * Returns the domain ID associated with this resource at creation. + * + * @return this resource's domain ID. + */ + public IntentDomainId domainId() { + return intentDomainId; + } + +} diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/IntentDomainProvider.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/IntentDomainProvider.java index 51265f71..a19add60 100644 --- a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/IntentDomainProvider.java +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/IntentDomainProvider.java @@ -34,51 +34,51 @@ public interface IntentDomainProvider { * * @param domain intent domain for the request * @param primitive intent primitive - * @return request contexts that contain resources to satisfy the intent + * @return intent resources that specify paths that satisfy the request. */ //TODO Consider an iterable and/or holds (only hold one or two reservation(s) at a time) - List<RequestContext> request(IntentDomain domain, IntentPrimitive primitive); + List<DomainIntentResource> request(IntentDomain domain, IntentPrimitive primitive); /** * Request that the provider attempt to modify an existing resource to satisfy * a new intent primitive. The application must apply the context before * the intent resource can be used. * - * @param resource existing resource - * @param newPrimitive intent primitive + * @param oldResource the resource to be replaced + * @param newResource the resource to be applied * @return request contexts that contain resources to satisfy the intent */ - List<RequestContext> modify(IntentResource resource, IntentPrimitive newPrimitive); + DomainIntentResource modify(DomainIntentResource oldResource, DomainIntentResource newResource); /** * Requests that the provider release an intent resource. * * @param resource intent resource */ - void release(IntentResource resource); + void release(DomainIntentResource resource); /** - * Requests that the provider apply the intent resource in the request context. + * Requests that the provider apply the path from the intent resource. * - * @param context request context + * @param domainIntentResource request context * @return intent resource that satisfies the intent */ - IntentResource apply(RequestContext context); + DomainIntentResource apply(DomainIntentResource domainIntentResource); /** - * Requests that the provider cancel the request. Requests that are not applied + * Requests that the provider cancel the path. Requests that are not applied * will be eventually timed out by the provider. * - * @param context request context + * @param domainIntentResource the intent resource whose path should be cancelled. */ - void cancel(RequestContext context); + void cancel(DomainIntentResource domainIntentResource); /** * Returns all intent resources held by the provider. * * @return set of intent resources */ - Set<IntentResource> getResources(); + Set<DomainIntentResource> getResources(); } diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/IntentResource.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/IntentResource.java index 9cd9aac0..627c863f 100644 --- a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/IntentResource.java +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/IntentResource.java @@ -16,53 +16,73 @@ package org.onosproject.incubator.net.domain; import com.google.common.annotations.Beta; +import org.onosproject.core.ApplicationId; +import org.onosproject.net.ConnectPoint; + /** * The abstract base class for the resource that satisfies an intent primitive. */ @Beta -public class IntentResource { +public abstract class IntentResource { private final IntentPrimitive primitive; - private final long tunnelId; - private final IntentDomainId domainId; + + private final ApplicationId appId; + private final ConnectPoint ingress; + private final ConnectPoint egress; + + //* QUESTIONABLE ADDITIONS *// // TODO add other common fields //String ingressTag; //String egressTag; //etc. - public IntentResource(IntentPrimitive primitive, long tunnelId, IntentDomainId domainId) { + public IntentResource(IntentPrimitive primitive, ApplicationId appId, + ConnectPoint ingress, ConnectPoint egress) { + this.appId = appId; + this.ingress = ingress; + this.egress = egress; this.primitive = primitive; - this.tunnelId = tunnelId; - this.domainId = domainId; } + //TODO when is same package tunnelID should be of type tunnelID and netTunnelId not long. + + /** - * Returns the intent primitive associated with this resource as creation. + * Returns the intent primitive associated with this resource at creation. * - * @return this resource's intent primitive + * @return this resource's intent primitive. */ public IntentPrimitive primitive() { return primitive; } /** - * Returns the tunnel ID associated with this resource as creation. + * Returns the application ID associated with this resource at creation. * - * @return this resource's tunnel ID + * @return this resource's application ID. */ - public long tunnelId() { - return tunnelId; + public ApplicationId appId() { + return appId; } /** - * Returns the domain ID associated with this resource as creation. + * Returns the ingress connect point associated with this resource at creation. * - * @return this resource's domain ID + * @return this resource's ingress connect point. */ - public IntentDomainId domainId() { - return domainId; + public ConnectPoint ingress() { + return ingress; } + /** + * Returns the egress connect point associated with this resource at creation. + * + * @return this resource's connect point. + */ + public ConnectPoint egress() { + return egress; + } } diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/NetworkIntentResource.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/NetworkIntentResource.java new file mode 100644 index 00000000..ac4445b4 --- /dev/null +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/domain/NetworkIntentResource.java @@ -0,0 +1,70 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.incubator.net.domain; + +import org.onosproject.core.ApplicationId; +import org.onosproject.incubator.net.tunnel.NetworkTunnelId; +import org.onosproject.net.ConnectPoint; + +/** + * A variant of intent resource specialized for use on the inter-domain level. It contains a higher level path. + */ +public class NetworkIntentResource extends IntentResource { + + private final org.onlab.graph.Path<DomainVertex, DomainEdge> netPath; + + private NetworkTunnelId networkTunnelId; + + /** + * Constructor for a network intent resource. + * + * @param primitive the primitive associated with this resource + * @param networkTunnelId the id of this tunnel (used as a sorting mechanism) + * @param appId the id of the application which created this tunnel + * @param ingress the fist connect point associated with this tunnel (order is irrelevant as long as it is + * consistent with the path) + * @param egress the second connect point associated with this tunnel (order is irrelevant as long as it is + * consistent with the path) + * @param path the path followed through the graph of domain vertices and domain edges + */ + public NetworkIntentResource(IntentPrimitive primitive, NetworkTunnelId networkTunnelId, ApplicationId appId, + ConnectPoint ingress, ConnectPoint egress, + org.onlab.graph.Path<DomainVertex, DomainEdge> path) { + super(primitive, appId, ingress, egress); + + this.networkTunnelId = networkTunnelId; + this.netPath = path; + } + + /** + * Returns the network path associated with this resource at creation. + * + * @return this resource's network lever path or if this resource backs a domain level tunnel then null. + */ + public org.onlab.graph.Path<DomainVertex, DomainEdge> path() { + return netPath; + } + + /** + * Returns ths network ID associated with this network tunnel at creation. + * + * @return thsi resource's tunnel ID. + */ + public NetworkTunnelId tunnelId() { + return this.networkTunnelId; + } +} diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/intf/InterfaceAdminService.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/intf/InterfaceAdminService.java new file mode 100644 index 00000000..56d5aecc --- /dev/null +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/intf/InterfaceAdminService.java @@ -0,0 +1,40 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.incubator.net.intf; + +import org.onlab.packet.VlanId; +import org.onosproject.net.ConnectPoint; + +/** + * Provides a means to modify the interfaces configuration. + */ +public interface InterfaceAdminService { + /** + * Adds a new interface configuration to the system. + * + * @param intf interface to add + */ + void add(Interface intf); + + /** + * Removes an interface configuration from the system. + * + * @param connectPoint connect point of the interface + * @param vlanId vlan id + */ + void remove(ConnectPoint connectPoint, VlanId vlanId); +} diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/tunnel/DomainTunnelId.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/tunnel/DomainTunnelId.java new file mode 100644 index 00000000..430823ca --- /dev/null +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/tunnel/DomainTunnelId.java @@ -0,0 +1,92 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.incubator.net.tunnel; + +/** + * A wrapper class for a long used to identify domain level tunnels. + */ +public final class DomainTunnelId { + + private final long value; + + /** + * Creates a tunnel identifier from the specified tunnel. + * + * @param value long value + * @return domain tunnel identifier + */ + public static DomainTunnelId valueOf(long value) { + return new DomainTunnelId(value); + } + + /** + * Creates a tunnel identifier from the specified tunnel. + * + * @param value long value as a string + * @return domain tunnel identifier + */ + public static DomainTunnelId valueOf(String value) { + return new DomainTunnelId(Long.parseLong(value)); + } + + /** + * Constructor for serializer. + */ + protected DomainTunnelId() { + this.value = 0; + } + + /** + * Constructs the Domain ID corresponding to a given long value. + * + * @param value the underlying value of this domain ID + */ + public DomainTunnelId(long value) { + this.value = value; + } + + /** + * Returns the backing value of this domain ID. + * + * @return the long value + */ + public long id() { + return value; + } + + @Override + public int hashCode() { + return Long.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof DomainTunnelId)) { + return false; + } + DomainTunnelId that = (DomainTunnelId) obj; + return this.value == that.value; + } + + @Override + public String toString() { + return "0x" + Long.toHexString(value); + } +} diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/tunnel/NetworkTunnelId.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/tunnel/NetworkTunnelId.java new file mode 100644 index 00000000..a3de7883 --- /dev/null +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/tunnel/NetworkTunnelId.java @@ -0,0 +1,89 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.incubator.net.tunnel; + +import com.google.common.annotations.Beta; + +/** + * Representation of a Network Tunnel Id. + */ +@Beta +public final class NetworkTunnelId { + private final long value; + + /** + * Creates an tunnel identifier from the specified tunnel. + * + * @param value long value + * @return tunnel identifier + */ + public static NetworkTunnelId valueOf(long value) { + return new NetworkTunnelId(value); + } + + public static NetworkTunnelId valueOf(String value) { + return new NetworkTunnelId(Long.parseLong(value)); + } + + /** + * Constructor for serializer. + */ + NetworkTunnelId() { + this.value = 0; + } + + /** + * Constructs the ID corresponding to a given long value. + * + * @param value the underlying value of this ID + */ + public NetworkTunnelId(long value) { + this.value = value; + } + + /** + * Returns the backing value. + * + * @return the value + */ + public long id() { + return value; + } + + @Override + public int hashCode() { + return Long.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof NetworkTunnelId)) { + return false; + } + NetworkTunnelId that = (NetworkTunnelId) obj; + return this.value == that.value; + } + + @Override + public String toString() { + return "0x" + Long.toHexString(value); + } + +} diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualDevice.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualDevice.java new file mode 100644 index 00000000..54a22a46 --- /dev/null +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualDevice.java @@ -0,0 +1,75 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.incubator.net.virtual; + +import org.onlab.packet.ChassisId; +import org.onosproject.net.DefaultDevice; +import org.onosproject.net.DeviceId; +import org.onosproject.net.provider.ProviderId; + +import java.util.Objects; + +import static com.google.common.base.MoreObjects.*; + +/** + * Default representation of a virtual device. + */ +public class DefaultVirtualDevice extends DefaultDevice implements VirtualDevice { + + private static final String VIRTUAL = "virtual"; + private static final ProviderId PID = new ProviderId(VIRTUAL, VIRTUAL); + + private final NetworkId networkId; + + /** + * Creates a network element attributed to the specified provider. + * + * @param networkId network identifier + * @param id device identifier + */ + public DefaultVirtualDevice(NetworkId networkId, DeviceId id) { + super(PID, id, Type.VIRTUAL, VIRTUAL, VIRTUAL, VIRTUAL, VIRTUAL, + new ChassisId(0)); + this.networkId = networkId; + } + + @Override + public NetworkId networkId() { + return networkId; + } + + @Override + public int hashCode() { + return 31 * super.hashCode() + Objects.hash(networkId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DefaultVirtualDevice) { + DefaultVirtualDevice that = (DefaultVirtualDevice) obj; + return super.equals(that) && Objects.equals(this.networkId, that.networkId); + } + return false; + } + + @Override + public String toString() { + return toStringHelper(this).add("networkId", networkId).toString(); + } +} diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetwork.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetwork.java new file mode 100644 index 00000000..c1141912 --- /dev/null +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/DefaultVirtualNetwork.java @@ -0,0 +1,76 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.incubator.net.virtual; + +import java.util.Objects; + +import static com.google.common.base.MoreObjects.toStringHelper; + +/** + * Default implementation of the virtual network descriptor. + */ +public class DefaultVirtualNetwork implements VirtualNetwork { + + private final NetworkId id; + private final TenantId tenantId; + + /** + * Creates a new virtual network descriptor. + * + * @param id network identifier + * @param tenantId tenant identifier + */ + public DefaultVirtualNetwork(NetworkId id, TenantId tenantId) { + this.id = id; + this.tenantId = tenantId; + } + + @Override + public NetworkId id() { + return id; + } + + @Override + public TenantId tenantId() { + return tenantId; + } + + @Override + public int hashCode() { + return Objects.hash(id, tenantId); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof DefaultVirtualNetwork) { + DefaultVirtualNetwork that = (DefaultVirtualNetwork) obj; + return Objects.equals(this.id, that.id) + && Objects.equals(this.tenantId, that.tenantId); + } + return false; + } + + @Override + public String toString() { + return toStringHelper(this) + .add("id", id) + .add("tenantId", tenantId) + .toString(); + } +} diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualElement.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualElement.java index 1c74b92a..791b8e24 100644 --- a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualElement.java +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualElement.java @@ -24,13 +24,6 @@ import com.google.common.annotations.Beta; public interface VirtualElement { /** - * Returns the identifier of the tenant to which this virtual element belongs. - * - * @return tenant identifier - */ - TenantId tenantId(); - - /** * Returns the network identifier to which this virtual element belongs. * * @return network identifier diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkAdminService.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkAdminService.java index 1e3648b5..07c399c0 100644 --- a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkAdminService.java +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkAdminService.java @@ -16,14 +16,11 @@ package org.onosproject.incubator.net.virtual; import com.google.common.annotations.Beta; -import org.onosproject.incubator.net.tunnel.Tunnel; +import org.onosproject.incubator.net.tunnel.TunnelId; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; import org.onosproject.net.Port; import org.onosproject.net.PortNumber; -import org.onosproject.net.device.DeviceDescription; -import org.onosproject.net.device.PortDescription; -import org.onosproject.net.link.LinkDescription; import java.util.Set; @@ -76,12 +73,12 @@ public interface VirtualNetworkAdminService extends VirtualNetworkService { * Creates a new virtual device within the specified network. The device id * must be unique within the bounds of the network. * - * @param networkId network identifier - * @param description device description + * @param networkId network identifier + * @param deviceId device identifier * @return newly created device * @throws org.onlab.util.ItemNotFoundException if no such network found */ - VirtualDevice createVirtualDevice(NetworkId networkId, DeviceDescription description); + VirtualDevice createVirtualDevice(NetworkId networkId, DeviceId deviceId); /** * Removes the specified virtual device and all its ports and affiliated links. @@ -96,14 +93,16 @@ public interface VirtualNetworkAdminService extends VirtualNetworkService { /** * Creates a new virtual link within the specified network. * - * @param networkId network identifier - * @param description link description - * @param realizedBy tunnel using which this link is realized + * @param networkId network identifier + * @param src source connection point + * @param dst destination connection point + * @param realizedBy identifier of the tunnel using which this link is realized * @return newly created virtual link * @throws org.onlab.util.ItemNotFoundException if no such network found */ - VirtualLink createVirtualLink(NetworkId networkId, LinkDescription description, - Tunnel realizedBy); + VirtualLink createVirtualLink(NetworkId networkId, + ConnectPoint src, ConnectPoint dst, + TunnelId realizedBy); // TODO: Discuss whether we should provide an alternate createVirtualLink // which is backed by a Path instead; I'm leaning towards not doing that. @@ -119,20 +118,17 @@ public interface VirtualNetworkAdminService extends VirtualNetworkService { void removeVirtualLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst); /** - * Creates a new virtual port on the specified device. Note that the port - * description can only request the resources which the underlying port - * port is capable of providing. It is, however, permissible to request - * only portion of those resources. + * Creates a new virtual port on the specified device. * - * @param networkId network identifier - * @param deviceId device identifier - * @param description port description - * @param realizedBy underlying port using which this virtual port is realized + * @param networkId network identifier + * @param deviceId device identifier + * @param portNumber port number + * @param realizedBy underlying port using which this virtual port is realized * @return newly created port * @throws org.onlab.util.ItemNotFoundException if no such network or device found */ VirtualPort createVirtualPort(NetworkId networkId, DeviceId deviceId, - PortDescription description, Port realizedBy); + PortNumber portNumber, Port realizedBy); /** * Removes the specified virtual port. diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkEvent.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkEvent.java new file mode 100644 index 00000000..7e076e09 --- /dev/null +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkEvent.java @@ -0,0 +1,72 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.incubator.net.virtual; + +import org.onosproject.event.AbstractEvent; + +/** + * Describes virtual network event. + */ +public class VirtualNetworkEvent extends AbstractEvent<VirtualNetworkEvent.Type, NetworkId> { + + /** + * Type of virtual network events. + */ + public enum Type { + /** + * Signifies that a new tenant identifier was registered. + */ + TENANT_REGISTERED, + /** + * Signifies that a tenant identifier was unregistered. + */ + TENANT_UNREGISTERED, + /** + * Signifies that a new virtual network was added. + */ + NETWORK_ADDED, + /** + * Signifies that a virtual network was updated. + */ + NETWORK_UPDATED, + /** + * Signifies that a virtual network was removed. + */ + NETWORK_REMOVED + } + + /** + * Creates an event of a given type and for the specified subject and the + * current time. + * + * @param type event type + * @param subject event subject + */ + public VirtualNetworkEvent(Type type, NetworkId subject) { + super(type, subject); + } + + /** + * Creates an event of a given type and for the specified subject and time. + * + * @param type device event type + * @param subject event subject + * @param time occurrence time + */ + public VirtualNetworkEvent(Type type, NetworkId subject, long time) { + super(type, subject, time); + } +} diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkListener.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkListener.java new file mode 100644 index 00000000..707ca8a7 --- /dev/null +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkListener.java @@ -0,0 +1,24 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.incubator.net.virtual; + +import org.onosproject.event.EventListener; + +/** + * Represents entity capable of receiving virtual network events. + */ +public interface VirtualNetworkListener extends EventListener<VirtualNetworkEvent> { +} diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProvider.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProvider.java new file mode 100644 index 00000000..8e5b19a5 --- /dev/null +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProvider.java @@ -0,0 +1,31 @@ +package org.onosproject.incubator.net.virtual; + +import org.onosproject.incubator.net.tunnel.TunnelId; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.provider.Provider; + +/** + * Entity capable of providing traffic isolation constructs for use in + * implementation of virtual devices and virtual links. + */ +public interface VirtualNetworkProvider extends Provider { + + /** + * Creates a network tunnel for all traffic from the specified source + * connection point to the indicated destination connection point. + * + * @param networkId virtual network identifier + * @param src source connection point + * @param dst destination connection point + */ + TunnelId createTunnel(NetworkId networkId, ConnectPoint src, ConnectPoint dst); + + /** + * Destroys the specified network tunnel. + * + * @param networkId virtual network identifier + * @param tunnelId tunnel identifier + */ + void destroyTunnel(NetworkId networkId, TunnelId tunnelId); + +} diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProviderRegistry.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProviderRegistry.java new file mode 100644 index 00000000..4e893165 --- /dev/null +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProviderRegistry.java @@ -0,0 +1,25 @@ +/* + * Copyright 2014 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.incubator.net.virtual; + +import org.onosproject.net.provider.ProviderRegistry; + +/** + * Abstraction of a virtual network provider registry. + */ +public interface VirtualNetworkProviderRegistry + extends ProviderRegistry<VirtualNetworkProvider, VirtualNetworkProviderService> { +} diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProviderService.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProviderService.java new file mode 100644 index 00000000..cba933c9 --- /dev/null +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkProviderService.java @@ -0,0 +1,11 @@ +package org.onosproject.incubator.net.virtual; + +import org.onosproject.net.provider.ProviderService; + +/** + * Service through which virtual network providers can inject information into + * the core. + */ +public interface VirtualNetworkProviderService extends ProviderService<VirtualNetworkProvider> { + // TODO: Add methods for notification of core about damaged tunnels, etc. +} diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java new file mode 100644 index 00000000..49ad2f2b --- /dev/null +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStore.java @@ -0,0 +1,161 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.incubator.net.virtual; + +import org.onosproject.incubator.net.tunnel.TunnelId; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; +import org.onosproject.store.Store; + +import java.util.Set; + +/** + * Mechanism for distributing and storing virtual network model information. + */ +public interface VirtualNetworkStore + extends Store<VirtualNetworkEvent, VirtualNetworkStoreDelegate> { + + /** + * Adds a new tenant ID to the store. + * + * @param tenantId tenant identifier + */ + void addTenantId(TenantId tenantId); + + /** + * Removes the specified tenant ID from the store. + * + * @param tenantId tenant identifier + */ + void removeTenantId(TenantId tenantId); + + /** + * Returns set of registered tenant IDs. + * + * @return set of tenant identifiers + */ + Set<TenantId> getTenantIds(); + + /** + * Adds a new virtual network for the specified tenant to the store. + * + * @param tenantId tenant identifier + * @return the virtual network + */ + VirtualNetwork addNetwork(TenantId tenantId); + + /** + * Removes the specified virtual network from the store. + * + * @param networkId network identifier + */ + void removeNetwork(NetworkId networkId); + + /** + * Adds a new virtual device to the store. This device will have no ports. + * + * @param networkId network identifier + * @param deviceId device identifier + * @return the virtual device + */ + VirtualDevice addDevice(NetworkId networkId, DeviceId deviceId); + + /** + * Renmoves the specified virtual device from the given network. + * + * @param networkId network identifier + * @param deviceId device identifier + */ + void removeDevice(NetworkId networkId, DeviceId deviceId); + + /** + * Adds a new virtual link. + * + * @param networkId network identifier + * @param src source end-point of the link + * @param dst destination end-point of the link + * @param realizedBy underlying tunnel using which this link is realized + * @return the virtual link + */ + VirtualLink addLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst, + TunnelId realizedBy); + + /** + * Removes the specified link from the store. + * + * @param networkId network identifier + * @param src source connection point + * @param dst destination connection point + */ + void removeLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst); + + /** + * Adds a new virtual port to the network. + * + * @param networkId network identifier + * @param deviceId device identifier + * @param portNumber port number + * @param realizedBy underlying port which realizes the virtual port + * @return the virtual port + */ + VirtualPort addPort(NetworkId networkId, DeviceId deviceId, + PortNumber portNumber, Port realizedBy); + + /** + * Removes the specified port from the given device and network. + * + * @param networkId network identifier + * @param deviceId device identifier + * @param portNumber port number + */ + void removePort(NetworkId networkId, DeviceId deviceId, PortNumber portNumber); + + /** + * Returns the list of networks. + * + * @param tenantId tenant identifier + * @return set of virtual networks + */ + Set<VirtualNetwork> getNetworks(TenantId tenantId); + + /** + * Returns the list of devices in the specified virtual network. + * + * @param networkId network identifier + * @return set of virtual devices + */ + Set<VirtualDevice> getDevices(NetworkId networkId); + + /** + * Returns the list of virtual links in the specified virtual network. + * + * @param networkId network identifier + * @return set of virtual links + */ + Set<VirtualLink> getLinks(NetworkId networkId); + + /** + * Returns the list of ports of the specified virtual device. + * + * @param networkId network identifier + * @param deviceId device identifier + * @return set of virtual networks + */ + Set<VirtualPort> getPorts(NetworkId networkId, DeviceId deviceId); + +} diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStoreDelegate.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStoreDelegate.java new file mode 100644 index 00000000..e57c3d3a --- /dev/null +++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/virtual/VirtualNetworkStoreDelegate.java @@ -0,0 +1,24 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.incubator.net.virtual; + +import org.onosproject.store.StoreDelegate; + +/** + * Network configuration store delegate abstraction. + */ +public interface VirtualNetworkStoreDelegate extends StoreDelegate<VirtualNetworkEvent> { +} diff --git a/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/intf/impl/InterfaceManager.java b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/intf/impl/InterfaceManager.java index f82cdbf2..0439d038 100644 --- a/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/intf/impl/InterfaceManager.java +++ b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/intf/impl/InterfaceManager.java @@ -29,6 +29,7 @@ import org.onlab.packet.VlanId; import org.onosproject.incubator.net.config.basics.ConfigException; import org.onosproject.incubator.net.config.basics.InterfaceConfig; import org.onosproject.incubator.net.intf.Interface; +import org.onosproject.incubator.net.intf.InterfaceAdminService; import org.onosproject.incubator.net.intf.InterfaceService; import org.onosproject.net.ConnectPoint; import org.onosproject.net.config.NetworkConfigEvent; @@ -50,7 +51,8 @@ import static java.util.stream.Collectors.toSet; */ @Service @Component(immediate = true) -public class InterfaceManager implements InterfaceService { +public class InterfaceManager implements InterfaceService, + InterfaceAdminService { private final Logger log = LoggerFactory.getLogger(getClass()); @@ -153,6 +155,54 @@ public class InterfaceManager implements InterfaceService { interfaces.remove(port); } + @Override + public void add(Interface intf) { + if (interfaces.containsKey(intf.connectPoint())) { + boolean conflict = interfaces.get(intf.connectPoint()).stream() + .filter(i -> i.connectPoint().equals(intf.connectPoint())) + .filter(i -> i.mac().equals(intf.mac())) + .filter(i -> i.vlan().equals(intf.vlan())) + .findAny().isPresent(); + + if (conflict) { + log.error("Can't add interface because it conflicts with existing config"); + return; + } + } + + InterfaceConfig config = + configService.addConfig(intf.connectPoint(), CONFIG_CLASS); + + config.addInterface(intf); + + configService.applyConfig(intf.connectPoint(), CONFIG_CLASS, config.node()); + } + + @Override + public void remove(ConnectPoint connectPoint, VlanId vlanId) { + Optional<Interface> intf = interfaces.get(connectPoint).stream() + .filter(i -> i.vlan().equals(vlanId)) + .findAny(); + + if (!intf.isPresent()) { + log.error("Can't find interface {}/{} to remove", connectPoint, vlanId); + return; + } + + InterfaceConfig config = configService.addConfig(intf.get().connectPoint(), CONFIG_CLASS); + config.removeInterface(intf.get()); + + try { + if (config.getInterfaces().isEmpty()) { + configService.removeConfig(connectPoint, CONFIG_CLASS); + } else { + configService.applyConfig(intf.get().connectPoint(), CONFIG_CLASS, config.node()); + } + } catch (ConfigException e) { + log.error("Error reading interfaces JSON", e); + } + } + /** * Listener for network config events. */ diff --git a/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/MulticastData.java b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/MulticastData.java new file mode 100644 index 00000000..946d8c6e --- /dev/null +++ b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/MulticastData.java @@ -0,0 +1,85 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.incubator.net.mcast.impl; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import org.onosproject.net.ConnectPoint; + +import java.util.Collections; +import java.util.List; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Simple entity maintaining a mapping between a source and a collection of sink + * connect points. + */ +public final class MulticastData { + + private final ConnectPoint source; + private final List<ConnectPoint> sinks; + private final boolean isEmpty; + + private MulticastData() { + this.source = null; + this.sinks = Collections.EMPTY_LIST; + isEmpty = true; + } + + public MulticastData(ConnectPoint source, List<ConnectPoint> sinks) { + this.source = checkNotNull(source, "Multicast source cannot be null."); + this.sinks = checkNotNull(sinks, "List of sinks cannot be null."); + isEmpty = false; + } + + public MulticastData(ConnectPoint source, ConnectPoint sink) { + this.source = checkNotNull(source, "Multicast source cannot be null."); + this.sinks = Lists.newArrayList(checkNotNull(sink, "Sink cannot be null.")); + isEmpty = false; + } + + public MulticastData(ConnectPoint source) { + this.source = checkNotNull(source, "Multicast source cannot be null."); + this.sinks = Lists.newArrayList(); + isEmpty = false; + } + + public ConnectPoint source() { + return source; + } + + public List<ConnectPoint> sinks() { + return ImmutableList.copyOf(sinks); + } + + public void appendSink(ConnectPoint sink) { + sinks.add(sink); + } + + public boolean removeSink(ConnectPoint sink) { + return sinks.remove(sink); + } + + public boolean isEmpty() { + return isEmpty; + } + + public static MulticastData empty() { + return new MulticastData(); + } + +} diff --git a/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/MulticastRouteManager.java b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/MulticastRouteManager.java new file mode 100644 index 00000000..f73dfe44 --- /dev/null +++ b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/MulticastRouteManager.java @@ -0,0 +1,174 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.incubator.net.mcast.impl; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.Service; +import org.onlab.packet.IpPrefix; +import org.onlab.util.KryoNamespace; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.event.AbstractListenerManager; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.mcast.McastEvent; +import org.onosproject.net.mcast.McastListener; +import org.onosproject.net.mcast.McastRoute; +import org.onosproject.net.mcast.MulticastRouteService; +import org.onosproject.store.service.ConsistentMap; +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.List; +import java.util.concurrent.atomic.AtomicReference; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * An implementation of a multicast route table. + */ +@Component(immediate = true) +@Service +public class MulticastRouteManager + extends AbstractListenerManager<McastEvent, McastListener> + implements MulticastRouteService { + //TODO: add MulticastRouteAdminService + + private static final String MCASTRIB = "mcast-rib-table"; + + private Logger log = getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + private StorageService storageService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + private CoreService coreService; + + + protected ApplicationId appId; + protected ConsistentMap<McastRoute, MulticastData> mcastRoutes; + + @Activate + public void activate() { + + eventDispatcher.addSink(McastEvent.class, listenerRegistry); + + appId = coreService.registerApplication("org.onosproject.mcastrib"); + + mcastRoutes = storageService.<McastRoute, MulticastData>consistentMapBuilder() + .withApplicationId(appId) + .withName(MCASTRIB) + .withSerializer(Serializer.using(KryoNamespace.newBuilder().register( + MulticastData.class, + McastRoute.class, + McastRoute.Type.class, + IpPrefix.class, + List.class, + ConnectPoint.class + ).build())).build(); + + log.info("Started"); + } + + @Deactivate + public void deactivate() { + log.info("Stopped"); + } + + @Override + public void add(McastRoute route) { + mcastRoutes.put(route, MulticastData.empty()); + post(new McastEvent(McastEvent.Type.ROUTE_ADDED, route, null, null)); + } + + @Override + public void remove(McastRoute route) { + mcastRoutes.remove(route); + post(new McastEvent(McastEvent.Type.ROUTE_REMOVED, route, null, null)); + } + + @Override + public void addSource(McastRoute route, ConnectPoint connectPoint) { + Versioned<MulticastData> d = mcastRoutes.compute(route, (k, v) -> { + if (v.isEmpty()) { + return new MulticastData(connectPoint); + } else { + log.warn("Route {} is already in use.", route); + return v; + } + }); + + if (d != null) { + post(new McastEvent(McastEvent.Type.SOURCE_ADDED, + route, null, connectPoint)); + } + } + + @Override + public void addSink(McastRoute route, ConnectPoint connectPoint) { + AtomicReference<ConnectPoint> source = new AtomicReference<>(); + mcastRoutes.compute(route, (k, v) -> { + if (!v.isEmpty()) { + v.appendSink(connectPoint); + source.set(v.source()); + } else { + log.warn("Route {} does not exist"); + } + return v; + }); + + if (source.get() != null) { + post(new McastEvent(McastEvent.Type.SINK_ADDED, route, + connectPoint, source.get())); + } + } + + + @Override + public void removeSink(McastRoute route, ConnectPoint connectPoint) { + AtomicReference<ConnectPoint> source = new AtomicReference<>(); + mcastRoutes.compute(route, (k, v) -> { + if (v.removeSink(connectPoint)) { + source.set(v.source()); + } + return v; + }); + + if (source.get() != null) { + post(new McastEvent(McastEvent.Type.SINK_REMOVED, route, + connectPoint, source.get())); + } + } + + @Override + public ConnectPoint fetchSource(McastRoute route) { + MulticastData d = mcastRoutes.asJavaMap().getOrDefault(route, + MulticastData.empty()); + return d.source(); + } + + @Override + public List<ConnectPoint> fetchSinks(McastRoute route) { + MulticastData d = mcastRoutes.asJavaMap().getOrDefault(route, + MulticastData.empty()); + return d.sinks(); + } +} diff --git a/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/package-info.java b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/package-info.java new file mode 100644 index 00000000..464cf701 --- /dev/null +++ b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/mcast/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * An implementation of a multicast RIB. + */ +package org.onosproject.incubator.net.mcast.impl;
\ No newline at end of file diff --git a/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/meter/impl/MeterManager.java b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/meter/impl/MeterManager.java index 575a7153..5c5c11cd 100644 --- a/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/meter/impl/MeterManager.java +++ b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/meter/impl/MeterManager.java @@ -15,6 +15,7 @@ */ package org.onosproject.incubator.net.meter.impl; +import com.google.common.collect.Maps; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; @@ -27,6 +28,7 @@ import org.onosproject.net.meter.Meter; import org.onosproject.net.meter.MeterEvent; import org.onosproject.net.meter.MeterFailReason; import org.onosproject.net.meter.MeterId; +import org.onosproject.net.meter.MeterKey; import org.onosproject.net.meter.MeterListener; import org.onosproject.net.meter.MeterOperation; import org.onosproject.net.meter.MeterProvider; @@ -61,7 +63,7 @@ public class MeterManager extends AbstractListenerProviderRegistry<MeterEvent, M MeterProvider, MeterProviderService> implements MeterService, MeterProviderRegistry { - private final String meterIdentifier = "meter-id-counter"; + private static final String METERCOUNTERIDENTIFIER = "meter-id-counter-%s"; private final Logger log = getLogger(getClass()); private final MeterStoreDelegate delegate = new InternalMeterStoreDelegate(); @@ -71,15 +73,13 @@ public class MeterManager extends AbstractListenerProviderRegistry<MeterEvent, M @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected MeterStore store; - private AtomicCounter meterIdCounter; + private Map<DeviceId, AtomicCounter> meterIdCounters + = Maps.newConcurrentMap(); private TriConsumer<MeterRequest, MeterStoreResult, Throwable> onComplete; @Activate public void activate() { - meterIdCounter = storageService.atomicCounterBuilder() - .withName(meterIdentifier) - .build(); store.setDelegate(delegate); @@ -115,11 +115,13 @@ public class MeterManager extends AbstractListenerProviderRegistry<MeterEvent, M @Override public Meter submit(MeterRequest request) { + MeterId id = allocateMeterId(request.deviceId()); + Meter.Builder mBuilder = DefaultMeter.builder() .forDevice(request.deviceId()) .fromApp(request.appId()) .withBands(request.bands()) - .withId(allocateMeterId()) + .withId(id) .withUnit(request.unit()); if (request.isBurst()) { @@ -152,8 +154,9 @@ public class MeterManager extends AbstractListenerProviderRegistry<MeterEvent, M } @Override - public Meter getMeter(MeterId id) { - return store.getMeter(id); + public Meter getMeter(DeviceId deviceId, MeterId id) { + MeterKey key = MeterKey.key(deviceId, id); + return store.getMeter(key); } @Override @@ -161,9 +164,21 @@ public class MeterManager extends AbstractListenerProviderRegistry<MeterEvent, M return store.getAllMeters(); } - private MeterId allocateMeterId() { - // FIXME: This will break one day. - return MeterId.meterId((int) meterIdCounter.incrementAndGet()); + private MeterId allocateMeterId(DeviceId deviceId) { + long id = meterIdCounters.compute(deviceId, (k, v) -> { + if (v == null) { + return allocateCounter(k); + } + return v; + }).incrementAndGet(); + + return MeterId.meterId((int) id); + } + + private AtomicCounter allocateCounter(DeviceId deviceId) { + return storageService.atomicCounterBuilder() + .withName(String.format(METERCOUNTERIDENTIFIER, deviceId)) + .build(); } private class InternalMeterProviderService diff --git a/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java new file mode 100644 index 00000000..fe9f8841 --- /dev/null +++ b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/VirtualNetworkManager.java @@ -0,0 +1,223 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.incubator.net.virtual.impl; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.Service; +import org.onosproject.incubator.net.tunnel.TunnelId; +import org.onosproject.incubator.net.virtual.NetworkId; +import org.onosproject.incubator.net.virtual.TenantId; +import org.onosproject.incubator.net.virtual.VirtualDevice; +import org.onosproject.incubator.net.virtual.VirtualLink; +import org.onosproject.incubator.net.virtual.VirtualNetwork; +import org.onosproject.incubator.net.virtual.VirtualNetworkAdminService; +import org.onosproject.incubator.net.virtual.VirtualNetworkEvent; +import org.onosproject.incubator.net.virtual.VirtualNetworkListener; +import org.onosproject.incubator.net.virtual.VirtualNetworkProvider; +import org.onosproject.incubator.net.virtual.VirtualNetworkProviderRegistry; +import org.onosproject.incubator.net.virtual.VirtualNetworkProviderService; +import org.onosproject.incubator.net.virtual.VirtualNetworkService; +import org.onosproject.incubator.net.virtual.VirtualNetworkStore; +import org.onosproject.incubator.net.virtual.VirtualNetworkStoreDelegate; +import org.onosproject.incubator.net.virtual.VirtualPort; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; +import org.onosproject.net.provider.AbstractListenerProviderRegistry; +import org.onosproject.net.provider.AbstractProviderService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; + +import static com.google.common.base.Preconditions.checkNotNull; + +/** + * Implementation of the virtual network service. + */ +@Component(immediate = true) +@Service +public class VirtualNetworkManager + extends AbstractListenerProviderRegistry<VirtualNetworkEvent, VirtualNetworkListener, + VirtualNetworkProvider, VirtualNetworkProviderService> + implements VirtualNetworkService, VirtualNetworkAdminService, VirtualNetworkProviderRegistry { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private static final String TENANT_NULL = "Tenant ID cannot be null"; + private static final String NETWORK_NULL = "Network ID cannot be null"; + private static final String DEVICE_NULL = "Device ID cannot be null"; + private static final String LINK_POINT_NULL = "Link end-point cannot be null"; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected VirtualNetworkStore store; + + private VirtualNetworkStoreDelegate delegate = new InternalStoreDelegate(); + + // TODO: figure out how to coordinate "implementation" of a virtual network in a cluster + + @Activate + protected void activate() { + store.setDelegate(delegate); + log.info("Started"); + } + + @Deactivate + protected void deactivate() { + store.unsetDelegate(delegate); + log.info("Stopped"); + } + + @Override + public void registerTenantId(TenantId tenantId) { + checkNotNull(tenantId, TENANT_NULL); + store.addTenantId(tenantId); + } + + @Override + public void unregisterTenantId(TenantId tenantId) { + checkNotNull(tenantId, TENANT_NULL); + store.removeTenantId(tenantId); + } + + @Override + public Set<TenantId> getTenantIds() { + return store.getTenantIds(); + } + + @Override + public VirtualNetwork createVirtualNetwork(TenantId tenantId) { + checkNotNull(tenantId, TENANT_NULL); + return store.addNetwork(tenantId); + } + + @Override + public void removeVirtualNetwork(NetworkId networkId) { + checkNotNull(networkId, NETWORK_NULL); + store.removeNetwork(networkId); + } + + @Override + public VirtualDevice createVirtualDevice(NetworkId networkId, DeviceId deviceId) { + checkNotNull(networkId, NETWORK_NULL); + checkNotNull(deviceId, DEVICE_NULL); + return store.addDevice(networkId, deviceId); + } + + @Override + public void removeVirtualDevice(NetworkId networkId, DeviceId deviceId) { + checkNotNull(networkId, NETWORK_NULL); + checkNotNull(deviceId, DEVICE_NULL); + store.removeDevice(networkId, deviceId); + } + + @Override + public VirtualLink createVirtualLink(NetworkId networkId, + ConnectPoint src, ConnectPoint dst, + TunnelId realizedBy) { + checkNotNull(networkId, NETWORK_NULL); + checkNotNull(src, LINK_POINT_NULL); + checkNotNull(dst, LINK_POINT_NULL); + checkNotNull(realizedBy, "Tunnel ID cannot be null"); + return store.addLink(networkId, src, dst, realizedBy); + } + + @Override + public void removeVirtualLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst) { + checkNotNull(networkId, NETWORK_NULL); + checkNotNull(src, LINK_POINT_NULL); + checkNotNull(dst, LINK_POINT_NULL); + store.removeLink(networkId, src, dst); + } + + @Override + public VirtualPort createVirtualPort(NetworkId networkId, DeviceId deviceId, + PortNumber portNumber, Port realizedBy) { + checkNotNull(networkId, NETWORK_NULL); + checkNotNull(deviceId, DEVICE_NULL); + checkNotNull(portNumber, "Port description cannot be null"); + return store.addPort(networkId, deviceId, portNumber, realizedBy); + } + + @Override + public void removeVirtualPort(NetworkId networkId, DeviceId deviceId, PortNumber portNumber) { + checkNotNull(networkId, NETWORK_NULL); + checkNotNull(deviceId, DEVICE_NULL); + checkNotNull(portNumber, "Port number cannot be null"); + store.removePort(networkId, deviceId, portNumber); + } + + @Override + public Set<VirtualNetwork> getVirtualNetworks(TenantId tenantId) { + checkNotNull(tenantId, TENANT_NULL); + return store.getNetworks(tenantId); + } + + @Override + public Set<VirtualDevice> getVirtualDevices(NetworkId networkId) { + checkNotNull(networkId, NETWORK_NULL); + return store.getDevices(networkId); + } + + @Override + public Set<VirtualLink> getVirtualLinks(NetworkId networkId) { + checkNotNull(networkId, NETWORK_NULL); + return store.getLinks(networkId); + } + + @Override + public Set<VirtualPort> getVirtualPorts(NetworkId networkId, DeviceId deviceId) { + checkNotNull(networkId, NETWORK_NULL); + checkNotNull(deviceId, DEVICE_NULL); + return store.getPorts(networkId, deviceId); + } + + @Override + public <T> T get(NetworkId networkId, Class<T> serviceClass) { + checkNotNull(networkId, NETWORK_NULL); + return null; + } + + @Override + protected VirtualNetworkProviderService createProviderService(VirtualNetworkProvider provider) { + return new InternalVirtualNetworkProviderService(provider); + } + + // Service issued to registered virtual network providers so that they + // can interact with the core. + private class InternalVirtualNetworkProviderService + extends AbstractProviderService<VirtualNetworkProvider> + implements VirtualNetworkProviderService { + InternalVirtualNetworkProviderService(VirtualNetworkProvider provider) { + super(provider); + } + } + + // Auxiliary store delegate to receive notification about changes in + // the virtual network configuration store state - by the store itself. + private class InternalStoreDelegate implements VirtualNetworkStoreDelegate { + @Override + public void notify(VirtualNetworkEvent event) { + post(event); + } + } + +} diff --git a/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/package-info.java b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/package-info.java new file mode 100644 index 00000000..da4be5aa --- /dev/null +++ b/framework/src/onos/incubator/net/src/main/java/org/onosproject/incubator/net/virtual/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Implementation of the virtual network subsystem. + */ +package org.onosproject.incubator.net.virtual.impl;
\ No newline at end of file diff --git a/framework/src/onos/incubator/net/src/test/java/org/onosproject/incubator/net/mcast/impl/MulticastRouteManagerTest.java b/framework/src/onos/incubator/net/src/test/java/org/onosproject/incubator/net/mcast/impl/MulticastRouteManagerTest.java new file mode 100644 index 00000000..545e21d0 --- /dev/null +++ b/framework/src/onos/incubator/net/src/test/java/org/onosproject/incubator/net/mcast/impl/MulticastRouteManagerTest.java @@ -0,0 +1,200 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.incubator.net.mcast.impl; + +import com.google.common.collect.Lists; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.junit.TestUtils; +import org.onlab.packet.IpPrefix; +import org.onosproject.common.event.impl.TestEventDispatcher; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.core.DefaultApplicationId; +import org.onosproject.core.IdGenerator; +import org.onosproject.core.Version; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.PortNumber; +import org.onosproject.net.mcast.McastEvent; +import org.onosproject.net.mcast.McastListener; +import org.onosproject.net.mcast.McastRoute; +import org.onosproject.store.service.TestStorageService; + +import java.util.List; +import java.util.Set; + +import static junit.framework.Assert.fail; +import static junit.framework.TestCase.assertEquals; +import static org.onosproject.net.NetTestTools.did; +import static org.onosproject.net.NetTestTools.injectEventDispatcher; + +/** + * Tests for the multicast RIB. + */ +public class MulticastRouteManagerTest { + + McastRoute r1 = new McastRoute(IpPrefix.valueOf("1.1.1.1/8"), + IpPrefix.valueOf("1.1.1.2/8"), + McastRoute.Type.IGMP); + + McastRoute r11 = new McastRoute(IpPrefix.valueOf("1.1.1.1/8"), + IpPrefix.valueOf("1.1.1.2/8"), + McastRoute.Type.STATIC); + + McastRoute r2 = new McastRoute(IpPrefix.valueOf("2.2.2.1/8"), + IpPrefix.valueOf("2.2.2.2/8"), + McastRoute.Type.PIM); + + ConnectPoint cp1 = new ConnectPoint(did("1"), PortNumber.portNumber(1)); + + ConnectPoint cp2 = new ConnectPoint(did("2"), PortNumber.portNumber(2)); + + private TestMulticastListener listener = new TestMulticastListener(); + + private MulticastRouteManager manager; + + private List<McastEvent> events; + + @Before + public void setUp() throws Exception { + manager = new MulticastRouteManager(); + injectEventDispatcher(manager, new TestEventDispatcher()); + TestUtils.setField(manager, "storageService", new TestStorageService()); + TestUtils.setField(manager, "coreService", new TestCoreService()); + events = Lists.newArrayList(); + manager.activate(); + manager.addListener(listener); + } + + @After + public void tearDown() { + manager.removeListener(listener); + manager.deactivate(); + } + + @Test + public void testAdd() { + manager.add(r1); + + assertEquals("Add failed", manager.mcastRoutes.size(), 1); + validateEvents(McastEvent.Type.ROUTE_ADDED); + } + + @Test + public void testRemove() { + manager.add(r1); + + manager.remove(r1); + + assertEquals("Remove failed", manager.mcastRoutes.size(), 0); + validateEvents(McastEvent.Type.ROUTE_ADDED, McastEvent.Type.ROUTE_REMOVED); + } + + @Test + public void testAddSource() { + manager.add(r1); + + manager.addSource(r1, cp1); + + validateEvents(McastEvent.Type.ROUTE_ADDED, McastEvent.Type.SOURCE_ADDED); + assertEquals("Route is not equal", cp1, manager.fetchSource(r1)); + } + + @Test + public void testAddSink() { + manager.add(r1); + + manager.addSource(r1, cp1); + manager.addSink(r1, cp1); + + validateEvents(McastEvent.Type.ROUTE_ADDED, + McastEvent.Type.SOURCE_ADDED, + McastEvent.Type.SINK_ADDED); + assertEquals("Route is not equal", Lists.newArrayList(cp1), manager.fetchSinks(r1)); + } + + @Test + public void testRemoveSink() { + manager.add(r1); + + manager.addSource(r1, cp1); + manager.addSink(r1, cp1); + manager.addSink(r1, cp2); + manager.removeSink(r1, cp2); + + validateEvents(McastEvent.Type.ROUTE_ADDED, + McastEvent.Type.SOURCE_ADDED, + McastEvent.Type.SINK_ADDED, + McastEvent.Type.SINK_ADDED, + McastEvent.Type.SINK_REMOVED); + assertEquals("Route is not equal", Lists.newArrayList(cp1), manager.fetchSinks(r1)); + } + + private void validateEvents(McastEvent.Type... evs) { + if (events.size() != evs.length) { + fail(String.format("Mismatch number of events# obtained -> %s : expected %s", + events, evs)); + } + + for (int i = 0; i < evs.length; i++) { + if (evs[i] != events.get(i).type()) { + fail(String.format("Mismtached events# obtained -> %s : expected %s", + events, evs)); + } + } + } + + class TestMulticastListener implements McastListener { + + @Override + public void event(McastEvent event) { + events.add(event); + } + } + + private class TestCoreService implements CoreService { + @Override + public Version version() { + return null; + } + + @Override + public Set<ApplicationId> getAppIds() { + return null; + } + + @Override + public ApplicationId getAppId(Short id) { + return null; + } + + @Override + public ApplicationId getAppId(String name) { + return null; + } + + @Override + public ApplicationId registerApplication(String identifier) { + return new DefaultApplicationId(0, identifier); + } + + @Override + public IdGenerator getIdGenerator(String topic) { + return null; + } + } +} diff --git a/framework/src/onos/incubator/net/src/test/java/org/onosproject/incubator/net/meter/impl/MeterManagerTest.java b/framework/src/onos/incubator/net/src/test/java/org/onosproject/incubator/net/meter/impl/MeterManagerTest.java index e0c0c868..76caebcb 100644 --- a/framework/src/onos/incubator/net/src/test/java/org/onosproject/incubator/net/meter/impl/MeterManagerTest.java +++ b/framework/src/onos/incubator/net/src/test/java/org/onosproject/incubator/net/meter/impl/MeterManagerTest.java @@ -130,7 +130,7 @@ public class MeterManagerTest { m2 = DefaultMeter.builder() .forDevice(did("2")) .fromApp(APP_ID) - .withId(MeterId.meterId(2)) + .withId(MeterId.meterId(1)) .withUnit(Meter.Unit.KB_PER_SEC) .withBands(Collections.singletonList(band)) .build(); @@ -167,7 +167,7 @@ public class MeterManagerTest { assertTrue("The meter was not added", manager.getAllMeters().size() == 1); - assertThat(manager.getMeter(MeterId.meterId(1)), is(m1)); + assertThat(manager.getMeter(did("1"), MeterId.meterId(1)), is(m1)); } @Test @@ -175,7 +175,7 @@ public class MeterManagerTest { manager.submit(m1Request.add()); manager.withdraw(m1Request.remove(), m1.id()); - assertThat(manager.getMeter(MeterId.meterId(1)).state(), + assertThat(manager.getMeter(did("1"), MeterId.meterId(1)).state(), is(MeterState.PENDING_REMOVE)); providerService.pushMeterMetrics(m1.deviceId(), Collections.emptyList()); @@ -184,7 +184,16 @@ public class MeterManagerTest { } + @Test + public void testMultipleDevice() { + manager.submit(m1Request.add()); + manager.submit(m2Request.add()); + assertTrue("The meters were not added", manager.getAllMeters().size() == 2); + + assertThat(manager.getMeter(did("1"), MeterId.meterId(1)), is(m1)); + assertThat(manager.getMeter(did("2"), MeterId.meterId(1)), is(m2)); + } public class TestApplicationId extends DefaultApplicationId { public TestApplicationId(int id, String name) { diff --git a/framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStore.java b/framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStore.java index 32890cb1..62a94675 100644 --- a/framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStore.java +++ b/framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/meter/impl/DistributedMeterStore.java @@ -33,6 +33,7 @@ import org.onosproject.net.meter.Meter; import org.onosproject.net.meter.MeterEvent; import org.onosproject.net.meter.MeterFailReason; import org.onosproject.net.meter.MeterId; +import org.onosproject.net.meter.MeterKey; import org.onosproject.net.meter.MeterOperation; import org.onosproject.net.meter.MeterState; import org.onosproject.net.meter.MeterStore; @@ -78,12 +79,12 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) private ClusterService clusterService; - private ConsistentMap<MeterId, MeterData> meters; + private ConsistentMap<MeterKey, MeterData> meters; private NodeId local; - private MapEventListener mapListener = new InternalMapEventListener(); + private MapEventListener<MeterKey, MeterData> mapListener = new InternalMapEventListener(); - private Map<MeterId, CompletableFuture<MeterStoreResult>> futures = + private Map<MeterKey, CompletableFuture<MeterStoreResult>> futures = Maps.newConcurrentMap(); @Activate @@ -92,9 +93,10 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD local = clusterService.getLocalNode().id(); - meters = storageService.<MeterId, MeterData>consistentMapBuilder() + meters = storageService.<MeterKey, MeterData>consistentMapBuilder() .withName(METERSTORE) .withSerializer(Serializer.using(Arrays.asList(KryoNamespaces.API), + MeterKey.class, MeterData.class, DefaultMeter.class, DefaultBand.class, @@ -120,11 +122,12 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD @Override public CompletableFuture<MeterStoreResult> storeMeter(Meter meter) { CompletableFuture<MeterStoreResult> future = new CompletableFuture<>(); - futures.put(meter.id(), future); + MeterKey key = MeterKey.key(meter.deviceId(), meter.id()); + futures.put(key, future); MeterData data = new MeterData(meter, null, local); try { - meters.put(meter.id(), data); + meters.put(key, data); } catch (StorageException e) { future.completeExceptionally(e); } @@ -136,14 +139,15 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD @Override public CompletableFuture<MeterStoreResult> deleteMeter(Meter meter) { CompletableFuture<MeterStoreResult> future = new CompletableFuture<>(); - futures.put(meter.id(), future); + MeterKey key = MeterKey.key(meter.deviceId(), meter.id()); + futures.put(key, future); MeterData data = new MeterData(meter, null, local); // update the state of the meter. It will be pruned by observing // that it has been removed from the dataplane. try { - if (meters.computeIfPresent(meter.id(), (k, v) -> data) == null) { + if (meters.computeIfPresent(key, (k, v) -> data) == null) { future.complete(MeterStoreResult.success()); } } catch (StorageException e) { @@ -157,11 +161,12 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD @Override public CompletableFuture<MeterStoreResult> updateMeter(Meter meter) { CompletableFuture<MeterStoreResult> future = new CompletableFuture<>(); - futures.put(meter.id(), future); + MeterKey key = MeterKey.key(meter.deviceId(), meter.id()); + futures.put(key, future); MeterData data = new MeterData(meter, null, local); try { - if (meters.computeIfPresent(meter.id(), (k, v) -> data) == null) { + if (meters.computeIfPresent(key, (k, v) -> data) == null) { future.complete(MeterStoreResult.fail(MeterFailReason.INVALID_METER)); } } catch (StorageException e) { @@ -172,7 +177,8 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD @Override public void updateMeterState(Meter meter) { - meters.computeIfPresent(meter.id(), (id, v) -> { + MeterKey key = MeterKey.key(meter.deviceId(), meter.id()); + meters.computeIfPresent(key, (k, v) -> { DefaultMeter m = (DefaultMeter) v.meter(); m.setState(meter.state()); m.setProcessedPackets(meter.packetsSeen()); @@ -185,8 +191,8 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD } @Override - public Meter getMeter(MeterId meterId) { - MeterData data = Versioned.valueOrElse(meters.get(meterId), null); + public Meter getMeter(MeterKey key) { + MeterData data = Versioned.valueOrElse(meters.get(key), null); return data == null ? null : data.meter(); } @@ -198,19 +204,22 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD @Override public void failedMeter(MeterOperation op, MeterFailReason reason) { - meters.computeIfPresent(op.meter().id(), (k, v) -> + MeterKey key = MeterKey.key(op.meter().deviceId(), op.meter().id()); + meters.computeIfPresent(key, (k, v) -> new MeterData(v.meter(), reason, v.origin())); } @Override public void deleteMeterNow(Meter m) { - futures.remove(m.id()); - meters.remove(m.id()); + MeterKey key = MeterKey.key(m.deviceId(), m.id()); + futures.remove(key); + meters.remove(key); } - private class InternalMapEventListener implements MapEventListener<MeterId, MeterData> { + private class InternalMapEventListener implements MapEventListener<MeterKey, MeterData> { @Override - public void event(MapEvent<MeterId, MeterData> event) { + public void event(MapEvent<MeterKey, MeterData> event) { + MeterKey key = event.key(); MeterData data = event.value().value(); NodeId master = mastershipService.getMasterFor(data.meter().deviceId()); switch (event.type()) { @@ -227,17 +236,17 @@ public class DistributedMeterStore extends AbstractStore<MeterEvent, MeterStoreD } else if (data.reason().isPresent() && local.equals(data.origin())) { MeterStoreResult msr = MeterStoreResult.fail(data.reason().get()); //TODO: No future -> no friend - futures.get(data.meter().id()).complete(msr); + futures.get(key).complete(msr); } break; case ADDED: if (local.equals(data.origin()) && data.meter().state() == MeterState.PENDING_ADD) { - futures.remove(data.meter().id()).complete(MeterStoreResult.success()); + futures.remove(key).complete(MeterStoreResult.success()); } break; case REMOVED: if (local.equals(data.origin()) && data.meter().state() == MeterState.PENDING_REMOVE) { - futures.remove(data.meter().id()).complete(MeterStoreResult.success()); + futures.remove(key).complete(MeterStoreResult.success()); } break; default: diff --git a/framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java b/framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java new file mode 100644 index 00000000..69e56c0b --- /dev/null +++ b/framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/DistributedVirtualNetworkStore.java @@ -0,0 +1,147 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.incubator.store.virtual.impl; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Service; +import org.onosproject.incubator.net.tunnel.TunnelId; +import org.onosproject.incubator.net.virtual.DefaultVirtualDevice; +import org.onosproject.incubator.net.virtual.DefaultVirtualNetwork; +import org.onosproject.incubator.net.virtual.NetworkId; +import org.onosproject.incubator.net.virtual.TenantId; +import org.onosproject.incubator.net.virtual.VirtualDevice; +import org.onosproject.incubator.net.virtual.VirtualLink; +import org.onosproject.incubator.net.virtual.VirtualNetwork; +import org.onosproject.incubator.net.virtual.VirtualNetworkEvent; +import org.onosproject.incubator.net.virtual.VirtualNetworkStore; +import org.onosproject.incubator.net.virtual.VirtualNetworkStoreDelegate; +import org.onosproject.incubator.net.virtual.VirtualPort; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; +import org.onosproject.store.AbstractStore; +import org.slf4j.Logger; + +import java.util.Set; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Implementation of the network store. + */ +@Component(immediate = true) +@Service +public class DistributedVirtualNetworkStore + extends AbstractStore<VirtualNetworkEvent, VirtualNetworkStoreDelegate> + implements VirtualNetworkStore { + + private final Logger log = getLogger(getClass()); + + // TODO: track tenants by ID + // TODO: track networks by ID and by tenants + // TODO: track devices by network ID and device ID + // TODO: track devices by network ID + // TODO: setup block allocator for network IDs + + // TODO: notify delegate + + @Activate + public void activate() { + log.info("Started"); + } + + @Deactivate + public void deactivate() { + log.info("Stopped"); + } + + @Override + public void addTenantId(TenantId tenantId) { + } + + @Override + public void removeTenantId(TenantId tenantId) { + } + + @Override + public Set<TenantId> getTenantIds() { + return null; + } + + @Override + public VirtualNetwork addNetwork(TenantId tenantId) { + return new DefaultVirtualNetwork(genNetworkId(), tenantId); + } + + private NetworkId genNetworkId() { + return NetworkId.networkId(0); // TODO: use a block allocator + } + + + @Override + public void removeNetwork(NetworkId networkId) { + } + + @Override + public VirtualDevice addDevice(NetworkId networkId, DeviceId deviceId) { + return new DefaultVirtualDevice(networkId, deviceId); + } + + @Override + public void removeDevice(NetworkId networkId, DeviceId deviceId) { + } + + @Override + public VirtualLink addLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst, TunnelId realizedBy) { + return null; + } + + @Override + public void removeLink(NetworkId networkId, ConnectPoint src, ConnectPoint dst) { + } + + @Override + public VirtualPort addPort(NetworkId networkId, DeviceId deviceId, PortNumber portNumber, Port realizedBy) { + return null; + } + + @Override + public void removePort(NetworkId networkId, DeviceId deviceId, PortNumber portNumber) { + } + + @Override + public Set<VirtualNetwork> getNetworks(TenantId tenantId) { + return null; + } + + @Override + public Set<VirtualDevice> getDevices(NetworkId networkId) { + return null; + } + + @Override + public Set<VirtualLink> getLinks(NetworkId networkId) { + return null; + } + + @Override + public Set<VirtualPort> getPorts(NetworkId networkId, DeviceId deviceId) { + return null; + } +} diff --git a/framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/package-info.java b/framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/package-info.java new file mode 100644 index 00000000..12fa9091 --- /dev/null +++ b/framework/src/onos/incubator/store/src/main/java/org/onosproject/incubator/store/virtual/impl/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Implementation of distributed virtual network store. + */ +package org.onosproject.incubator.store.virtual.impl; diff --git a/framework/src/onos/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java b/framework/src/onos/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java index 238c4000..af92a1d0 100644 --- a/framework/src/onos/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java +++ b/framework/src/onos/openflow/api/src/main/java/org/onosproject/openflow/controller/DefaultOpenFlowPacketContext.java @@ -104,7 +104,7 @@ public final class DefaultOpenFlowPacketContext implements OpenFlowPacketContext } catch (BufferUnderflowException | NullPointerException | DeserializationException e) { Logger log = LoggerFactory.getLogger(getClass()); - log.warn("packet deserialization problem : {}", e.getMessage()); + log.error("packet deserialization problem : {}", e.getMessage()); return null; } } diff --git a/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/Controller.java b/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/Controller.java index 0c28a6fa..9d355156 100644 --- a/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/Controller.java +++ b/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/Controller.java @@ -16,6 +16,8 @@ package org.onosproject.openflow.controller.impl; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.group.ChannelGroup; @@ -37,13 +39,24 @@ import org.projectfloodlight.openflow.protocol.OFVersion; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.TrustManagerFactory; +import java.io.FileInputStream; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; import java.net.InetSocketAddress; +import java.security.KeyStore; +import java.util.Dictionary; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.Executors; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import static org.onlab.util.Tools.get; import static org.onlab.util.Tools.groupedThreads; import static org.onosproject.net.DeviceId.deviceId; import static org.onosproject.openflow.controller.Dpid.uri; @@ -59,14 +72,16 @@ public class Controller { protected static final OFFactory FACTORY13 = OFFactories.getFactory(OFVersion.OF_13); protected static final OFFactory FACTORY10 = OFFactories.getFactory(OFVersion.OF_10); + private static final boolean TLS_DISABLED = false; + private static final short MIN_KS_LENGTH = 6; protected HashMap<String, String> controllerNodeIPsCache; private ChannelGroup cg; // Configuration options - protected int openFlowPort = 6633; - protected int workerThreads = 0; + protected List<Integer> openFlowPorts = ImmutableList.of(6633, 6653); + protected int workerThreads = 16; // Start time of the controller protected long systemStartTime; @@ -75,9 +90,16 @@ public class Controller { private NioServerSocketChannelFactory execFactory; + protected String ksLocation; + protected String tsLocation; + protected char[] ksPwd; + protected char[] tsPwd; + private SSLEngine serverSSLEngine; + // Perf. related configuration protected static final int SEND_BUFFER_SIZE = 4 * 1024 * 1024; private DriverService driverService; + private boolean enableOFTLS = TLS_DISABLED; // *************** // Getters/Setters @@ -127,13 +149,15 @@ public class Controller { bootstrap.setOption("child.sendBufferSize", Controller.SEND_BUFFER_SIZE); ChannelPipelineFactory pfact = - new OpenflowPipelineFactory(this, null); + new OpenflowPipelineFactory(this, null, serverSSLEngine); bootstrap.setPipelineFactory(pfact); - InetSocketAddress sa = new InetSocketAddress(openFlowPort); cg = new DefaultChannelGroup(); - cg.add(bootstrap.bind(sa)); + openFlowPorts.forEach(port -> { + InetSocketAddress sa = new InetSocketAddress(port); + cg.add(bootstrap.bind(sa)); + log.info("Listening for switch connections on {}", sa); + }); - log.info("Listening for switch connections on {}", sa); } catch (Exception e) { throw new RuntimeException(e); } @@ -155,19 +179,22 @@ public class Controller { } } - public void setConfigParams(Map<String, String> configParams) { - String ofPort = configParams.get("openflowport"); - if (ofPort != null) { - this.openFlowPort = Integer.parseInt(ofPort); + public void setConfigParams(Dictionary<?, ?> properties) { + String ports = get(properties, "openflowPorts"); + if (!Strings.isNullOrEmpty(ports)) { + this.openFlowPorts = Stream.of(ports.split(",")) + .map(s -> Integer.parseInt(s)) + .collect(Collectors.toList()); } + log.debug("OpenFlow ports set to {}", this.openFlowPorts); - log.debug("OpenFlow port set to {}", this.openFlowPort); - String threads = configParams.get("workerthreads"); - this.workerThreads = threads != null ? Integer.parseInt(threads) : 16; + String threads = get(properties, "workerThreads"); + if (!Strings.isNullOrEmpty(threads)) { + this.workerThreads = Integer.parseInt(threads); + } log.debug("Number of worker threads set to {}", this.workerThreads); } - /** * Initialize internal data structures. */ @@ -177,6 +204,68 @@ public class Controller { this.controllerNodeIPsCache = new HashMap<>(); this.systemStartTime = System.currentTimeMillis(); + + try { + getTLSParameters(); + if (enableOFTLS) { + initSSL(); + } + } catch (Exception ex) { + log.error("SSL init failed: {}", ex.getMessage()); + } + + } + + private void getTLSParameters() { + String tempString = System.getProperty("enableOFTLS"); + enableOFTLS = Strings.isNullOrEmpty(tempString) ? TLS_DISABLED : Boolean.parseBoolean(tempString); + log.info("OpenFlow Security is {}", enableOFTLS ? "enabled" : "disabled"); + if (enableOFTLS) { + ksLocation = System.getProperty("javax.net.ssl.keyStore"); + if (Strings.isNullOrEmpty(ksLocation)) { + enableOFTLS = TLS_DISABLED; + return; + } + tsLocation = System.getProperty("javax.net.ssl.trustStore"); + if (Strings.isNullOrEmpty(tsLocation)) { + enableOFTLS = TLS_DISABLED; + return; + } + ksPwd = System.getProperty("javax.net.ssl.keyStorePassword").toCharArray(); + if (MIN_KS_LENGTH > ksPwd.length) { + enableOFTLS = TLS_DISABLED; + return; + } + tsPwd = System.getProperty("javax.net.ssl.trustStorePassword").toCharArray(); + if (MIN_KS_LENGTH > tsPwd.length) { + enableOFTLS = TLS_DISABLED; + return; + } + } + } + + private void initSSL() throws Exception { + + TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + KeyStore ts = KeyStore.getInstance("JKS"); + ts.load(new FileInputStream(tsLocation), tsPwd); + tmFactory.init(ts); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(new FileInputStream(ksLocation), ksPwd); + kmf.init(ks, ksPwd); + + SSLContext serverContext = SSLContext.getInstance("TLS"); + serverContext.init(kmf.getKeyManagers(), tmFactory.getTrustManagers(), null); + + serverSSLEngine = serverContext.createSSLEngine(); + + serverSSLEngine.setNeedClientAuth(true); + serverSSLEngine.setUseClientMode(false); + serverSSLEngine.setEnabledProtocols(serverSSLEngine.getSupportedProtocols()); + serverSSLEngine.setEnabledCipherSuites(serverSSLEngine.getSupportedCipherSuites()); + serverSSLEngine.setEnableSessionCreation(true); } // ************** diff --git a/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFChannelHandler.java b/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFChannelHandler.java index 450aa827..1a088ff7 100644 --- a/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFChannelHandler.java +++ b/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFChannelHandler.java @@ -941,7 +941,8 @@ class OFChannelHandler extends IdleStateAwareChannelHandler { void processOFHello(OFChannelHandler h, OFHello m) throws IOException, SwitchStateException { // we only expect hello in the WAIT_HELLO state - illegalMessageReceived(h, m); + log.warn("Received Hello outside WAIT_HELLO state; switch {} is not complaint.", + h.channel.getRemoteAddress()); } void processOFBarrierReply(OFChannelHandler h, OFBarrierReply m) diff --git a/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java b/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java index c82078a1..d0429947 100644 --- a/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java +++ b/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenFlowControllerImpl.java @@ -15,7 +15,6 @@ */ package org.onosproject.openflow.controller.impl; -import com.google.common.base.Strings; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; @@ -48,6 +47,8 @@ import org.projectfloodlight.openflow.protocol.OFExperimenter; import org.projectfloodlight.openflow.protocol.OFFactories; import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry; import org.projectfloodlight.openflow.protocol.OFFlowStatsReply; +import org.projectfloodlight.openflow.protocol.OFTableStatsEntry; +import org.projectfloodlight.openflow.protocol.OFTableStatsReply; import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry; import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply; import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry; @@ -67,11 +68,8 @@ import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Collections; -import java.util.Dictionary; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; @@ -80,13 +78,12 @@ import java.util.concurrent.Executors; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import static org.onlab.util.Tools.get; import static org.onlab.util.Tools.groupedThreads; @Component(immediate = true) @Service public class OpenFlowControllerImpl implements OpenFlowController { - private static final int DEFAULT_OFPORT = 6633; + private static final String DEFAULT_OFPORT = "6633,6653"; private static final int DEFAULT_WORKER_THREADS = 16; private static final Logger log = @@ -102,9 +99,9 @@ public class OpenFlowControllerImpl implements OpenFlowController { @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected ComponentConfigService cfgService; - @Property(name = "openflowPort", intValue = DEFAULT_OFPORT, - label = "Port number used by OpenFlow protocol; default is 6633") - private int openflowPort = DEFAULT_OFPORT; + @Property(name = "openflowPorts", value = DEFAULT_OFPORT, + label = "Port numbers (comma separated) used by OpenFlow protocol; default is 6633,6653") + private String openflowPorts = DEFAULT_OFPORT; @Property(name = "workerThreads", intValue = DEFAULT_WORKER_THREADS, label = "Number of controller worker threads; default is 16") @@ -134,6 +131,9 @@ public class OpenFlowControllerImpl implements OpenFlowController { protected Multimap<Dpid, OFFlowStatsEntry> fullFlowStats = ArrayListMultimap.create(); + protected Multimap<Dpid, OFTableStatsEntry> fullTableStats = + ArrayListMultimap.create(); + protected Multimap<Dpid, OFGroupStatsEntry> fullGroupStats = ArrayListMultimap.create(); @@ -148,8 +148,7 @@ public class OpenFlowControllerImpl implements OpenFlowController { @Activate public void activate(ComponentContext context) { cfgService.registerProperties(getClass()); - Map<String, String> properties = readComponentConfiguration(context); - ctrl.setConfigParams(properties); + ctrl.setConfigParams(context.getProperties()); ctrl.start(agent, driverService); } @@ -159,33 +158,10 @@ public class OpenFlowControllerImpl implements OpenFlowController { ctrl.stop(); } - /** - * Extracts properties from the component configuration context. - * - * @param context the component context - */ - private Map<String, String> readComponentConfiguration(ComponentContext context) { - Dictionary<?, ?> properties = context.getProperties(); - Map<String, String> outProperties = new HashMap<>(); - - String port = get(properties, "openflowPort"); - if (!Strings.isNullOrEmpty(port)) { - outProperties.put("openflowport", port); - } - - String thread = get(properties, "workerThreads"); - if (!Strings.isNullOrEmpty(thread)) { - outProperties.put("workerthreads", thread); - } - - return outProperties; - } - @Modified public void modified(ComponentContext context) { - Map<String, String> properties = readComponentConfiguration(context); ctrl.stop(); - ctrl.setConfigParams(properties); + ctrl.setConfigParams(context.getProperties()); ctrl.start(agent, driverService); } @@ -259,6 +235,7 @@ public class OpenFlowControllerImpl implements OpenFlowController { @Override public void processPacket(Dpid dpid, OFMessage msg) { Collection<OFFlowStatsEntry> flowStats; + Collection<OFTableStatsEntry> tableStats; Collection<OFGroupStatsEntry> groupStats; Collection<OFGroupDescStatsEntry> groupDescStats; Collection<OFPortStatsEntry> portStats; @@ -302,6 +279,16 @@ public class OpenFlowControllerImpl implements OpenFlowController { OFFlowStatsReply.Builder rep = OFFactories.getFactory(msg.getVersion()).buildFlowStatsReply(); rep.setEntries(Lists.newLinkedList(flowStats)); + rep.setXid(reply.getXid()); + executorMsgs.submit(new OFMessageHandler(dpid, rep.build())); + } + break; + case TABLE: + tableStats = publishTableStats(dpid, (OFTableStatsReply) reply); + if (tableStats != null) { + OFTableStatsReply.Builder rep = + OFFactories.getFactory(msg.getVersion()).buildTableStatsReply(); + rep.setEntries(Lists.newLinkedList(tableStats)); executorMsgs.submit(new OFMessageHandler(dpid, rep.build())); } break; @@ -423,6 +410,16 @@ public class OpenFlowControllerImpl implements OpenFlowController { return null; } + private synchronized Collection<OFTableStatsEntry> publishTableStats(Dpid dpid, + OFTableStatsReply reply) { + //TODO: Get rid of synchronized + fullTableStats.putAll(dpid, reply.getEntries()); + if (!reply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) { + return fullTableStats.removeAll(dpid); + } + return null; + } + private synchronized Collection<OFGroupStatsEntry> publishGroupStats(Dpid dpid, OFGroupStatsReply reply) { //TODO: Get rid of synchronized diff --git a/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenflowPipelineFactory.java b/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenflowPipelineFactory.java index c7ba105c..1467520d 100644 --- a/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenflowPipelineFactory.java +++ b/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OpenflowPipelineFactory.java @@ -27,6 +27,10 @@ import org.jboss.netty.handler.timeout.ReadTimeoutHandler; import org.jboss.netty.util.ExternalResourceReleasable; import org.jboss.netty.util.HashedWheelTimer; import org.jboss.netty.util.Timer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLEngine; /** * Creates a ChannelPipeline for a server-side openflow channel. @@ -34,6 +38,9 @@ import org.jboss.netty.util.Timer; public class OpenflowPipelineFactory implements ChannelPipelineFactory, ExternalResourceReleasable { + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final SSLEngine sslEngine; protected Controller controller; protected ThreadPoolExecutor pipelineExecutor; protected Timer timer; @@ -41,13 +48,15 @@ public class OpenflowPipelineFactory protected ReadTimeoutHandler readTimeoutHandler; public OpenflowPipelineFactory(Controller controller, - ThreadPoolExecutor pipelineExecutor) { + ThreadPoolExecutor pipelineExecutor, + SSLEngine sslEngine) { super(); this.controller = controller; this.pipelineExecutor = pipelineExecutor; this.timer = new HashedWheelTimer(); this.idleHandler = new IdleStateHandler(timer, 20, 25, 0); this.readTimeoutHandler = new ReadTimeoutHandler(timer, 30); + this.sslEngine = sslEngine; } @Override @@ -55,6 +64,13 @@ public class OpenflowPipelineFactory OFChannelHandler handler = new OFChannelHandler(controller); ChannelPipeline pipeline = Channels.pipeline(); + if (sslEngine != null) { + log.info("OpenFlow SSL enabled."); + pipeline.addLast("ssl", + new org.jboss.netty.handler.ssl.SslHandler(sslEngine)); + } else { + log.info("OpenFlow SSL disabled"); + } pipeline.addLast("ofmessagedecoder", new OFMessageDecoder()); pipeline.addLast("ofmessageencoder", new OFMessageEncoder()); pipeline.addLast("idle", idleHandler); diff --git a/framework/src/onos/ovsdb/api/pom.xml b/framework/src/onos/ovsdb/api/pom.xml index 7089216f..c264ae97 100644 --- a/framework/src/onos/ovsdb/api/pom.xml +++ b/framework/src/onos/ovsdb/api/pom.xml @@ -48,6 +48,10 @@ </dependency> <dependency> <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> <artifactId>onos-ovsdb-rfc</artifactId> <version>${project.version}</version> </dependency> diff --git a/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbClientService.java b/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbClientService.java index ab88a24e..f2ff0709 100644 --- a/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbClientService.java +++ b/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbClientService.java @@ -15,19 +15,20 @@ */ package org.onosproject.ovsdb.controller; -import java.util.List; -import java.util.Set; - +import com.google.common.util.concurrent.ListenableFuture; import org.onlab.packet.IpAddress; - +import org.onosproject.net.DeviceId; +import org.onosproject.net.behaviour.ControllerInfo; import org.onosproject.ovsdb.rfc.jsonrpc.OvsdbRPC; import org.onosproject.ovsdb.rfc.message.OperationResult; import org.onosproject.ovsdb.rfc.message.TableUpdates; import org.onosproject.ovsdb.rfc.notation.Row; +import org.onosproject.ovsdb.rfc.notation.UUID; import org.onosproject.ovsdb.rfc.operations.Operation; import org.onosproject.ovsdb.rfc.schema.DatabaseSchema; -import com.google.common.util.concurrent.ListenableFuture; +import java.util.List; +import java.util.Set; /** * Represents to provider facing side of a node. @@ -85,10 +86,39 @@ public interface OvsdbClientService extends OvsdbRPC { Set<OvsdbBridge> getBridges(); /** + * Gets controllers of the node. + * + * @return set of controllers; empty if no controller is find + */ + Set<ControllerInfo> getControllers(DeviceId openflowDeviceId); + + /** + * Sets the Controllers for the specified bridge. + * <p/> + * This method will replace the existing controller list with the new controller + * list. + * + * @param bridgeUuid bridge uuid + * @param controllers list of controllers + */ + void setControllersWithUUID(UUID bridgeUuid, List<ControllerInfo> controllers); + + /** + * Sets the Controllers for the specified device. + * <p/> + * This method will replace the existing controller list with the new controller + * list. + * + * @param deviceId device id (likely Openflow device) + * @param controllers list of controllers + */ + void setControllersWithDeviceId(DeviceId deviceId, List<ControllerInfo> controllers); + + /** * Creates a port. * * @param bridgeName bridge name - * @param portName port name + * @param portName port name */ void createPort(String bridgeName, String portName); @@ -96,7 +126,7 @@ public interface OvsdbClientService extends OvsdbRPC { * Drops a port. * * @param bridgeName bridge name - * @param portName port name + * @param portName port name */ void dropPort(String bridgeName, String portName); @@ -125,7 +155,7 @@ public interface OvsdbClientService extends OvsdbRPC { /** * Gets the Port uuid. * - * @param portName port name + * @param portName port name * @param bridgeUuid bridge uuid * @return port uuid, empty if no uuid is find */ @@ -143,7 +173,7 @@ public interface OvsdbClientService extends OvsdbRPC { /** * Gets the Controller uuid. * - * @param controllerName controller name + * @param controllerName controller name * @param controllerTarget controller target * @return controller uuid, empty if no uuid is find */ @@ -169,7 +199,7 @@ public interface OvsdbClientService extends OvsdbRPC { * Gets the ovsdb table updates. * * @param dbName database name - * @param id random uuid + * @param id random uuid * @return table updates */ ListenableFuture<TableUpdates> monitorTables(String dbName, String id); @@ -177,7 +207,7 @@ public interface OvsdbClientService extends OvsdbRPC { /** * Gets the ovsdb config operation result. * - * @param dbName database name + * @param dbName database name * @param operations the list of operations * @return operation results */ @@ -187,7 +217,7 @@ public interface OvsdbClientService extends OvsdbRPC { /** * Gets the ovsdb database schema from local. * - * @param dbName database name + * @param dbName database name * @return database schema */ DatabaseSchema getDatabaseSchema(String dbName); @@ -195,9 +225,9 @@ public interface OvsdbClientService extends OvsdbRPC { /** * Gets the ovsdb row from the local ovsdb store. * - * @param dbName database name + * @param dbName database name * @param tableName table name - * @param uuid row uuid + * @param uuid row uuid * @return row ovsdb row */ Row getRow(String dbName, String tableName, String uuid); @@ -205,19 +235,19 @@ public interface OvsdbClientService extends OvsdbRPC { /** * Removes the ovsdb row from the local ovsdb store. * - * @param dbName database name + * @param dbName database name * @param tableName table name - * @param uuid row uuid + * @param uuid row uuid */ void removeRow(String dbName, String tableName, String uuid); /** * Updates the local ovsdb store. * - * @param dbName database name + * @param dbName database name * @param tableName table name - * @param uuid row uuid - * @param row ovsdb row + * @param uuid row uuid + * @param row ovsdb row */ void updateOvsdbStore(String dbName, String tableName, String uuid, Row row); @@ -228,4 +258,9 @@ public interface OvsdbClientService extends OvsdbRPC { * @return ovsdb ports */ Set<OvsdbPort> getLocalPorts(Iterable<String> ifaceids); + + /** + * Disconnects the ovsdb server. + */ + void disconnect(); } diff --git a/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbConstant.java b/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbConstant.java index c1133bbd..e91c928a 100644 --- a/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbConstant.java +++ b/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbConstant.java @@ -60,7 +60,7 @@ public final class OvsdbConstant { public static final String EXTERNAL_ID_VM_MAC = "attached-mac"; /** Openflow port. */ - public static final int OFPORT = 6633; + public static final int OFPORT = 6653; /** Ovsdb port. */ public static final int OVSDBPORT = 6640; diff --git a/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbController.java b/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbController.java index 9e245240..24bfeae9 100644 --- a/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbController.java +++ b/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/OvsdbController.java @@ -15,6 +15,9 @@ */ package org.onosproject.ovsdb.controller; +import org.onlab.packet.IpAddress; +import org.onlab.packet.TpPort; + import java.util.List; /** @@ -65,4 +68,12 @@ public interface OvsdbController { * @return OvsdbClient ovsdb node information */ OvsdbClientService getOvsdbClient(OvsdbNodeId nodeId); + + /** + * Connect to the ovsdb server with given ip address and port number. + * + * @param ip ip address + * @param port port number + */ + void connect(IpAddress ip, TpPort port); } diff --git a/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/driver/DefaultOvsdbClient.java b/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/driver/DefaultOvsdbClient.java index 0c64cc0e..3a84d000 100644 --- a/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/driver/DefaultOvsdbClient.java +++ b/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/driver/DefaultOvsdbClient.java @@ -15,19 +15,19 @@ */ package org.onosproject.ovsdb.controller.driver; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; import io.netty.channel.Channel; - -import java.net.InetSocketAddress; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ExecutionException; - import org.onlab.packet.IpAddress; +import org.onosproject.net.DeviceId; +import org.onosproject.net.behaviour.ControllerInfo; import org.onosproject.ovsdb.controller.OvsdbBridge; import org.onosproject.ovsdb.controller.OvsdbBridgeName; import org.onosproject.ovsdb.controller.OvsdbClientService; @@ -71,14 +71,17 @@ import org.onosproject.ovsdb.rfc.utils.MutationUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.databind.JsonNode; -import com.google.common.base.Function; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; /** * An representation of an ovsdb client. @@ -168,9 +171,8 @@ public class DefaultOvsdbClient /** * Gets the ovsdb row store. * - * @param dbName the ovsdb database name + * @param dbName the ovsdb database name * @param tableName the ovsdb table name - * * @return ovsRowStore, empty if row store is find */ private OvsdbRowStore getRowStore(String dbName, String tableName) { @@ -184,9 +186,9 @@ public class DefaultOvsdbClient /** * Gets the ovsdb row. * - * @param dbName the ovsdb database name + * @param dbName the ovsdb database name * @param tableName the ovsdb table name - * @param uuid the key of the row + * @param uuid the key of the row * @return row, empty if row is find */ @Override @@ -394,7 +396,7 @@ public class DefaultOvsdbClient port.setName(portName); if (portUuid == null) { insertConfig(OvsdbConstant.PORT, "_uuid", OvsdbConstant.BRIDGE, - "ports", bridgeUuid, port.getRow()); + "ports", bridgeUuid, port.getRow()); } else { updateConfig(OvsdbConstant.PORT, "_uuid", portUuid, port.getRow()); } @@ -414,7 +416,7 @@ public class DefaultOvsdbClient if (portUuid != null) { log.info("Port {} delete", portName); deleteConfig(OvsdbConstant.PORT, "_uuid", portUuid, - OvsdbConstant.BRIDGE, "ports"); + OvsdbConstant.BRIDGE, "ports"); } } @@ -455,8 +457,8 @@ public class DefaultOvsdbClient bridge.setName(bridgeName); bridgeUuid = insertConfig(OvsdbConstant.BRIDGE, "_uuid", - OvsdbConstant.DATABASENAME, "bridges", - ovsUuid, bridge.getRow()); + OvsdbConstant.DATABASENAME, "bridges", + ovsUuid, bridge.getRow()); if (bridgeUuid != null) { Port port = (Port) TableGenerator.createTable(dbSchema, @@ -466,7 +468,7 @@ public class DefaultOvsdbClient port.setName(bridgeName); insertConfig(OvsdbConstant.PORT, "_uuid", "Bridge", "ports", bridgeUuid, - port.getRow()); + port.getRow()); } } @@ -475,51 +477,92 @@ public class DefaultOvsdbClient updateConfig(OvsdbConstant.BRIDGE, "_uuid", bridgeUuid, bridge.getRow()); } - setController(bridgeUuid); + setControllerAuto(bridgeUuid); log.info("Create bridge success"); } /** - * Sets the Controller. + * Sets the bridge's controller automatically. + * <p/> + * The connection is a TCP connection to the local ONOS instance's IP + * and the default OpenFlow port. * * @param bridgeUuid bridge uuid */ - private void setController(String bridgeUuid) { - String controllerUuid = null; - String iPAddress = IpAddress.valueOf(((InetSocketAddress) channel - .localAddress()) - .getAddress() - .getHostAddress()) - .toString(); + private void setControllerAuto(String bridgeUuid) { + IpAddress ipAddress = IpAddress.valueOf(((InetSocketAddress) channel.localAddress()).getAddress()); + ControllerInfo controllerInfo = new ControllerInfo(ipAddress, OvsdbConstant.OFPORT, "tcp"); + log.debug("Automatically setting controller for bridge {} to {}", + bridgeUuid, controllerInfo.target()); + setControllersWithUUID(UUID.uuid(bridgeUuid), ImmutableList.of(controllerInfo)); + } - String target = "tcp:" + iPAddress + ":" + OvsdbConstant.OFPORT; - log.debug("controller IP {}: port {}", iPAddress, OvsdbConstant.OFPORT); + @Override + public void setControllersWithUUID(UUID bridgeUuid, List<ControllerInfo> controllers) { DatabaseSchema dbSchema = schema.get(OvsdbConstant.DATABASENAME); - Controller controller = (Controller) TableGenerator - .createTable(dbSchema, OvsdbTable.CONTROLLER); - - if (controller != null) { - controller.setTarget(target); - controllerUuid = getControllerUuid(OvsdbConstant.CONTROLLER, target); - if (controllerUuid == null) { + if (dbSchema == null) { + log.debug("There is no schema"); + return; + } + List<Controller> oldControllers = getControllers(bridgeUuid); + if (oldControllers == null) { + log.warn("There are no controllers"); + return; + } - insertConfig(OvsdbConstant.CONTROLLER, "_uuid", - OvsdbConstant.BRIDGE, "controller", bridgeUuid, - controller.getRow()); + Set<UUID> newControllerUuids = new HashSet<>(); + Set<ControllerInfo> newControllers = new HashSet<>(controllers); + List<Controller> removeControllers = new ArrayList<>(); + oldControllers.forEach(controller -> { + ControllerInfo controllerInfo = new ControllerInfo((String) controller.getTargetColumn().data()); + if (newControllers.contains(controllerInfo)) { + newControllers.remove(controllerInfo); + newControllerUuids.add(controller.getRow().uuid()); } else { + removeControllers.add(controller); + } + }); + OvsdbRowStore controllerRowStore = getRowStore(OvsdbConstant.DATABASENAME, + OvsdbConstant.CONTROLLER); + if (controllerRowStore == null) { + log.debug("There is no controller table"); + return; + } - Bridge bridge = (Bridge) TableGenerator - .createTable(dbSchema, OvsdbTable.BRIDGE); - Set<UUID> controllerUuids = new HashSet<>(); - controllerUuids.add(UUID.uuid(controllerUuid)); - bridge.setController(controllerUuids); - updateConfig(OvsdbConstant.CONTROLLER, "_uuid", bridgeUuid, bridge.getRow()); + removeControllers.forEach(c -> deleteConfig(OvsdbConstant.CONTROLLER, "_uuid", c.getRow().uuid().value(), + OvsdbConstant.BRIDGE, "controller")); - } + newControllers.stream().map(c -> { + Controller controller = (Controller) TableGenerator + .createTable(dbSchema, OvsdbTable.CONTROLLER); + controller.setTarget(c.target()); + return controller; + }).forEach(c -> { + String uuid = insertConfig(OvsdbConstant.CONTROLLER, "_uuid", + OvsdbConstant.BRIDGE, "controller", bridgeUuid.value(), + c.getRow()); + newControllerUuids.add(UUID.uuid(uuid)); + + }); + + OvsdbRowStore rowStore = getRowStore(OvsdbConstant.DATABASENAME, + OvsdbConstant.BRIDGE); + if (rowStore == null) { + log.debug("There is no bridge table"); + return; } + Row bridgeRow = rowStore.getRow(bridgeUuid.value()); + Bridge bridge = (Bridge) TableGenerator.getTable(dbSchema, bridgeRow, OvsdbTable.BRIDGE); + bridge.setController(OvsdbSet.ovsdbSet(newControllerUuids)); + updateConfig(OvsdbConstant.BRIDGE, "_uuid", bridgeUuid.value(), bridge.getRow()); + } + + @Override + public void setControllersWithDeviceId(DeviceId deviceId, List<ControllerInfo> controllers) { + setControllersWithUUID(getBridgeUUID(deviceId), controllers); } @Override @@ -530,7 +573,7 @@ public class DefaultOvsdbClient return; } deleteConfig(OvsdbConstant.BRIDGE, "_uuid", bridgeUUID, - OvsdbConstant.DATABASENAME, "bridges"); + OvsdbConstant.DATABASENAME, "bridges"); } @Override @@ -554,7 +597,7 @@ public class DefaultOvsdbClient if (portUuid == null) { portUuid = insertConfig(OvsdbConstant.PORT, "_uuid", OvsdbConstant.BRIDGE, - "ports", bridgeUuid, port.getRow()); + "ports", bridgeUuid, port.getRow()); } else { updateConfig(OvsdbConstant.PORT, "_uuid", portUuid, port.getRow()); } @@ -595,7 +638,7 @@ public class DefaultOvsdbClient options.put("remote_ip", dstIp.toString()); tunInterface.setOptions(options); updateConfig(OvsdbConstant.INTERFACE, "_uuid", interfaceUuid, - tunInterface.getRow()); + tunInterface.getRow()); log.info("Tunnel added success", tunInterface); } @@ -619,7 +662,7 @@ public class DefaultOvsdbClient if (portUUID != null) { log.info("Delete tunnel"); deleteConfig(OvsdbConstant.PORT, "_uuid", portUUID, - OvsdbConstant.BRIDGE, "ports"); + OvsdbConstant.BRIDGE, "ports"); } return; @@ -628,16 +671,15 @@ public class DefaultOvsdbClient /** * Delete transact config. * - * @param childTableName child table name - * @param childColumnName child column name - * @param childUuid child row uuid - * @param parentTableName parent table name + * @param childTableName child table name + * @param childColumnName child column name + * @param childUuid child row uuid + * @param parentTableName parent table name * @param parentColumnName parent column - * */ private void deleteConfig(String childTableName, String childColumnName, - String childUuid, String parentTableName, - String parentColumnName) { + String childUuid, String parentTableName, + String parentColumnName) { DatabaseSchema dbSchema = schema.get(OvsdbConstant.DATABASENAME); TableSchema childTableSchema = dbSchema.getTableSchema(childTableName); @@ -672,14 +714,13 @@ public class DefaultOvsdbClient /** * Update transact config. * - * @param tableName table name + * @param tableName table name * @param columnName column name - * @param uuid uuid - * @param row the config data - * + * @param uuid uuid + * @param row the config data */ private void updateConfig(String tableName, String columnName, String uuid, - Row row) { + Row row) { DatabaseSchema dbSchema = schema.get(OvsdbConstant.DATABASENAME); TableSchema tableSchema = dbSchema.getTableSchema(tableName); @@ -698,18 +739,17 @@ public class DefaultOvsdbClient /** * Insert transact config. * - * @param childTableName child table name - * @param childColumnName child column name - * @param parentTableName parent table name + * @param childTableName child table name + * @param childColumnName child column name + * @param parentTableName parent table name * @param parentColumnName parent column - * @param parentUuid parent uuid - * @param row the config data - * + * @param parentUuid parent uuid + * @param row the config data * @return uuid, empty if no uuid is find */ private String insertConfig(String childTableName, String childColumnName, - String parentTableName, String parentColumnName, - String parentUuid, Row row) { + String parentTableName, String parentColumnName, + String parentUuid, Row row) { DatabaseSchema dbSchema = schema.get(OvsdbConstant.DATABASENAME); TableSchema tableSchema = dbSchema.getTableSchema(childTableName); @@ -741,7 +781,7 @@ public class DefaultOvsdbClient if (childTableName.equalsIgnoreCase(OvsdbConstant.PORT)) { log.info("Handle port insert"); Insert intfInsert = handlePortInsertTable(OvsdbConstant.INTERFACE, - row); + row); if (intfInsert != null) { operations.add(intfInsert); @@ -772,8 +812,7 @@ public class DefaultOvsdbClient * Handles port insert. * * @param tableName ovsdb table interface - * @param portRow row of port - * + * @param portRow row of port * @return insert, empty if null */ private Insert handlePortInsertTable(String tableName, Row portRow) { @@ -801,8 +840,7 @@ public class DefaultOvsdbClient * Gets tunnel name. * * @param tunnelType - * @param dstIp the remote ip address - * + * @param dstIp the remote ip address * @return tunnel name */ private String getTunnelName(String tunnelType, IpAddress dstIp) { @@ -821,7 +859,7 @@ public class DefaultOvsdbClient Function<JsonNode, DatabaseSchema> rowFunction = new Function<JsonNode, DatabaseSchema>() { @Override public DatabaseSchema apply(JsonNode input) { - log.info("Get ovsdb database schema", dbName); + log.info("Get ovsdb database schema {}", dbName); DatabaseSchema dbSchema = FromJsonUtil .jsonNodeToDbSchema(dbName, input); if (dbSchema == null) { @@ -877,21 +915,17 @@ public class DefaultOvsdbClient } DatabaseSchema dbSchema = schema.get(dbName); if (dbSchema != null) { - Function<List<JsonNode>, List<OperationResult>> rowFunction = - new Function<List<JsonNode>, List<OperationResult>>() { - @Override - public List<OperationResult> apply(List<JsonNode> input) { - log.info("Get ovsdb operation result"); - List<OperationResult> result = FromJsonUtil - .jsonNodeToOperationResult(input, operations); - - if (result == null) { - log.debug("The operation result is null"); - return null; - } - return result; + Function<List<JsonNode>, List<OperationResult>> rowFunction = (input -> { + log.info("Get ovsdb operation result"); + List<OperationResult> result = FromJsonUtil + .jsonNodeToOperationResult(input, operations); + + if (result == null) { + log.debug("The operation result is null"); + return null; } - }; + return result; + }); return Futures.transform(transact(dbSchema, operations), rowFunction); } @@ -972,7 +1006,7 @@ public class DefaultOvsdbClient } - @SuppressWarnings({ "rawtypes", "unchecked" }) + @SuppressWarnings({"rawtypes", "unchecked"}) @Override public void processResult(JsonNode response) { log.debug("Handle result"); @@ -1042,6 +1076,105 @@ public class DefaultOvsdbClient } @Override + public Set<ControllerInfo> getControllers(DeviceId openflowDeviceId) { + UUID bridgeUuid = getBridgeUUID(openflowDeviceId); + if (bridgeUuid == null) { + log.warn("bad bridge Uuid"); + return null; + } + List<Controller> controllers = getControllers(bridgeUuid); + if (controllers == null) { + log.warn("bad list of controllers"); + return null; + } + return controllers.stream(). + map(controller -> new ControllerInfo( + (String) controller.getTargetColumn() + .data())).collect(Collectors.toSet()); + } + + private List<Controller> getControllers(UUID bridgeUuid) { + DatabaseSchema dbSchema = schema.get(OvsdbConstant.DATABASENAME); + if (dbSchema == null) { + return null; + } + OvsdbRowStore rowStore = getRowStore(OvsdbConstant.DATABASENAME, + OvsdbConstant.BRIDGE); + if (rowStore == null) { + log.debug("There is no bridge table"); + return null; + } + + Row bridgeRow = rowStore.getRow(bridgeUuid.value()); + Bridge bridge = (Bridge) TableGenerator. + getTable(dbSchema, bridgeRow, OvsdbTable.BRIDGE); + + //FIXME remove log + log.warn("type of controller column", bridge.getControllerColumn() + .data().getClass()); + Set<UUID> controllerUuids = (Set<UUID>) ((OvsdbSet) bridge + .getControllerColumn().data()).set(); +// Set<String> controllerUuidStrings = (Set<String>) bridge.getControllerColumn().data(); + + OvsdbRowStore controllerRowStore = getRowStore(OvsdbConstant.DATABASENAME, + OvsdbConstant.CONTROLLER); + if (controllerRowStore == null) { + log.debug("There is no controller table"); + return null; + } + + List<Controller> ovsdbControllers = new ArrayList<>(); + ConcurrentMap<String, Row> controllerTableRows = controllerRowStore.getRowStore(); + controllerTableRows.forEach((key, row) -> { + if (!controllerUuids.contains(UUID.uuid(key))) { + return; + } + Controller controller = (Controller) TableGenerator + .getTable(dbSchema, row, OvsdbTable.CONTROLLER); + ovsdbControllers.add(controller); + }); + return ovsdbControllers; + } + + + private UUID getBridgeUUID(DeviceId openflowDeviceId) { + DatabaseSchema dbSchema = schema.get(OvsdbConstant.DATABASENAME); + if (dbSchema == null) { + return null; + } + OvsdbRowStore rowStore = getRowStore(OvsdbConstant.DATABASENAME, + OvsdbConstant.BRIDGE); + if (rowStore == null) { + log.debug("There is no bridge table"); + return null; + } + + ConcurrentMap<String, Row> bridgeTableRows = rowStore.getRowStore(); + final AtomicReference<UUID> uuid = new AtomicReference<>(); + for (Map.Entry<String, Row> entry : bridgeTableRows.entrySet()) { + Bridge b = (Bridge) TableGenerator.getTable(dbSchema, + entry.getValue(), + OvsdbTable.BRIDGE); + if (matchesDpid(b, openflowDeviceId)) { + uuid.set(UUID.uuid(entry.getKey())); + break; + } + } + if (uuid.get() == null) { + log.debug("There is no bridge for {}", openflowDeviceId); + } + return uuid.get(); + + } + + private static boolean matchesDpid(Bridge b, DeviceId deviceId) { + String ofDpid = deviceId.toString().replace("of:", ""); + Set ofDeviceIds = ((OvsdbSet) b.getDatapathIdColumn().data()).set(); + //TODO Set<String> + return ofDeviceIds.contains(ofDpid); + } + + @Override public Set<OvsdbPort> getPorts() { Set<OvsdbPort> ovsdbPorts = new HashSet<OvsdbPort>(); OvsdbTableStore tableStore = getTableStore(OvsdbConstant.DATABASENAME); @@ -1185,4 +1318,10 @@ public class DefaultOvsdbClient } return ifaceid; } + + @Override + public void disconnect() { + channel.disconnect(); + this.agent.removeConnectedNode(nodeId); + } } diff --git a/framework/src/onos/ovsdb/api/src/test/java/org/onosproject/ovsdb/controller/driver/OvsdbClientServiceAdapter.java b/framework/src/onos/ovsdb/api/src/test/java/org/onosproject/ovsdb/controller/driver/OvsdbClientServiceAdapter.java new file mode 100644 index 00000000..71fee4fe --- /dev/null +++ b/framework/src/onos/ovsdb/api/src/test/java/org/onosproject/ovsdb/controller/driver/OvsdbClientServiceAdapter.java @@ -0,0 +1,208 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.ovsdb.controller.driver; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.util.concurrent.ListenableFuture; +import org.onlab.packet.IpAddress; +import org.onosproject.net.DeviceId; +import org.onosproject.net.behaviour.ControllerInfo; +import org.onosproject.ovsdb.controller.OvsdbBridge; +import org.onosproject.ovsdb.controller.OvsdbClientService; +import org.onosproject.ovsdb.controller.OvsdbNodeId; +import org.onosproject.ovsdb.controller.OvsdbPort; +import org.onosproject.ovsdb.controller.OvsdbTunnel; +import org.onosproject.ovsdb.rfc.message.OperationResult; +import org.onosproject.ovsdb.rfc.message.TableUpdates; +import org.onosproject.ovsdb.rfc.notation.Row; +import org.onosproject.ovsdb.rfc.notation.UUID; +import org.onosproject.ovsdb.rfc.operations.Operation; +import org.onosproject.ovsdb.rfc.schema.DatabaseSchema; + +import java.util.List; +import java.util.Set; + +/** + * Test Adapter for OvsdbClientService. + */ +public class OvsdbClientServiceAdapter implements OvsdbClientService { + + @Override + public OvsdbNodeId nodeId() { + return null; + } + + @Override + public void createTunnel(IpAddress srcIp, IpAddress dstIp) { + + } + + @Override + public void dropTunnel(IpAddress srcIp, IpAddress dstIp) { + + } + + @Override + public Set<OvsdbTunnel> getTunnels() { + return null; + } + + @Override + public void createBridge(String bridgeName) { + + } + + @Override + public void dropBridge(String bridgeName) { + + } + + @Override + public Set<OvsdbBridge> getBridges() { + return null; + } + + @Override + public Set<ControllerInfo> getControllers(DeviceId openflowDeviceId) { + return null; + } + + @Override + public void setControllersWithUUID(UUID bridgeUuid, List<ControllerInfo> controllers) { + + } + + @Override + public void setControllersWithDeviceId(DeviceId deviceId, List<ControllerInfo> controllers) { + + } + + @Override + public void createPort(String bridgeName, String portName) { + + } + + @Override + public void dropPort(String bridgeName, String portName) { + + } + + @Override + public Set<OvsdbPort> getPorts() { + return null; + } + + @Override + public boolean isConnected() { + return false; + } + + @Override + public String getBridgeUuid(String bridgeName) { + return null; + } + + @Override + public String getPortUuid(String portName, String bridgeUuid) { + return null; + } + + @Override + public String getInterfaceUuid(String portUuid, String portName) { + return null; + } + + @Override + public String getControllerUuid(String controllerName, String controllerTarget) { + return null; + } + + @Override + public String getOvsUuid(String dbName) { + return null; + } + + @Override + public ListenableFuture<DatabaseSchema> getOvsdbSchema(String dbName) { + return null; + } + + @Override + public ListenableFuture<TableUpdates> monitorTables(String dbName, String id) { + return null; + } + + @Override + public ListenableFuture<List<OperationResult>> transactConfig(String dbName, List<Operation> operations) { + return null; + } + + @Override + public DatabaseSchema getDatabaseSchema(String dbName) { + return null; + } + + @Override + public Row getRow(String dbName, String tableName, String uuid) { + return null; + } + + @Override + public void removeRow(String dbName, String tableName, String uuid) { + + } + + @Override + public void updateOvsdbStore(String dbName, String tableName, String uuid, Row row) { + + } + + @Override + public Set<OvsdbPort> getLocalPorts(Iterable<String> ifaceids) { + return null; + } + + @Override + public void disconnect() { + + } + + @Override + public ListenableFuture<JsonNode> getSchema(List<String> dbnames) { + return null; + } + + @Override + public ListenableFuture<List<String>> echo() { + return null; + } + + @Override + public ListenableFuture<JsonNode> monitor(DatabaseSchema dbSchema, String monitorId) { + return null; + } + + @Override + public ListenableFuture<List<String>> listDbs() { + return null; + } + + @Override + public ListenableFuture<List<JsonNode>> transact(DatabaseSchema dbSchema, List<Operation> operations) { + return null; + } +} diff --git a/framework/src/onos/ovsdb/api/src/test/java/org/onosproject/ovsdb/controller/driver/OvsdbControllerAdapter.java b/framework/src/onos/ovsdb/api/src/test/java/org/onosproject/ovsdb/controller/driver/OvsdbControllerAdapter.java new file mode 100644 index 00000000..902113aa --- /dev/null +++ b/framework/src/onos/ovsdb/api/src/test/java/org/onosproject/ovsdb/controller/driver/OvsdbControllerAdapter.java @@ -0,0 +1,75 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.ovsdb.controller.driver; + +import org.onlab.packet.IpAddress; +import org.onlab.packet.TpPort; +import org.onosproject.ovsdb.controller.OvsdbClientService; +import org.onosproject.ovsdb.controller.OvsdbController; +import org.onosproject.ovsdb.controller.OvsdbEventListener; +import org.onosproject.ovsdb.controller.OvsdbNodeId; +import org.onosproject.ovsdb.controller.OvsdbNodeListener; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Test Adapter for OvsdbController. + */ +public class OvsdbControllerAdapter implements OvsdbController { + protected ConcurrentHashMap<OvsdbNodeId, OvsdbClientServiceAdapter> ovsdbClients = + new ConcurrentHashMap<OvsdbNodeId, OvsdbClientServiceAdapter>(); + + @Override + public void addNodeListener(OvsdbNodeListener listener) { + + } + + @Override + public void removeNodeListener(OvsdbNodeListener listener) { + + } + + @Override + public void addOvsdbEventListener(OvsdbEventListener listener) { + + } + + @Override + public void removeOvsdbEventListener(OvsdbEventListener listener) { + + } + + @Override + public List<OvsdbNodeId> getNodeIds() { + long port = 6653; + return new ArrayList<OvsdbNodeId>(Arrays.asList( + new OvsdbNodeId(IpAddress.valueOf("127.0.0.1"), port))); + } + + @Override + public OvsdbClientService getOvsdbClient(OvsdbNodeId nodeId) { + return ovsdbClients.get(nodeId); + } + + @Override + public void connect(IpAddress ip, TpPort port) { + + } +} diff --git a/framework/src/onos/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/Controller.java b/framework/src/onos/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/Controller.java index 07582327..2e84a16a 100644 --- a/framework/src/onos/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/Controller.java +++ b/framework/src/onos/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/Controller.java @@ -15,25 +15,38 @@ */ package org.onosproject.ovsdb.controller.impl; +import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.PooledByteBufAllocator; import io.netty.channel.Channel; +import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoop; import io.netty.channel.EventLoopGroup; import io.netty.channel.ServerChannel; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.string.StringEncoder; +import io.netty.handler.timeout.IdleState; +import io.netty.handler.timeout.IdleStateEvent; +import io.netty.handler.timeout.IdleStateHandler; import io.netty.util.CharsetUtil; import java.net.InetSocketAddress; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import org.onlab.packet.IpAddress; +import org.onlab.packet.TpPort; import org.onosproject.ovsdb.controller.OvsdbConstant; import org.onosproject.ovsdb.controller.OvsdbNodeId; import org.onosproject.ovsdb.controller.driver.DefaultOvsdbClient; @@ -63,6 +76,9 @@ public class Controller { private EventLoopGroup workerGroup; private Class<? extends ServerChannel> serverChannelClass; + private static final int MAX_RETRY = 5; + private static final int IDLE_TIMEOUT_SEC = 10; + /** * Initialization. */ @@ -198,4 +214,86 @@ public class Controller { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } + + /** + * Connect to the ovsdb server with given ip address and port number. + * + * @param ip ip address + * @param port port number + */ + public void connect(IpAddress ip, TpPort port) { + ChannelFutureListener listener = new ConnectionListener(this, ip, port); + connectRetry(ip, port, listener); + } + + private void connectRetry(IpAddress ip, TpPort port, ChannelFutureListener listener) { + try { + Bootstrap b = new Bootstrap(); + b.group(workerGroup) + .channel(NioSocketChannel.class) + .option(ChannelOption.TCP_NODELAY, true) + .handler(new ChannelInitializer<SocketChannel>() { + + @Override + protected void initChannel(SocketChannel channel) throws Exception { + ChannelPipeline p = channel.pipeline(); + p.addLast(new MessageDecoder(), + new StringEncoder(CharsetUtil.UTF_8), + new IdleStateHandler(IDLE_TIMEOUT_SEC, 0, 0), + new ConnectionHandler()); + } + }); + b.remoteAddress(ip.toString(), port.toInt()); + b.connect().addListener(listener); + } catch (Exception e) { + log.warn("Connection to the ovsdb server {}:{} failed", ip.toString(), port.toString()); + } + } + + private class ConnectionListener implements ChannelFutureListener { + private Controller controller; + private IpAddress ip; + private TpPort port; + private AtomicInteger count = new AtomicInteger(); + + public ConnectionListener(Controller controller, + IpAddress ip, + TpPort port) { + this.controller = controller; + this.ip = ip; + this.port = port; + } + + @Override + public void operationComplete(ChannelFuture channelFuture) throws Exception { + if (!channelFuture.isSuccess()) { + channelFuture.channel().close(); + + if (count.incrementAndGet() < MAX_RETRY) { + final EventLoop loop = channelFuture.channel().eventLoop(); + + loop.schedule(() -> { + controller.connectRetry(this.ip, this.port, this); + }, 1L, TimeUnit.SECONDS); + } else { + log.info("Connection to the ovsdb {}:{} failed", + this.ip.toString(), this.port.toString()); + } + } else { + handleNewNodeConnection(channelFuture.channel()); + } + } + } + + private class ConnectionHandler extends ChannelDuplexHandler { + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + IdleStateEvent e = (IdleStateEvent) evt; + + if (e.state() == IdleState.READER_IDLE) { + ctx.close(); + } + } + } } diff --git a/framework/src/onos/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbControllerImpl.java b/framework/src/onos/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbControllerImpl.java index 9b482968..c2cbbf8b 100644 --- a/framework/src/onos/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbControllerImpl.java +++ b/framework/src/onos/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbControllerImpl.java @@ -15,24 +15,15 @@ */ package org.onosproject.ovsdb.controller.impl; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.math.BigInteger; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.ExecutionException; - +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.ImmutableList; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Service; import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; +import org.onlab.packet.TpPort; import org.onosproject.ovsdb.controller.DefaultEventSubject; import org.onosproject.ovsdb.controller.EventSubject; import org.onosproject.ovsdb.controller.OvsdbClientService; @@ -67,7 +58,17 @@ import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.databind.JsonNode; +import java.math.BigInteger; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.ExecutionException; + +import static com.google.common.base.Preconditions.checkNotNull; /** * The implementation of OvsdbController. @@ -133,8 +134,7 @@ public class OvsdbControllerImpl implements OvsdbController { @Override public List<OvsdbNodeId> getNodeIds() { - // TODO Auto-generated method stub - return null; + return ImmutableList.copyOf(ovsdbClients.keySet()); } @Override @@ -142,6 +142,11 @@ public class OvsdbControllerImpl implements OvsdbController { return ovsdbClients.get(nodeId); } + @Override + public void connect(IpAddress ip, TpPort port) { + controller.connect(ip, port); + } + /** * Implementation of an Ovsdb Agent which is responsible for keeping track * of connected node and the state in which they are. @@ -204,8 +209,8 @@ public class OvsdbControllerImpl implements OvsdbController { * Processes table updates. * * @param clientService OvsdbClientService instance - * @param updates TableUpdates instance - * @param dbName ovsdb database name + * @param updates TableUpdates instance + * @param dbName ovsdb database name */ private void processTableUpdates(OvsdbClientService clientService, TableUpdates updates, String dbName) @@ -236,8 +241,8 @@ public class OvsdbControllerImpl implements OvsdbController { Row row = clientService.getRow(OvsdbConstant.DATABASENAME, tableName, uuid.value()); dispatchInterfaceEvent(clientService, row, - OvsdbEvent.Type.PORT_REMOVED, - dbSchema); + OvsdbEvent.Type.PORT_REMOVED, + dbSchema); } clientService.removeRow(dbName, tableName, uuid.value()); } @@ -249,10 +254,10 @@ public class OvsdbControllerImpl implements OvsdbController { * Dispatches event to the north. * * @param clientService OvsdbClientService instance - * @param newRow a new row - * @param oldRow an old row - * @param eventType type of event - * @param dbSchema ovsdb database schema + * @param newRow a new row + * @param oldRow an old row + * @param eventType type of event + * @param dbSchema ovsdb database schema */ private void dispatchInterfaceEvent(OvsdbClientService clientService, Row row, @@ -277,13 +282,13 @@ public class OvsdbControllerImpl implements OvsdbController { } EventSubject eventSubject = new DefaultEventSubject(MacAddress.valueOf( - macAndIfaceId[0]), + macAndIfaceId[0]), new HashSet<IpAddress>(), new OvsdbPortName(intf - .getName()), + .getName()), new OvsdbPortNumber(localPort), new OvsdbDatapathId(Long - .toString(dpid)), + .toString(dpid)), new OvsdbPortType(portType), new OvsdbIfaceId(macAndIfaceId[1])); for (OvsdbEventListener listener : ovsdbEventListener) { @@ -309,7 +314,7 @@ public class OvsdbControllerImpl implements OvsdbController { String attachedMac = externalIds.get(OvsdbConstant.EXTERNAL_ID_VM_MAC); if (attachedMac == null) { - log.warn("The attachedMac is null"); + log.debug("The attachedMac is null"); //FIXME why always null? return null; } String ifaceid = externalIds @@ -318,7 +323,7 @@ public class OvsdbControllerImpl implements OvsdbController { log.warn("The ifaceid is null"); return null; } - return new String[] {attachedMac, ifaceid}; + return new String[]{attachedMac, ifaceid}; } /** @@ -343,7 +348,7 @@ public class OvsdbControllerImpl implements OvsdbController { * Gets datapathid from table bridge. * * @param clientService OvsdbClientService instance - * @param dbSchema ovsdb database schema + * @param dbSchema ovsdb database schema * @return datapathid the bridge datapathid */ private long getDataPathid(OvsdbClientService clientService, diff --git a/framework/src/onos/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbJsonRpcHandler.java b/framework/src/onos/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbJsonRpcHandler.java index 37942c24..1956a1eb 100644 --- a/framework/src/onos/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbJsonRpcHandler.java +++ b/framework/src/onos/ovsdb/ctl/src/main/java/org/onosproject/ovsdb/controller/impl/OvsdbJsonRpcHandler.java @@ -89,7 +89,7 @@ public final class OvsdbJsonRpcHandler extends ChannelInboundHandlerAdapter { */ private void processOvsdbMessage(JsonNode jsonNode) { - log.info("Handle ovsdb message"); + log.debug("Handle ovsdb message"); if (jsonNode.has("result")) { diff --git a/framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/notation/Row.java b/framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/notation/Row.java index 33269223..00609602 100644 --- a/framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/notation/Row.java +++ b/framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/notation/Row.java @@ -15,20 +15,21 @@ */ package org.onosproject.ovsdb.rfc.notation; -import static com.google.common.base.MoreObjects.toStringHelper; -import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.collect.Maps; import java.util.Collection; import java.util.Map; import java.util.Objects; -import com.google.common.collect.Maps; +import static com.google.common.base.MoreObjects.toStringHelper; +import static com.google.common.base.Preconditions.checkNotNull; /** * Row is the basic element of the OpenVswitch's table. */ public final class Row { private String tableName; + private UUID uuid; private Map<String, Column> columns; /** @@ -40,9 +41,11 @@ public final class Row { /** * Row constructor. + * * @param tableName table name */ - public Row(String tableName) { + @Deprecated + private Row(String tableName) { checkNotNull(tableName, "tableName cannot be null"); this.tableName = tableName; this.columns = Maps.newHashMap(); @@ -50,18 +53,22 @@ public final class Row { /** * Row constructor. + * * @param tableName table name - * @param columns Map of Column entity + * @param columns Map of Column entity */ - public Row(String tableName, Map<String, Column> columns) { + public Row(String tableName, UUID uuid, Map<String, Column> columns) { checkNotNull(tableName, "table name cannot be null"); + checkNotNull(uuid, "uuid cannot be null"); checkNotNull(columns, "columns cannot be null"); this.tableName = tableName; + this.uuid = uuid; this.columns = columns; } /** * Returns tableName. + * * @return tableName */ public String tableName() { @@ -70,6 +77,7 @@ public final class Row { /** * Set tableName value. + * * @param tableName table name */ public void setTableName(String tableName) { @@ -77,7 +85,26 @@ public final class Row { } /** + * Returns uuid. + * + * @return uuid + */ + public UUID uuid() { + return uuid; + } + + /** + * Sets uuid value. + * + * @param uuid new uuid + */ + public void setUuid(UUID uuid) { + this.uuid = uuid; + } + + /** * Returns Column by ColumnSchema. + * * @param columnName column name * @return Column */ @@ -87,6 +114,7 @@ public final class Row { /** * Returns Collection of Column. + * * @return Collection of Column */ public Collection<Column> getColumns() { @@ -95,8 +123,9 @@ public final class Row { /** * add Column. + * * @param columnName column name - * @param data Column entity + * @param data Column entity */ public void addColumn(String columnName, Column data) { this.columns.put(columnName, data); diff --git a/framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/table/Bridge.java b/framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/table/Bridge.java index bd589f03..0b5ffeff 100644 --- a/framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/table/Bridge.java +++ b/framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/table/Bridge.java @@ -15,16 +15,17 @@ */ package org.onosproject.ovsdb.rfc.table; -import java.util.Map; -import java.util.Set; - import org.onosproject.ovsdb.rfc.notation.Column; +import org.onosproject.ovsdb.rfc.notation.OvsdbSet; import org.onosproject.ovsdb.rfc.notation.Row; import org.onosproject.ovsdb.rfc.notation.UUID; import org.onosproject.ovsdb.rfc.schema.DatabaseSchema; import org.onosproject.ovsdb.rfc.tableservice.AbstractOvsdbTableService; import org.onosproject.ovsdb.rfc.tableservice.ColumnDescription; +import java.util.Map; +import java.util.Set; + /** * This class provides operations of Bridge Table. */ @@ -351,7 +352,7 @@ public class Bridge extends AbstractOvsdbTableService { * of attributes. * @param controller the column data which column name is "controller" */ - public void setController(Set<UUID> controller) { + public void setController(OvsdbSet controller) { ColumnDescription columndesc = new ColumnDescription( BridgeColumn.CONTROLLER .columnName(), diff --git a/framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/table/TableGenerator.java b/framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/table/TableGenerator.java index c1ae7c79..f5bd860d 100644 --- a/framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/table/TableGenerator.java +++ b/framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/table/TableGenerator.java @@ -37,6 +37,7 @@ public final class TableGenerator { * @param tableName table name * @return Object table entity */ + //FIXME change the name, it creates a row object, such as a controller. public static Object createTable(DatabaseSchema dbSchema, OvsdbTable tableName) { Row row = new Row(); diff --git a/framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/utils/FromJsonUtil.java b/framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/utils/FromJsonUtil.java index 9744fb49..1dcf48f3 100644 --- a/framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/utils/FromJsonUtil.java +++ b/framework/src/onos/ovsdb/rfc/src/main/java/org/onosproject/ovsdb/rfc/utils/FromJsonUtil.java @@ -15,12 +15,11 @@ */ package org.onosproject.ovsdb.rfc.utils; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import org.onosproject.ovsdb.rfc.exception.AbnormalJsonNodeException; import org.onosproject.ovsdb.rfc.exception.UnsupportedException; import org.onosproject.ovsdb.rfc.jsonrpc.Callback; @@ -41,11 +40,11 @@ import org.onosproject.ovsdb.rfc.schema.type.ColumnTypeFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; /** * JsonNode utility class. convert JsonNode into Object. @@ -247,7 +246,7 @@ public final class FromJsonUtil { validateJsonNode(rowsNode, "rows"); ArrayList<Row> rows = Lists.newArrayList(); for (JsonNode rowNode : rowsNode.get("rows")) { - rows.add(createRow(tableSchema, rowNode)); + rows.add(createRow(tableSchema, null, rowNode)); //FIXME null will throw exception } return rows; } @@ -285,8 +284,8 @@ public final class FromJsonUtil { UUID uuid = UUID.uuid(uuidStr); JsonNode newR = oldNewRow.getValue().get("new"); JsonNode oldR = oldNewRow.getValue().get("old"); - Row newRow = newR != null ? createRow(tableSchema, newR) : null; - Row oldRow = oldR != null ? createRow(tableSchema, oldR) : null; + Row newRow = newR != null ? createRow(tableSchema, uuid, newR) : null; + Row oldRow = oldR != null ? createRow(tableSchema, uuid, oldR) : null; RowUpdate rowUpdate = new RowUpdate(uuid, oldRow, newRow); rows.put(uuid, rowUpdate); } @@ -299,7 +298,7 @@ public final class FromJsonUtil { * @param rowNode JsonNode * @return Row */ - private static Row createRow(TableSchema tableSchema, JsonNode rowNode) { + private static Row createRow(TableSchema tableSchema, UUID uuid, JsonNode rowNode) { if (tableSchema == null) { return null; } @@ -314,7 +313,7 @@ public final class FromJsonUtil { columns.put(columnName, new Column(columnName, obj)); } } - return new Row(tableSchema.name(), columns); + return new Row(tableSchema.name(), uuid, columns); } } diff --git a/framework/src/onos/pcep/pcepio/src/main/java/org/onosproject/pcepio/protocol/ver1/PcepErrorVer1.java b/framework/src/onos/pcep/pcepio/src/main/java/org/onosproject/pcepio/protocol/ver1/PcepErrorVer1.java index 847211ed..0ea51410 100644 --- a/framework/src/onos/pcep/pcepio/src/main/java/org/onosproject/pcepio/protocol/ver1/PcepErrorVer1.java +++ b/framework/src/onos/pcep/pcepio/src/main/java/org/onosproject/pcepio/protocol/ver1/PcepErrorVer1.java @@ -110,8 +110,8 @@ public class PcepErrorVer1 implements PcepError { /** * Parse RP List from the channel buffer. * - * @throws PcepParseException if mandatory fields are missing * @param cb of type channel buffer + * @throws PcepParseException if mandatory fields are missing */ public void parseRPList(ChannelBuffer cb) throws PcepParseException { byte yObjClass; diff --git a/framework/src/onos/pom.xml b/framework/src/onos/pom.xml index 1bce8cdb..93ef2779 100644 --- a/framework/src/onos/pom.xml +++ b/framework/src/onos/pom.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <!-- - ~ Copyright 2014 Open Networking Laboratory + ~ Copyright 2015 Open Networking Laboratory ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -57,6 +57,7 @@ <module>tools/package/archetypes</module> <module>tools/package/branding</module> + <module>bgp</module> </modules> <url>http://onosproject.org/</url> @@ -77,6 +78,7 @@ <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <onos-build-conf.version>1.0</onos-build-conf.version> <netty4.version>4.0.23.Final</netty4.version> <copycat.version>0.5.0.onos</copycat.version> <openflowj.version>0.9.0.onos</openflowj.version> @@ -430,11 +432,27 @@ <scope>test</scope> </dependency> <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-bgpio</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> <groupId>commons-pool</groupId> <artifactId>commons-pool</artifactId> <version>1.6</version> </dependency> <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-bgp-api</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-app-bgp-api</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> <groupId>io.netty</groupId> <artifactId>netty-common</artifactId> <version>${netty4.version}</version> @@ -591,12 +609,12 @@ <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>findbugs-maven-plugin</artifactId> - <version>3.0.0</version> + <version>3.0.1</version> <dependencies> <dependency> <groupId>org.onosproject</groupId> <artifactId>onos-build-conf</artifactId> - <version>1.0</version> + <version>${onos-build-conf.version}</version> </dependency> </dependencies> <configuration> @@ -652,7 +670,7 @@ <dependency> <groupId>org.onosproject</groupId> <artifactId>onos-build-conf</artifactId> - <version>1.0</version> + <version>${onos-build-conf.version}</version> </dependency> <!-- For Java 8 lambda support--> <dependency> diff --git a/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java b/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java index 9a823630..93f6bf8c 100644 --- a/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java +++ b/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java @@ -335,15 +335,15 @@ public class HostLocationProvider extends AbstractProvider implements HostProvid arp.getSenderProtocolAddress()); updateLocationIP(hid, srcMac, vlan, hloc, ip); - // IPv4: update location only + // IPv4: update location only } else if (eth.getEtherType() == Ethernet.TYPE_IPV4) { updateLocation(hid, srcMac, vlan, hloc); - // - // NeighborAdvertisement and NeighborSolicitation: possible - // new hosts, update both location and IP. - // - // IPv6: update location only + // + // NeighborAdvertisement and NeighborSolicitation: possible + // new hosts, update both location and IP. + // + // IPv6: update location only } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) { IPv6 ipv6 = (IPv6) eth.getPayload(); IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET6, diff --git a/framework/src/onos/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java b/framework/src/onos/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java index d010f178..6cbb623b 100644 --- a/framework/src/onos/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java +++ b/framework/src/onos/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java @@ -23,9 +23,17 @@ import org.onlab.osgi.ComponentContextAdapter; import org.onlab.packet.ARP; import org.onlab.packet.ChassisId; import org.onlab.packet.Ethernet; +import org.onlab.packet.ICMP6; +import org.onlab.packet.IPv4; +import org.onlab.packet.IPv6; +import org.onlab.packet.Ip6Address; import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; import org.onlab.packet.VlanId; +import org.onlab.packet.ndp.NeighborAdvertisement; +import org.onlab.packet.ndp.NeighborSolicitation; +import org.onlab.packet.ndp.RouterAdvertisement; +import org.onlab.packet.ndp.RouterSolicitation; import org.onosproject.cfg.ComponentConfigAdapter; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; @@ -66,6 +74,7 @@ import java.util.Hashtable; import java.util.Set; import static org.easymock.EasyMock.*; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.*; import static org.onlab.packet.VlanId.vlanId; import static org.onosproject.net.Device.Type.SWITCH; @@ -75,27 +84,42 @@ import static org.onosproject.net.PortNumber.portNumber; import static org.onosproject.net.device.DeviceEvent.Type.*; public class HostLocationProviderTest { - private static final Integer INPORT = 10; private static final String DEV1 = "of:1"; private static final String DEV2 = "of:2"; private static final String DEV3 = "of:3"; + private static final String DEV4 = "of:4"; + private static final String DEV5 = "of:5"; + private static final String DEV6 = "of:6"; private static final VlanId VLAN = vlanId(); + + // IPv4 Host private static final MacAddress MAC = MacAddress.valueOf("00:00:11:00:00:01"); private static final MacAddress BCMAC = MacAddress.valueOf("ff:ff:ff:ff:ff:ff"); private static final byte[] IP = new byte[]{10, 0, 0, 1}; - private static final IpAddress IP_ADDRESS = IpAddress.valueOf(IpAddress.Version.INET, IP); private static final HostLocation LOCATION = new HostLocation(deviceId(DEV1), portNumber(INPORT), 0L); - private static final DefaultHost HOST = new DefaultHost(ProviderId.NONE, hostId(MAC), MAC, vlanId(VlanId.UNTAGGED), LOCATION, ImmutableSet.of(IP_ADDRESS)); + // IPv6 Host + private static final MacAddress MAC2 = MacAddress.valueOf("00:00:22:00:00:02"); + private static final MacAddress BCMAC2 = MacAddress.valueOf("33:33:00:00:00:01"); + private static final byte[] IP2 = Ip6Address.valueOf("1000::1").toOctets(); + private static final IpAddress IP_ADDRESS2 = + IpAddress.valueOf(IpAddress.Version.INET6, IP2); + private static final HostLocation LOCATION2 = + new HostLocation(deviceId(DEV4), portNumber(INPORT), 0L); + private static final DefaultHost HOST2 = + new DefaultHost(ProviderId.NONE, hostId(MAC2), MAC2, + vlanId(VlanId.UNTAGGED), LOCATION2, + ImmutableSet.of(IP_ADDRESS2)); + private static final ComponentContextAdapter CTX_FOR_REMOVE = new ComponentContextAdapter() { @Override @@ -157,51 +181,189 @@ public class HostLocationProviderTest { @Test public void events() { // new host - testProcessor.process(new TestPacketContext(DEV1)); + testProcessor.process(new TestArpPacketContext(DEV1)); + assertNotNull("new host expected", providerService.added); + assertNull("host motion unexpected", providerService.moved); + + // the host moved to new switch + testProcessor.process(new TestArpPacketContext(DEV2)); + assertNotNull("host motion expected", providerService.moved); + + // the host was misheard on a spine + testProcessor.process(new TestArpPacketContext(DEV3)); + assertNull("host misheard on spine switch", providerService.spine); + + providerService.clear(); + + // new host + testProcessor.process(new TestNAPacketContext(DEV4)); assertNotNull("new host expected", providerService.added); assertNull("host motion unexpected", providerService.moved); // the host moved to new switch - testProcessor.process(new TestPacketContext(DEV2)); + testProcessor.process(new TestNAPacketContext(DEV5)); assertNotNull("host motion expected", providerService.moved); // the host was misheard on a spine - testProcessor.process(new TestPacketContext(DEV3)); + testProcessor.process(new TestNAPacketContext(DEV6)); assertNull("host misheard on spine switch", providerService.spine); } @Test public void removeHostByDeviceRemove() { provider.modified(CTX_FOR_REMOVE); - testProcessor.process(new TestPacketContext(DEV1)); + testProcessor.process(new TestArpPacketContext(DEV1)); + testProcessor.process(new TestNAPacketContext(DEV4)); + Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH, "m", "h", "s", "n", new ChassisId(0L)); deviceService.listener.event(new DeviceEvent(DEVICE_REMOVED, device)); assertEquals("incorrect remove count", 1, providerService.removeCount); + + device = new DefaultDevice(ProviderId.NONE, deviceId(DEV4), SWITCH, + "m", "h", "s", "n", new ChassisId(0L)); + deviceService.listener.event(new DeviceEvent(DEVICE_REMOVED, device)); + assertEquals("incorrect remove count", 2, providerService.removeCount); } @Test public void removeHostByDeviceOffline() { provider.modified(CTX_FOR_REMOVE); - testProcessor.process(new TestPacketContext(DEV1)); + testProcessor.process(new TestArpPacketContext(DEV1)); + testProcessor.process(new TestArpPacketContext(DEV4)); + Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH, "m", "h", "s", "n", new ChassisId(0L)); deviceService.listener.event(new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device)); assertEquals("incorrect remove count", 1, providerService.removeCount); + + device = new DefaultDevice(ProviderId.NONE, deviceId(DEV4), SWITCH, + "m", "h", "s", "n", new ChassisId(0L)); + deviceService.listener.event(new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device)); + assertEquals("incorrect remove count", 2, providerService.removeCount); } @Test public void removeHostByDevicePortDown() { provider.modified(CTX_FOR_REMOVE); - testProcessor.process(new TestPacketContext(DEV1)); + testProcessor.process(new TestArpPacketContext(DEV1)); + testProcessor.process(new TestArpPacketContext(DEV4)); + Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH, "m", "h", "s", "n", new ChassisId(0L)); deviceService.listener.event(new DeviceEvent(PORT_UPDATED, device, - new DefaultPort(device, portNumber(INPORT), - false))); + new DefaultPort(device, portNumber(INPORT), false))); assertEquals("incorrect remove count", 1, providerService.removeCount); + + device = new DefaultDevice(ProviderId.NONE, deviceId(DEV4), SWITCH, + "m", "h", "s", "n", new ChassisId(0L)); + deviceService.listener.event(new DeviceEvent(PORT_UPDATED, device, + new DefaultPort(device, portNumber(INPORT), false))); + assertEquals("incorrect remove count", 2, providerService.removeCount); } + /** + * When receiving ARP, updates location and IP. + */ + @Test + public void testReceiveArp() { + testProcessor.process(new TestArpPacketContext(DEV1)); + HostDescription descr = providerService.added; + assertThat(descr.location(), is(LOCATION)); + assertThat(descr.hwAddress(), is(MAC)); + assertThat(descr.ipAddress().toArray()[0], is(IP_ADDRESS)); + assertThat(descr.vlan(), is(VLAN)); + } + + /** + * When receiving IPv4, updates location only. + */ + @Test + public void testReceiveIpv4() { + testProcessor.process(new TestIpv4PacketContext(DEV1)); + HostDescription descr = providerService.added; + assertThat(descr.location(), is(LOCATION)); + assertThat(descr.hwAddress(), is(MAC)); + assertThat(descr.ipAddress().size(), is(0)); + assertThat(descr.vlan(), is(VLAN)); + } + + /** + * When receiving NeighborAdvertisement, updates location and IP. + */ + @Test + public void testReceiveNA() { + testProcessor.process(new TestNAPacketContext(DEV4)); + assertNotNull(providerService.added); + HostDescription descr = providerService.added; + assertThat(descr.location(), is(LOCATION2)); + assertThat(descr.hwAddress(), is(MAC2)); + assertThat(descr.ipAddress().toArray()[0], is(IP_ADDRESS2)); + assertThat(descr.vlan(), is(VLAN)); + } + + /** + * When receiving NeighborSolicitation, updates location and IP. + */ + @Test + public void testReceiveNS() { + testProcessor.process(new TestNSPacketContext(DEV4)); + HostDescription descr = providerService.added; + assertThat(descr.location(), is(LOCATION2)); + assertThat(descr.hwAddress(), is(MAC2)); + assertThat(descr.ipAddress().toArray()[0], is(IP_ADDRESS2)); + assertThat(descr.vlan(), is(VLAN)); + } + + /** + * When receiving RouterAdvertisement, ignores it. + */ + @Test + public void testReceivesRA() { + testProcessor.process(new TestRAPacketContext(DEV4)); + assertNull(providerService.added); + } + + /** + * When receiving RouterSolicitation, ignores it. + */ + @Test + public void testReceiveRS() { + testProcessor.process(new TestRSPacketContext(DEV4)); + assertNull(providerService.added); + } + + /** + * When receiving Duplicate Address Detection (DAD), ignores it. + */ + @Test + public void testReceiveDAD() { + testProcessor.process(new TestDADPacketContext(DEV4)); + assertNull(providerService.added); + } + + /** + * When receiving IPv6 multicast packet, ignores it. + */ + @Test + public void testReceiveIpv6Multicast() { + testProcessor.process(new TestIpv6McastPacketContext(DEV4)); + assertNull(providerService.added); + } + + /** + * When receiving IPv6 unicast packet, updates location only. + */ + @Test + public void testReceiveIpv6Unicast() { + testProcessor.process(new TestIpv6PacketContext(DEV4)); + assertNotNull(providerService.added); + HostDescription descr = providerService.added; + assertThat(descr.location(), is(LOCATION2)); + assertThat(descr.hwAddress(), is(MAC2)); + assertThat(descr.ipAddress().size(), is(0)); + assertThat(descr.vlan(), is(VLAN)); + } @After public void tearDown() { @@ -233,24 +395,30 @@ public class HostLocationProviderTest { extends AbstractProviderService<HostProvider> implements HostProviderService { - DeviceId added = null; - DeviceId moved = null; - DeviceId spine = null; + HostDescription added = null; + HostDescription moved = null; + HostDescription spine = null; public int removeCount; + public void clear() { + added = null; + moved = null; + spine = null; + removeCount = 0; + } + protected TestHostProviderService(HostProvider provider) { super(provider); } @Override public void hostDetected(HostId hostId, HostDescription hostDescription, boolean replaceIps) { - DeviceId descr = hostDescription.location().deviceId(); if (added == null) { - added = descr; - } else if ((moved == null) && !descr.equals(added)) { - moved = descr; + added = hostDescription; + } else if ((moved == null) && !hostDescription.equals(added)) { + moved = hostDescription; } else { - spine = descr; + spine = hostDescription; } } @@ -259,6 +427,10 @@ public class HostLocationProviderTest { removeCount++; } + @Override + public void removeIpFromHost(HostId hostId, IpAddress ipAddress) { + } + } private class TestPacketService extends PacketServiceAdapter { @@ -268,24 +440,26 @@ public class HostLocationProviderTest { } } - private class TestTopologyService extends TopologyServiceAdapter { @Override public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) { //simulate DPID3 as an infrastructure switch - if ((connectPoint.deviceId()).equals(deviceId(DEV3))) { + if ((connectPoint.deviceId()).equals(deviceId(DEV3)) || + connectPoint.deviceId().equals(deviceId(DEV6))) { return true; } return false; } } - private class TestPacketContext implements PacketContext { - + /** + * Generates ARP packet. + */ + private class TestArpPacketContext implements PacketContext { private final String deviceId; - public TestPacketContext(String deviceId) { + public TestArpPacketContext(String deviceId) { this.deviceId = deviceId; } @@ -340,6 +514,490 @@ public class HostLocationProviderTest { } } + /** + * Generates IPv6 Unicast packet. + */ + private class TestIpv4PacketContext implements PacketContext { + private final String deviceId; + + public TestIpv4PacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + IPv4 ipv4 = new IPv4(); + ipv4.setDestinationAddress("10.0.0.1"); + ipv4.setSourceAddress(IP_ADDRESS.toString()); + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_IPV4) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC) + .setDestinationMACAddress(MacAddress.valueOf("00:00:00:00:00:01")) + .setPayload(ipv4); + ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), + portNumber(INPORT)); + return new DefaultInboundPacket(receivedFrom, eth, + ByteBuffer.wrap(eth.serialize())); + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + return false; + } + + @Override + public boolean isHandled() { + return false; + } + } + + /** + * Generates NeighborAdvertisement packet. + */ + private class TestNAPacketContext implements PacketContext { + private final String deviceId; + + public TestNAPacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + NeighborAdvertisement na = new NeighborAdvertisement(); + ICMP6 icmp6 = new ICMP6(); + icmp6.setPayload(na); + IPv6 ipv6 = new IPv6(); + ipv6.setPayload(icmp6); + ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1").toOctets()); + ipv6.setSourceAddress(IP2); + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_IPV6) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC2.toBytes()) + .setDestinationMACAddress(BCMAC2) + .setPayload(ipv6); + ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), + portNumber(INPORT)); + return new DefaultInboundPacket(receivedFrom, eth, + ByteBuffer.wrap(eth.serialize())); + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + return false; + } + + @Override + public boolean isHandled() { + return false; + } + } + + /** + * Generates NeighborSolicitation packet. + */ + private class TestNSPacketContext implements PacketContext { + private final String deviceId; + + public TestNSPacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + NeighborSolicitation ns = new NeighborSolicitation(); + ICMP6 icmp6 = new ICMP6(); + icmp6.setPayload(ns); + IPv6 ipv6 = new IPv6(); + ipv6.setPayload(icmp6); + ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1:ff00:0000").toOctets()); + ipv6.setSourceAddress(IP2); + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_IPV6) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC2.toBytes()) + .setDestinationMACAddress(BCMAC2) + .setPayload(ipv6); + ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), + portNumber(INPORT)); + return new DefaultInboundPacket(receivedFrom, eth, + ByteBuffer.wrap(eth.serialize())); + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + return false; + } + + @Override + public boolean isHandled() { + return false; + } + } + + /** + * Generates Duplicate Address Detection packet. + */ + private class TestDADPacketContext implements PacketContext { + private final String deviceId; + + public TestDADPacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + NeighborSolicitation ns = new NeighborSolicitation(); + ICMP6 icmp6 = new ICMP6(); + icmp6.setPayload(ns); + IPv6 ipv6 = new IPv6(); + ipv6.setPayload(icmp6); + ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1").toOctets()); + ipv6.setSourceAddress(Ip6Address.valueOf("::").toOctets()); + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_IPV6) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC2.toBytes()) + .setDestinationMACAddress(BCMAC2) + .setPayload(ipv6); + ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), + portNumber(INPORT)); + return new DefaultInboundPacket(receivedFrom, eth, + ByteBuffer.wrap(eth.serialize())); + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + return false; + } + + @Override + public boolean isHandled() { + return false; + } + } + + /** + * Generates Router Solicitation packet. + */ + private class TestRSPacketContext implements PacketContext { + private final String deviceId; + + public TestRSPacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + RouterSolicitation ns = new RouterSolicitation(); + ICMP6 icmp6 = new ICMP6(); + icmp6.setPayload(ns); + IPv6 ipv6 = new IPv6(); + ipv6.setPayload(icmp6); + ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::2").toOctets()); + ipv6.setSourceAddress(Ip6Address.valueOf("::").toOctets()); + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_IPV6) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC2.toBytes()) + .setDestinationMACAddress(MacAddress.valueOf("33:33:00:00:00:02")) + .setPayload(ipv6); + ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), + portNumber(INPORT)); + return new DefaultInboundPacket(receivedFrom, eth, + ByteBuffer.wrap(eth.serialize())); + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + return false; + } + + @Override + public boolean isHandled() { + return false; + } + } + + /** + * Generates Router Advertisement packet. + */ + private class TestRAPacketContext implements PacketContext { + private final String deviceId; + + public TestRAPacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + RouterAdvertisement ns = new RouterAdvertisement(); + ICMP6 icmp6 = new ICMP6(); + icmp6.setPayload(ns); + IPv6 ipv6 = new IPv6(); + ipv6.setPayload(icmp6); + ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1").toOctets()); + ipv6.setSourceAddress(IP2); + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_IPV6) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC2.toBytes()) + .setDestinationMACAddress(MacAddress.valueOf("33:33:00:00:00:01")) + .setPayload(ipv6); + ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), + portNumber(INPORT)); + return new DefaultInboundPacket(receivedFrom, eth, + ByteBuffer.wrap(eth.serialize())); + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + return false; + } + + @Override + public boolean isHandled() { + return false; + } + } + + /** + * Generates IPv6 Multicast packet. + */ + private class TestIpv6McastPacketContext implements PacketContext { + private final String deviceId; + + public TestIpv6McastPacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + IPv6 ipv6 = new IPv6(); + ipv6.setDestinationAddress(Ip6Address.valueOf("ff02::1").toOctets()); + ipv6.setSourceAddress(IP2); + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_IPV6) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC2.toBytes()) + .setDestinationMACAddress(MacAddress.valueOf("33:33:00:00:00:01")) + .setPayload(ipv6); + ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), + portNumber(INPORT)); + return new DefaultInboundPacket(receivedFrom, eth, + ByteBuffer.wrap(eth.serialize())); + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + return false; + } + + @Override + public boolean isHandled() { + return false; + } + } + + /** + * Generates IPv6 Unicast packet. + */ + private class TestIpv6PacketContext implements PacketContext { + private final String deviceId; + + public TestIpv6PacketContext(String deviceId) { + this.deviceId = deviceId; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + IPv6 ipv6 = new IPv6(); + ipv6.setDestinationAddress(Ip6Address.valueOf("1000::1").toOctets()); + ipv6.setSourceAddress(IP2); + Ethernet eth = new Ethernet(); + eth.setEtherType(Ethernet.TYPE_IPV6) + .setVlanID(VLAN.toShort()) + .setSourceMACAddress(MAC2) + .setDestinationMACAddress(MacAddress.valueOf("00:00:00:00:00:01")) + .setPayload(ipv6); + ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId), + portNumber(INPORT)); + return new DefaultInboundPacket(receivedFrom, eth, + ByteBuffer.wrap(eth.serialize())); + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + return false; + } + + @Override + public boolean isHandled() { + return false; + } + } + private class TestDeviceService extends DeviceServiceAdapter { private DeviceListener listener; @@ -357,12 +1015,26 @@ public class HostLocationProviderTest { private class TestHostService extends HostServiceAdapter { @Override public Set<Host> getConnectedHosts(ConnectPoint connectPoint) { - return ImmutableSet.of(HOST); + ConnectPoint cp1 = new ConnectPoint(deviceId(DEV1), portNumber(INPORT)); + ConnectPoint cp2 = new ConnectPoint(deviceId(DEV4), portNumber(INPORT)); + if (connectPoint.equals(cp1)) { + return ImmutableSet.of(HOST); + } else if (connectPoint.equals(cp2)) { + return ImmutableSet.of(HOST2); + } else { + return ImmutableSet.of(); + } } @Override public Set<Host> getConnectedHosts(DeviceId deviceId) { - return ImmutableSet.of(HOST); + if (deviceId.equals(deviceId(DEV1))) { + return ImmutableSet.of(HOST); + } else if (deviceId.equals(deviceId(DEV4))) { + return ImmutableSet.of(HOST2); + } else { + return ImmutableSet.of(); + } } } diff --git a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LLDPLinkProvider.java b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LLDPLinkProvider.java index 386d838f..a840f856 100644 --- a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LLDPLinkProvider.java +++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LLDPLinkProvider.java @@ -67,6 +67,8 @@ import java.util.concurrent.ScheduledExecutorService; import static com.google.common.base.Strings.isNullOrEmpty; import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.onlab.packet.Ethernet.TYPE_BSN; +import static org.onlab.packet.Ethernet.TYPE_LLDP; import static org.onlab.util.Tools.get; import static org.onlab.util.Tools.groupedThreads; import static org.onosproject.net.Link.Type.DIRECT; @@ -326,10 +328,10 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider { */ private void requestIntercepts() { TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); - selector.matchEthType(Ethernet.TYPE_LLDP); + selector.matchEthType(TYPE_LLDP); packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId); - selector.matchEthType(Ethernet.TYPE_BSN); + selector.matchEthType(TYPE_BSN); if (useBDDP) { packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId); } else { @@ -342,9 +344,9 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider { */ private void withdrawIntercepts() { TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); - selector.matchEthType(Ethernet.TYPE_LLDP); + selector.matchEthType(TYPE_LLDP); packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId); - selector.matchEthType(Ethernet.TYPE_BSN); + selector.matchEthType(TYPE_BSN); packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId); } @@ -394,7 +396,7 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider { synchronized (discoverers) { ld = discoverers.get(deviceId); if (ld == null) { - if (rules.isSuppressed(device)) { + if (rules != null && rules.isSuppressed(device)) { log.debug("LinkDiscovery from {} disabled by configuration", device.id()); return; } @@ -474,9 +476,15 @@ public class LLDPLinkProvider extends AbstractProvider implements LinkProvider { private class InternalPacketProcessor implements PacketProcessor { @Override public void process(PacketContext context) { - if (context == null) { + if (context == null || context.isHandled()) { return; } + + Ethernet eth = context.inPacket().parsed(); + if (eth == null || (eth.getEtherType() != TYPE_LLDP && eth.getEtherType() != TYPE_BSN)) { + return; + } + LinkDiscovery ld = discoverers.get(context.inPacket().receivedFrom().deviceId()); if (ld == null) { return; diff --git a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java index cb19dc52..4fa961f8 100644 --- a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java +++ b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java @@ -15,10 +15,24 @@ */ package org.onosproject.provider.of.device.impl; -import com.google.common.base.Strings; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Strings.isNullOrEmpty; +import static org.onlab.util.Tools.get; +import static org.onosproject.net.DeviceId.deviceId; +import static org.onosproject.net.Port.Type.COPPER; +import static org.onosproject.net.Port.Type.FIBER; +import static org.onosproject.openflow.controller.Dpid.dpid; +import static org.onosproject.openflow.controller.Dpid.uri; +import static org.slf4j.LoggerFactory.getLogger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; @@ -28,15 +42,17 @@ import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.onlab.packet.ChassisId; import org.onlab.util.Frequency; -import org.onosproject.cfg.ComponentConfigService; import org.onlab.util.Spectrum; +import org.onosproject.cfg.ComponentConfigService; import org.onosproject.net.AnnotationKeys; import org.onosproject.net.ChannelSpacing; import org.onosproject.net.DefaultAnnotations; +import org.onosproject.net.Device; import org.onosproject.net.DeviceId; import org.onosproject.net.GridType; import org.onosproject.net.MastershipRole; import org.onosproject.net.OchSignal; +import org.onosproject.net.OduCltPort; import org.onosproject.net.OduSignalType; import org.onosproject.net.Port; import org.onosproject.net.PortNumber; @@ -49,6 +65,7 @@ import org.onosproject.net.device.DeviceProvider; import org.onosproject.net.device.DeviceProviderRegistry; import org.onosproject.net.device.DeviceProviderService; import org.onosproject.net.device.OchPortDescription; +import org.onosproject.net.device.OduCltPortDescription; import org.onosproject.net.device.OmsPortDescription; import org.onosproject.net.device.PortDescription; import org.onosproject.net.device.PortStatistics; @@ -64,13 +81,19 @@ import org.onosproject.openflow.controller.PortDescPropertyType; import org.onosproject.openflow.controller.RoleState; import org.osgi.service.component.ComponentContext; import org.projectfloodlight.openflow.protocol.OFCalientPortDescStatsEntry; +import org.projectfloodlight.openflow.protocol.OFExpPort; +import org.projectfloodlight.openflow.protocol.OFExpPortDescPropOpticalTransport; +import org.projectfloodlight.openflow.protocol.OFExpPortOpticalTransportLayerEntry; import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFObject; import org.projectfloodlight.openflow.protocol.OFPortConfig; import org.projectfloodlight.openflow.protocol.OFPortDesc; import org.projectfloodlight.openflow.protocol.OFPortDescPropOpticalTransport; import org.projectfloodlight.openflow.protocol.OFPortFeatures; import org.projectfloodlight.openflow.protocol.OFPortOptical; +import org.projectfloodlight.openflow.protocol.OFPortOpticalTransportLayerClass; +import org.projectfloodlight.openflow.protocol.OFPortOpticalTransportSignalType; import org.projectfloodlight.openflow.protocol.OFPortReason; import org.projectfloodlight.openflow.protocol.OFPortState; import org.projectfloodlight.openflow.protocol.OFPortStatsEntry; @@ -83,23 +106,10 @@ import org.projectfloodlight.openflow.protocol.OFVersion; import org.projectfloodlight.openflow.types.PortSpeed; import org.slf4j.Logger; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Dictionary; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Strings.isNullOrEmpty; -import static org.onlab.util.Tools.get; -import static org.onosproject.net.DeviceId.deviceId; -import static org.onosproject.net.Port.Type.COPPER; -import static org.onosproject.net.Port.Type.FIBER; -import static org.onosproject.openflow.controller.Dpid.dpid; -import static org.onosproject.openflow.controller.Dpid.uri; -import static org.slf4j.LoggerFactory.getLogger; +import com.google.common.base.Strings; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; /** * Provider which uses an OpenFlow controller to detect network @@ -109,7 +119,11 @@ import static org.slf4j.LoggerFactory.getLogger; public class OpenFlowDeviceProvider extends AbstractProvider implements DeviceProvider { private static final Logger LOG = getLogger(OpenFlowDeviceProvider.class); + private static final long MBPS = 1_000 * 1_000; + private static final Frequency FREQ100 = Frequency.ofGHz(100); + private static final Frequency FREQ193_1 = Frequency.ofTHz(193.1); + private static final Frequency FREQ4_4 = Frequency.ofTHz(4.4); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected DeviceProviderRegistry providerRegistry; @@ -145,27 +159,16 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr providerService = providerRegistry.register(this); controller.addListener(listener); controller.addEventListener(listener); - for (OpenFlowSwitch sw : controller.getSwitches()) { - try { - listener.switchAdded(new Dpid(sw.getId())); - } catch (Exception e) { - LOG.warn("Failed initially adding {} : {}", sw.getStringId(), e.getMessage()); - LOG.debug("Error details:", e); - // disconnect to trigger switch-add later - sw.disconnectSwitch(); - } - PortStatsCollector psc = new PortStatsCollector(sw, portStatsPollFrequency); - psc.start(); - collectors.put(new Dpid(sw.getId()), psc); - } + connectInitialDevices(); LOG.info("Started"); } @Deactivate public void deactivate(ComponentContext context) { cfgService.unregisterProperties(getClass(), false); - providerRegistry.unregister(this); controller.removeListener(listener); + disconnectDevices(); + providerRegistry.unregister(this); collectors.values().forEach(PortStatsCollector::stop); providerService = null; LOG.info("Stopped"); @@ -191,13 +194,31 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr LOG.info("Settings: portStatsPollFrequency={}", portStatsPollFrequency); } + private void connectInitialDevices() { + for (OpenFlowSwitch sw : controller.getSwitches()) { + try { + listener.switchAdded(new Dpid(sw.getId())); + } catch (Exception e) { + LOG.warn("Failed initially adding {} : {}", sw.getStringId(), e.getMessage()); + LOG.debug("Error details:", e); + // disconnect to trigger switch-add later + sw.disconnectSwitch(); + } + PortStatsCollector psc = new PortStatsCollector(sw, portStatsPollFrequency); + psc.start(); + collectors.put(new Dpid(sw.getId()), psc); + } + } + + private void disconnectDevices() { + // Only disconnect the devices for which we are currently master. + controller.getMasterSwitches().forEach(sw -> listener.switchRemoved(new Dpid(sw.getId()))); + } + @Override public boolean isReachable(DeviceId deviceId) { OpenFlowSwitch sw = controller.getSwitch(dpid(deviceId.uri())); - if (sw == null || !sw.isConnected()) { - return false; - } - return true; + return sw != null && sw.isConnected(); } @Override @@ -302,8 +323,9 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr ChassisId cId = new ChassisId(dpid.value()); SparseAnnotations annotations = DefaultAnnotations.builder() - .set("protocol", sw.factory().getVersion().toString()) - .set("channelId", sw.channelId()) + .set(AnnotationKeys.PROTOCOL, sw.factory().getVersion().toString()) + .set(AnnotationKeys.CHANNEL_ID, sw.channelId()) + .set(AnnotationKeys.MANAGEMENT_ADDRESS, sw.channelId().split(":")[0]) .build(); DeviceDescription description = @@ -386,18 +408,27 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr */ private List<PortDescription> buildPortDescriptions(OpenFlowSwitch sw) { final List<PortDescription> portDescs = new ArrayList<>(sw.getPorts().size()); - sw.getPorts().forEach(port -> portDescs.add(buildPortDescription(port))); + if (!(Device.Type.ROADM.equals(sw.deviceType()))) { + sw.getPorts().forEach(port -> portDescs.add(buildPortDescription(port))); + } OpenFlowOpticalSwitch opsw; switch (sw.deviceType()) { case ROADM: opsw = (OpenFlowOpticalSwitch) sw; + List<OFPortDesc> ports = opsw.getPorts(); + LOG.debug("SW ID {} , ETH- ODU CLT Ports {}", opsw.getId(), ports); + // ODU client ports are reported as ETH + ports.forEach(port -> portDescs.add(buildOduCltPortDescription(port))); + opsw.getPortTypes().forEach(type -> { - opsw.getPortsOf(type).forEach( - op -> { - portDescs.add(buildPortDescription(type, (OFPortOptical) op)); - } - ); + List<? extends OFObject> portsOf = opsw.getPortsOf(type); + LOG.debug("Ports Of{}", portsOf); + portsOf.forEach( + op -> { + portDescs.add(buildPortDescription(type, (OFObject) op)); + } + ); }); break; case FIBER_SWITCH: @@ -417,6 +448,105 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr return portDescs; } + private PortDescription buildOduCltPortDescription(OFPortDesc port) { + PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber()); + boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN) && + !port.getConfig().contains(OFPortConfig.PORT_DOWN); + Long portSpeed = portSpeed(port); + OduCltPort.SignalType sigType = null; + + switch (portSpeed.toString()) { + case "1": + sigType = OduCltPort.SignalType.CLT_1GBE; + break; + case "10": + sigType = OduCltPort.SignalType.CLT_10GBE; + break; + case "40": + sigType = OduCltPort.SignalType.CLT_40GBE; + break; + case "100": + sigType = OduCltPort.SignalType.CLT_100GBE; + break; + default: + throw new RuntimeException("Un recognize OduClt speed: " + portSpeed.toString()); + } + + SparseAnnotations annotations = buildOduCltAnnotation(port); + return new OduCltPortDescription(portNo, enabled, sigType, annotations); + } + + private SparseAnnotations buildOduCltAnnotation(OFPortDesc port) { + SparseAnnotations annotations = null; + String portName = Strings.emptyToNull(port.getName()); + if (portName != null) { + annotations = DefaultAnnotations.builder() + .set(AnnotationKeys.PORT_NAME, portName) + .set(AnnotationKeys.STATIC_PORT, Boolean.TRUE.toString()).build(); + } + return annotations; + } + + private PortDescription buildPortDescription(PortDescPropertyType ptype, OFObject port) { + if (port instanceof OFPortOptical) { + return buildPortDescription(ptype, (OFPortOptical) port); + } + return buildPortDescription(ptype, (OFExpPort) port); + } + + /** + * Build a portDescription from a given a port description describing some + * Optical port. + * + * @param ptype description property type. + * @param port the port to build from. + * @return portDescription for the port. + */ + private PortDescription buildPortDescription(PortDescPropertyType ptype, OFExpPort port) { + PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber()); + boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN) + && !port.getConfig().contains(OFPortConfig.PORT_DOWN); + SparseAnnotations annotations = makePortNameAnnotation(port.getName()); + + OFExpPortDescPropOpticalTransport firstProp = port.getProperties().get(0); + OFPortOpticalTransportSignalType sigType = firstProp.getPortSignalType(); + + DefaultPortDescription portDes = null; + switch (sigType) { + case OMSN: + portDes = new OmsPortDescription(portNo, enabled, FREQ193_1, FREQ193_1.add(FREQ4_4), + FREQ100, annotations); + break; + case OCH: + OFExpPortOpticalTransportLayerEntry entry = firstProp.getFeatures().get(0).getValue().get(0); + OFPortOpticalTransportLayerClass layerClass = entry.getLayerClass(); + if (!OFPortOpticalTransportLayerClass.ODU.equals(layerClass)) { + LOG.error("Unsupported layer Class {} ", layerClass); + return null; + } + + // convert to ONOS OduSignalType + OduSignalType oduSignalType = OpenFlowDeviceValueMapper. + lookupOduSignalType((byte) entry.getSignalType()); + //OchSignal is needed for OchPortDescription constructor, + //yet not relevant for tunable OCH port, creating with default parameters + OchSignal signalId = new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, 1, 1); + + portDes = new OchPortDescription(portNo, enabled, + oduSignalType, true, signalId, annotations); + + break; + case OTU2: + case OTU4: + LOG.error("Signal tpye OTU2/4 not supported yet ", port.toString()); + break; + default: + break; + } + + return portDes; + } + /** * Creates an annotation for the port name if one is available. * @@ -565,5 +695,4 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr } } } - } diff --git a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceValueMapper.java b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceValueMapper.java new file mode 100644 index 00000000..7bdf06fd --- /dev/null +++ b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceValueMapper.java @@ -0,0 +1,73 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.provider.of.device.impl; + +import org.onosproject.net.OduSignalType; + +import com.google.common.collect.BiMap; +import com.google.common.collect.EnumHashBiMap; + +/** + * Collection of helper methods to convert protocol agnostic models to values used in OpenFlow spec. + */ +final class OpenFlowDeviceValueMapper { + + // prohibit instantiation + private OpenFlowDeviceValueMapper() {} + + private static final BiMap<OduSignalType, Byte> ODU_SIGNAL_TYPES = EnumHashBiMap.create(OduSignalType.class); + static { + // See ONF "Optical Transport Protocol Extensions Version 1.0" for the following values + ODU_SIGNAL_TYPES.put(OduSignalType.ODU1, (byte) 1); // OFPODUT_ODU1 of enum ofp_odu_signal_type + ODU_SIGNAL_TYPES.put(OduSignalType.ODU2, (byte) 2); // OFPODUT_ODU2 of enum ofp_odu_signal_type + ODU_SIGNAL_TYPES.put(OduSignalType.ODU3, (byte) 3); // OFPODUT_ODU3 of enum ofp_odu_signal_type + ODU_SIGNAL_TYPES.put(OduSignalType.ODU4, (byte) 4); // OFPODUT_ODU4 of enum ofp_odu_signal_type + ODU_SIGNAL_TYPES.put(OduSignalType.ODU0, (byte) 10); // OFPODUT_ODU0 of enum ofp_odu_signal_type + ODU_SIGNAL_TYPES.put(OduSignalType.ODU2e, (byte) 11); // OFPODUT_ODU2E of enum ofp_odu_signal_type + } + + /** + * Looks up the specified input value to the corresponding value with the specified map. + * + * @param map bidirectional mapping + * @param input input value + * @param cls class of output value + * @param <I> type of input value + * @param <O> type of output value + * @return the corresponding value stored in the specified map + */ + private static <I, O> O lookup(BiMap<I, O> map, I input, Class<O> cls) { + if (!map.containsKey(input)) { + throw new RuntimeException( + String.format("No mapping found for %s when converting to %s", input, cls.getName())); + } + + return map.get(input); + } + + /** + * Looks up the the corresponding {@link OduSignalType} instance + * from the specified byte value for ODU signal type defined in + * ONF "Optical Transport Protocol Extensions Version 1.0". + * + * @param signalType byte value as ODU (Optical channel Data Unit) signal type defined the spec + * @return the corresponding OchSignalType instance + */ + static OduSignalType lookupOduSignalType(byte signalType) { + return lookup(ODU_SIGNAL_TYPES.inverse(), signalType, OduSignalType.class); + } + +} diff --git a/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java b/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java index 7b4d7922..d0838bb8 100644 --- a/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java +++ b/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java @@ -16,6 +16,7 @@ package org.onosproject.provider.of.device.impl; import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; @@ -236,7 +237,7 @@ public class OpenFlowDeviceProviderTest { @Override public Iterable<OpenFlowSwitch> getMasterSwitches() { - return null; + return ImmutableSet.of(); } @Override diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java index f238bdb1..cf918605 100644 --- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java @@ -221,11 +221,6 @@ public class FlowEntryBuilder { private TrafficTreatment buildTreatment() { TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder(); - // If this is a drop rule - if (instructions.size() == 0) { - builder.drop(); - return builder.build(); - } for (OFInstruction in : instructions) { switch (in.getType()) { case GOTO_TABLE: diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java index c9de4500..f77819d5 100644 --- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java @@ -142,7 +142,7 @@ public class FlowModBuilderVer10 extends FlowModBuilder { for (Instruction i : treatment.immediate()) { switch (i.type()) { case DROP: - log.warn("Saw drop action; assigning drop action"); + case NOACTION: return Collections.emptyList(); case L2MODIFICATION: act = buildL2Modification(i); diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java index 8918d337..cc265758 100644 --- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java @@ -123,6 +123,9 @@ public class FlowModBuilderVer13 extends FlowModBuilder { if (treatment.writeMetadata() != null) { instructions.add(buildMetadata(treatment.writeMetadata())); } + if (treatment.metered() != null) { + instructions.add(buildMeter(treatment.metered())); + } long cookie = flowRule().id().value(); @@ -212,6 +215,7 @@ public class FlowModBuilderVer13 extends FlowModBuilder { for (Instruction i : treatments) { switch (i.type()) { case DROP: + case NOACTION: return Collections.emptyList(); case L0MODIFICATION: actions.add(buildL0Modification(i)); diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java new file mode 100644 index 00000000..a81367cd --- /dev/null +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NewAdaptiveFlowStatsCollector.java @@ -0,0 +1,881 @@ +/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.provider.of.flow.impl;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+import org.onosproject.net.flow.DefaultTypedFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowId;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.StoredFlowEntry;
+import org.onosproject.net.flow.TypedStoredFlowEntry;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFFlowStatsRequest;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.TableId;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.flow.TypedStoredFlowEntry.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Efficiently and adaptively collects flow statistics for the specified switch.
+ */
+public class NewAdaptiveFlowStatsCollector {
+
+ private final Logger log = getLogger(getClass());
+
+ private final OpenFlowSwitch sw;
+
+ private ScheduledExecutorService adaptiveFlowStatsScheduler =
+ Executors.newScheduledThreadPool(4, groupedThreads("onos/flow", "device-stats-collector-%d"));
+ private ScheduledFuture<?> calAndShortFlowsThread;
+ private ScheduledFuture<?> midFlowsThread;
+ private ScheduledFuture<?> longFlowsThread;
+
+ // Task that calculates all flowEntries' FlowLiveType and collects stats IMMEDIATE flows every calAndPollInterval
+ private CalAndShortFlowsTask calAndShortFlowsTask;
+ // Task that collects stats MID flows every 2*calAndPollInterval
+ private MidFlowsTask midFlowsTask;
+ // Task that collects stats LONG flows every 3*calAndPollInterval
+ private LongFlowsTask longFlowsTask;
+
+ private static final int CAL_AND_POLL_TIMES = 1; // must be always 0
+ private static final int MID_POLL_TIMES = 2; // variable greater or equal than 1
+ private static final int LONG_POLL_TIMES = 3; // variable greater or equal than MID_POLL_TIMES
+ //TODO: make ENTIRE_POLL_TIMES configurable with enable or disable
+ // must be variable greater or equal than common multiple of MID_POLL_TIMES and LONG_POLL_TIMES
+ private static final int ENTIRE_POLL_TIMES = 6;
+
+ private static final int DEFAULT_CAL_AND_POLL_FREQUENCY = 5;
+ private static final int MIN_CAL_AND_POLL_FREQUENCY = 2;
+ private static final int MAX_CAL_AND_POLL_FREQUENCY = 60;
+
+ private int calAndPollInterval; // CAL_AND_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;
+ private int midPollInterval; // MID_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;
+ private int longPollInterval; // LONG_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;
+ // only used for checking condition at each task if it collects entire flows from a given switch or not
+ private int entirePollInterval; // ENTIRE_POLL_TIMES * DEFAULT_CAL_AND_POLL_FREQUENCY;
+
+ // Number of call count of each Task,
+ // for undoing collection except only entire flows collecting task in CalAndShortFlowsTask
+ private int callCountCalAndShortFlowsTask = 0; // increased CAL_AND_POLL_TIMES whenever Task is called
+ private int callCountMidFlowsTask = 0; // increased MID_POLL_TIMES whenever Task is called
+ private int callCountLongFlowsTask = 0; // increased LONG_POLL_TIMES whenever Task is called
+
+ private InternalDeviceFlowTable deviceFlowTable = new InternalDeviceFlowTable();
+
+ private boolean isFirstTimeStart = true;
+
+ public static final long NO_FLOW_MISSING_XID = (-1);
+ private long flowMissingXid = NO_FLOW_MISSING_XID;
+
+ /**
+ * Creates a new adaptive collector for the given switch and default cal_and_poll frequency.
+ *
+ * @param sw switch to pull
+ * @param pollInterval cal and immediate poll frequency in seconds
+ */
+ NewAdaptiveFlowStatsCollector(OpenFlowSwitch sw, int pollInterval) {
+ this.sw = sw;
+
+ initMemberVars(pollInterval);
+ }
+
+ // check calAndPollInterval validity and set all pollInterval values and finally initialize each task call count
+ private void initMemberVars(int pollInterval) {
+ if (pollInterval < MIN_CAL_AND_POLL_FREQUENCY) {
+ this.calAndPollInterval = MIN_CAL_AND_POLL_FREQUENCY;
+ } else if (pollInterval >= MAX_CAL_AND_POLL_FREQUENCY) {
+ this.calAndPollInterval = MAX_CAL_AND_POLL_FREQUENCY;
+ } else {
+ this.calAndPollInterval = pollInterval;
+ }
+
+ calAndPollInterval = CAL_AND_POLL_TIMES * calAndPollInterval;
+ midPollInterval = MID_POLL_TIMES * calAndPollInterval;
+ longPollInterval = LONG_POLL_TIMES * calAndPollInterval;
+ entirePollInterval = ENTIRE_POLL_TIMES * calAndPollInterval;
+
+ callCountCalAndShortFlowsTask = 0;
+ callCountMidFlowsTask = 0;
+ callCountLongFlowsTask = 0;
+
+ flowMissingXid = NO_FLOW_MISSING_XID;
+ }
+
+ /**
+ * Adjusts adaptive poll frequency.
+ *
+ * @param pollInterval poll frequency in seconds
+ */
+ synchronized void adjustCalAndPollInterval(int pollInterval) {
+ initMemberVars(pollInterval);
+
+ if (calAndShortFlowsThread != null) {
+ calAndShortFlowsThread.cancel(false);
+ }
+ if (midFlowsThread != null) {
+ midFlowsThread.cancel(false);
+ }
+ if (longFlowsThread != null) {
+ longFlowsThread.cancel(false);
+ }
+
+ calAndShortFlowsTask = new CalAndShortFlowsTask();
+ calAndShortFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ calAndShortFlowsTask,
+ 0,
+ calAndPollInterval,
+ TimeUnit.SECONDS);
+
+ midFlowsTask = new MidFlowsTask();
+ midFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ midFlowsTask,
+ 0,
+ midPollInterval,
+ TimeUnit.SECONDS);
+
+ longFlowsTask = new LongFlowsTask();
+ longFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ longFlowsTask,
+ 0,
+ longPollInterval,
+ TimeUnit.SECONDS);
+
+ log.debug("calAndPollInterval=" + calAndPollInterval + "is adjusted");
+ }
+
+ private class CalAndShortFlowsTask implements Runnable {
+ @Override
+ public void run() {
+ if (sw.getRole() == RoleState.MASTER) {
+ log.trace("CalAndShortFlowsTask Collecting AdaptiveStats for {}", sw.getStringId());
+
+ if (isFirstTimeStart) {
+ // isFirstTimeStart, get entire flow stats from a given switch sw
+ log.trace("CalAndShortFlowsTask Collecting Entire AdaptiveStats at first time start for {}",
+ sw.getStringId());
+ ofFlowStatsRequestAllSend();
+
+ callCountCalAndShortFlowsTask += CAL_AND_POLL_TIMES;
+ isFirstTimeStart = false;
+ } else if (callCountCalAndShortFlowsTask == ENTIRE_POLL_TIMES) {
+ // entire_poll_times, get entire flow stats from a given switch sw
+ log.trace("CalAndShortFlowsTask Collecting Entire AdaptiveStats for {}", sw.getStringId());
+ ofFlowStatsRequestAllSend();
+
+ callCountCalAndShortFlowsTask = CAL_AND_POLL_TIMES;
+ //TODO: check flows deleted in switch, but exist in controller flow table, then remove them
+ //
+ } else {
+ calAndShortFlowsTaskInternal();
+ callCountCalAndShortFlowsTask += CAL_AND_POLL_TIMES;
+ }
+ }
+ }
+ }
+
+ // send openflow flow stats request message with getting all flow entries to a given switch sw
+ private void ofFlowStatsRequestAllSend() {
+ OFFlowStatsRequest request = sw.factory().buildFlowStatsRequest()
+ .setMatch(sw.factory().matchWildcardAll())
+ .setTableId(TableId.ALL)
+ .setOutPort(OFPort.NO_MASK)
+ .build();
+
+ synchronized (this) {
+ // set the request xid to check the reply in OpenFlowRuleProvider
+ // After processing the reply of this request message,
+ // this must be set to NO_FLOW_MISSING_XID(-1) by provider
+ setFlowMissingXid(request.getXid());
+ log.debug("ofFlowStatsRequestAllSend,Request={},for {}", request.toString(), sw.getStringId());
+
+ sw.sendMsg(request);
+ }
+ }
+
+ // send openflow flow stats request message with getting the specific flow entry(fe) to a given switch sw
+ private void ofFlowStatsRequestFlowSend(FlowEntry fe) {
+ // set find match
+ Match match = FlowModBuilder.builder(fe, sw.factory(), Optional.empty()).buildMatch();
+ // set find tableId
+ TableId tableId = TableId.of(fe.tableId());
+ // set output port
+ Instruction ins = fe.treatment().allInstructions().stream()
+ .filter(i -> (i.type() == Instruction.Type.OUTPUT))
+ .findFirst()
+ .orElse(null);
+ OFPort ofPort = OFPort.NO_MASK;
+ if (ins != null) {
+ Instructions.OutputInstruction out = (Instructions.OutputInstruction) ins;
+ ofPort = OFPort.of((int) ((out.port().toLong())));
+ }
+
+ OFFlowStatsRequest request = sw.factory().buildFlowStatsRequest()
+ .setMatch(match)
+ .setTableId(tableId)
+ .setOutPort(ofPort)
+ .build();
+
+ synchronized (this) {
+ if (getFlowMissingXid() != NO_FLOW_MISSING_XID) {
+ log.debug("ofFlowStatsRequestFlowSend: previous FlowStatsRequestAll does not be processed yet,"
+ + " set no flow missing xid anyway, for {}",
+ sw.getStringId());
+ setFlowMissingXid(NO_FLOW_MISSING_XID);
+ }
+
+ sw.sendMsg(request);
+ }
+ }
+
+ private void calAndShortFlowsTaskInternal() {
+ deviceFlowTable.checkAndMoveLiveFlowAll();
+
+ deviceFlowTable.getShortFlows().forEach(fe -> {
+ ofFlowStatsRequestFlowSend(fe);
+ });
+ }
+
+ private class MidFlowsTask implements Runnable {
+ @Override
+ public void run() {
+ if (sw.getRole() == RoleState.MASTER) {
+ log.trace("MidFlowsTask Collecting AdaptiveStats for {}", sw.getStringId());
+
+ // skip collecting because CalAndShortFlowsTask collects entire flow stats from a given switch sw
+ if (callCountMidFlowsTask == ENTIRE_POLL_TIMES) {
+ callCountMidFlowsTask = MID_POLL_TIMES;
+ } else {
+ midFlowsTaskInternal();
+ callCountMidFlowsTask += MID_POLL_TIMES;
+ }
+ }
+ }
+ }
+
+ private void midFlowsTaskInternal() {
+ deviceFlowTable.getMidFlows().forEach(fe -> {
+ ofFlowStatsRequestFlowSend(fe);
+ });
+ }
+
+ private class LongFlowsTask implements Runnable {
+ @Override
+ public void run() {
+ if (sw.getRole() == RoleState.MASTER) {
+ log.trace("LongFlowsTask Collecting AdaptiveStats for {}", sw.getStringId());
+
+ // skip collecting because CalAndShortFlowsTask collects entire flow stats from a given switch sw
+ if (callCountLongFlowsTask == ENTIRE_POLL_TIMES) {
+ callCountLongFlowsTask = LONG_POLL_TIMES;
+ } else {
+ longFlowsTaskInternal();
+ callCountLongFlowsTask += LONG_POLL_TIMES;
+ }
+ }
+ }
+ }
+
+ private void longFlowsTaskInternal() {
+ deviceFlowTable.getLongFlows().forEach(fe -> {
+ ofFlowStatsRequestFlowSend(fe);
+ });
+ }
+
+ /**
+ * start adaptive flow statistic collection.
+ *
+ */
+ public synchronized void start() {
+ log.debug("Starting AdaptiveStats collection thread for {}", sw.getStringId());
+ callCountCalAndShortFlowsTask = 0;
+ callCountMidFlowsTask = 0;
+ callCountLongFlowsTask = 0;
+
+ isFirstTimeStart = true;
+
+ // Initially start polling quickly. Then drop down to configured value
+ calAndShortFlowsTask = new CalAndShortFlowsTask();
+ calAndShortFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ calAndShortFlowsTask,
+ 1,
+ calAndPollInterval,
+ TimeUnit.SECONDS);
+
+ midFlowsTask = new MidFlowsTask();
+ midFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ midFlowsTask,
+ 1,
+ midPollInterval,
+ TimeUnit.SECONDS);
+
+ longFlowsTask = new LongFlowsTask();
+ longFlowsThread = adaptiveFlowStatsScheduler.scheduleWithFixedDelay(
+ longFlowsTask,
+ 1,
+ longPollInterval,
+ TimeUnit.SECONDS);
+
+ log.info("Started");
+ }
+
+ /**
+ * stop adaptive flow statistic collection.
+ *
+ */
+ public synchronized void stop() {
+ log.debug("Stopping AdaptiveStats collection thread for {}", sw.getStringId());
+ if (calAndShortFlowsThread != null) {
+ calAndShortFlowsThread.cancel(true);
+ }
+ if (midFlowsThread != null) {
+ midFlowsThread.cancel(true);
+ }
+ if (longFlowsThread != null) {
+ longFlowsThread.cancel(true);
+ }
+
+ adaptiveFlowStatsScheduler.shutdownNow();
+
+ isFirstTimeStart = false;
+
+ log.info("Stopped");
+ }
+
+ /**
+ * add typed flow entry from flow rule into the internal flow table.
+ *
+ * @param flowRules the flow rules
+ *
+ */
+ public synchronized void addWithFlowRule(FlowRule... flowRules) {
+ for (FlowRule fr : flowRules) {
+ // First remove old entry unconditionally, if exist
+ deviceFlowTable.remove(fr);
+
+ // add new flow entry, we suppose IMMEDIATE_FLOW
+ TypedStoredFlowEntry newFlowEntry = new DefaultTypedFlowEntry(fr,
+ FlowLiveType.IMMEDIATE_FLOW);
+ deviceFlowTable.addWithCalAndSetFlowLiveType(newFlowEntry);
+ }
+ }
+
+ /**
+ * add or update typed flow entry from flow entry into the internal flow table.
+ *
+ * @param flowEntries the flow entries
+ *
+ */
+ public synchronized void addOrUpdateFlows(FlowEntry... flowEntries) {
+ for (FlowEntry fe : flowEntries) {
+ // check if this new rule is an update to an existing entry
+ TypedStoredFlowEntry stored = deviceFlowTable.getFlowEntry(fe);
+
+ if (stored != null) {
+ // duplicated flow entry is collected!, just skip
+ if (fe.bytes() == stored.bytes() && fe.packets() == stored.packets()
+ && fe.life() == stored.life()) {
+ log.debug("addOrUpdateFlows:, FlowId=" + Long.toHexString(fe.id().value())
+ + ",is DUPLICATED stats collection, just skip."
+ + " AdaptiveStats collection thread for {}",
+ sw.getStringId());
+
+ stored.setLastSeen();
+ continue;
+ } else if (fe.life() < stored.life()) {
+ // Invalid updates the stats values, i.e., bytes, packets, durations ...
+ log.debug("addOrUpdateFlows():" +
+ " Invalid Flow Update! The new life is SMALLER than the previous one, jus skip." +
+ " new flowId=" + Long.toHexString(fe.id().value()) +
+ ", old flowId=" + Long.toHexString(stored.id().value()) +
+ ", new bytes=" + fe.bytes() + ", old bytes=" + stored.bytes() +
+ ", new life=" + fe.life() + ", old life=" + stored.life() +
+ ", new lastSeen=" + fe.lastSeen() + ", old lastSeen=" + stored.lastSeen());
+ // go next
+ stored.setLastSeen();
+ continue;
+ }
+
+ // update now
+ stored.setLife(fe.life());
+ stored.setPackets(fe.packets());
+ stored.setBytes(fe.bytes());
+ stored.setLastSeen();
+ if (stored.state() == FlowEntry.FlowEntryState.PENDING_ADD) {
+ // flow is really RULE_ADDED
+ stored.setState(FlowEntry.FlowEntryState.ADDED);
+ }
+ // flow is RULE_UPDATED, skip adding and just updating flow live table
+ //deviceFlowTable.calAndSetFlowLiveType(stored);
+ continue;
+ }
+
+ // add new flow entry, we suppose IMMEDIATE_FLOW
+ TypedStoredFlowEntry newFlowEntry = new DefaultTypedFlowEntry(fe,
+ FlowLiveType.IMMEDIATE_FLOW);
+ deviceFlowTable.addWithCalAndSetFlowLiveType(newFlowEntry);
+ }
+ }
+
+ /**
+ * remove typed flow entry from the internal flow table.
+ *
+ * @param flowRules the flow entries
+ *
+ */
+ public synchronized void removeFlows(FlowRule... flowRules) {
+ for (FlowRule rule : flowRules) {
+ deviceFlowTable.remove(rule);
+ }
+ }
+
+ // same as removeFlows() function
+ /**
+ * remove typed flow entry from the internal flow table.
+ *
+ * @param flowRules the flow entries
+ *
+ */
+ public void flowRemoved(FlowRule... flowRules) {
+ removeFlows(flowRules);
+ }
+
+ // same as addOrUpdateFlows() function
+ /**
+ * add or update typed flow entry from flow entry into the internal flow table.
+ *
+ * @param flowEntries the flow entry list
+ *
+ */
+ public void pushFlowMetrics(List<FlowEntry> flowEntries) {
+ flowEntries.forEach(fe -> {
+ addOrUpdateFlows(fe);
+ });
+ }
+
+ /**
+ * returns flowMissingXid that indicates the execution of flowMissing process or not(NO_FLOW_MISSING_XID(-1)).
+ *
+ */
+ public long getFlowMissingXid() {
+ return flowMissingXid;
+ }
+
+ /**
+ * set flowMissingXid, namely OFFlowStatsRequest match any ALL message Id.
+ *
+ * @param flowMissingXid the OFFlowStatsRequest message Id
+ *
+ */
+ public void setFlowMissingXid(long flowMissingXid) {
+ this.flowMissingXid = flowMissingXid;
+ }
+
+ private class InternalDeviceFlowTable {
+
+ private final Map<FlowId, Set<TypedStoredFlowEntry>>
+ flowEntries = Maps.newConcurrentMap();
+
+ private final Set<StoredFlowEntry> shortFlows = new HashSet<>();
+ private final Set<StoredFlowEntry> midFlows = new HashSet<>();
+ private final Set<StoredFlowEntry> longFlows = new HashSet<>();
+
+ // Assumed latency adjustment(default=500 millisecond) between FlowStatsRequest and Reply
+ private final long latencyFlowStatsRequestAndReplyMillis = 500;
+
+
+ // Statistics for table operation
+ private long addCount = 0, addWithSetFlowLiveTypeCount = 0;
+ private long removeCount = 0;
+
+ /**
+ * Resets all count values with zero.
+ *
+ */
+ public void resetAllCount() {
+ addCount = 0;
+ addWithSetFlowLiveTypeCount = 0;
+ removeCount = 0;
+ }
+
+ // get set of flow entries for the given flowId
+ private Set<TypedStoredFlowEntry> getFlowEntriesInternal(FlowId flowId) {
+ return flowEntries.computeIfAbsent(flowId, id -> Sets.newCopyOnWriteArraySet());
+ }
+
+ // get flow entry for the given flow rule
+ private TypedStoredFlowEntry getFlowEntryInternal(FlowRule rule) {
+ Set<TypedStoredFlowEntry> flowEntries = getFlowEntriesInternal(rule.id());
+ return flowEntries.stream()
+ .filter(entry -> Objects.equal(entry, rule))
+ .findAny()
+ .orElse(null);
+ }
+
+ // get the flow entries for all flows in flow table
+ private Set<TypedStoredFlowEntry> getFlowEntriesInternal() {
+ Set<TypedStoredFlowEntry> result = Sets.newHashSet();
+
+ flowEntries.values().forEach(result::addAll);
+ return result;
+ }
+
+ /**
+ * Gets the number of flow entry in flow table.
+ *
+ * @return the number of flow entry.
+ *
+ */
+ public long getFlowCount() {
+ return flowEntries.values().stream().mapToLong(Set::size).sum();
+ }
+
+ /**
+ * Gets the number of flow entry in flow table.
+ *
+ * @param rule the flow rule
+ * @return the typed flow entry.
+ *
+ */
+ public TypedStoredFlowEntry getFlowEntry(FlowRule rule) {
+ checkNotNull(rule);
+
+ return getFlowEntryInternal(rule);
+ }
+
+ /**
+ * Gets the all typed flow entries in flow table.
+ *
+ * @return the set of typed flow entry.
+ *
+ */
+ public Set<TypedStoredFlowEntry> getFlowEntries() {
+ return getFlowEntriesInternal();
+ }
+
+ /**
+ * Gets the short typed flow entries in flow table.
+ *
+ * @return the set of typed flow entry.
+ *
+ */
+ public Set<StoredFlowEntry> getShortFlows() {
+ return ImmutableSet.copyOf(shortFlows); //Sets.newHashSet(shortFlows);
+ }
+
+ /**
+ * Gets the mid typed flow entries in flow table.
+ *
+ * @return the set of typed flow entry.
+ *
+ */
+ public Set<StoredFlowEntry> getMidFlows() {
+ return ImmutableSet.copyOf(midFlows); //Sets.newHashSet(midFlows);
+ }
+
+ /**
+ * Gets the long typed flow entries in flow table.
+ *
+ * @return the set of typed flow entry.
+ *
+ */
+ public Set<StoredFlowEntry> getLongFlows() {
+ return ImmutableSet.copyOf(longFlows); //Sets.newHashSet(longFlows);
+ }
+
+ /**
+ * Add typed flow entry into table only.
+ *
+ * @param rule the flow rule
+ *
+ */
+ public synchronized void add(TypedStoredFlowEntry rule) {
+ checkNotNull(rule);
+
+ //rule have to be new DefaultTypedFlowEntry
+ boolean result = getFlowEntriesInternal(rule.id()).add(rule);
+
+ if (result) {
+ addCount++;
+ }
+ }
+
+ /**
+ * Calculates and set the flow live type at the first time,
+ * and then add it into a corresponding typed flow table.
+ *
+ * @param rule the flow rule
+ *
+ */
+ public void calAndSetFlowLiveType(TypedStoredFlowEntry rule) {
+ checkNotNull(rule);
+
+ calAndSetFlowLiveTypeInternal(rule);
+ }
+
+ /**
+ * Add the typed flow entry into table, and calculates and set the flow live type,
+ * and then add it into a corresponding typed flow table.
+ *
+ * @param rule the flow rule
+ *
+ */
+ public synchronized void addWithCalAndSetFlowLiveType(TypedStoredFlowEntry rule) {
+ checkNotNull(rule);
+
+ //rule have to be new DefaultTypedFlowEntry
+ boolean result = getFlowEntriesInternal(rule.id()).add(rule);
+ if (result) {
+ calAndSetFlowLiveTypeInternal(rule);
+ addWithSetFlowLiveTypeCount++;
+ } else {
+ log.debug("addWithCalAndSetFlowLiveType, FlowId=" + Long.toHexString(rule.id().value())
+ + " ADD Failed, cause it may already exists in table !!!,"
+ + " AdaptiveStats collection thread for {}",
+ sw.getStringId());
+ }
+ }
+
+ // In real, calculates and set the flow live type at the first time,
+ // and then add it into a corresponding typed flow table
+ private void calAndSetFlowLiveTypeInternal(TypedStoredFlowEntry rule) {
+ long life = rule.life();
+ FlowLiveType prevFlowLiveType = rule.flowLiveType();
+
+ if (life >= longPollInterval) {
+ rule.setFlowLiveType(FlowLiveType.LONG_FLOW);
+ longFlows.add(rule);
+ } else if (life >= midPollInterval) {
+ rule.setFlowLiveType(FlowLiveType.MID_FLOW);
+ midFlows.add(rule);
+ } else if (life >= calAndPollInterval) {
+ rule.setFlowLiveType(FlowLiveType.SHORT_FLOW);
+ shortFlows.add(rule);
+ } else if (life >= 0) {
+ rule.setFlowLiveType(FlowLiveType.IMMEDIATE_FLOW);
+ } else { // life < 0
+ rule.setFlowLiveType(FlowLiveType.UNKNOWN_FLOW);
+ }
+
+ if (rule.flowLiveType() != prevFlowLiveType) {
+ switch (prevFlowLiveType) {
+ // delete it from previous flow table
+ case SHORT_FLOW:
+ shortFlows.remove(rule);
+ break;
+ case MID_FLOW:
+ midFlows.remove(rule);
+ break;
+ case LONG_FLOW:
+ longFlows.remove(rule);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+
+ // check the flow live type based on current time, then set and add it into corresponding table
+ private boolean checkAndMoveLiveFlowInternal(TypedStoredFlowEntry fe, long cTime) {
+ long curTime = (cTime > 0 ? cTime : System.currentTimeMillis());
+ // For latency adjustment(default=500 millisecond) between FlowStatsRequest and Reply
+ long fromLastSeen = ((curTime - fe.lastSeen() + latencyFlowStatsRequestAndReplyMillis) / 1000);
+ // fe.life() unit is SECOND!
+ long liveTime = fe.life() + fromLastSeen;
+
+
+ switch (fe.flowLiveType()) {
+ case IMMEDIATE_FLOW:
+ if (liveTime >= longPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.LONG_FLOW);
+ longFlows.add(fe);
+ } else if (liveTime >= midPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.MID_FLOW);
+ midFlows.add(fe);
+ } else if (liveTime >= calAndPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.SHORT_FLOW);
+ shortFlows.add(fe);
+ }
+ break;
+ case SHORT_FLOW:
+ if (liveTime >= longPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.LONG_FLOW);
+ shortFlows.remove(fe);
+ longFlows.add(fe);
+ } else if (liveTime >= midPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.MID_FLOW);
+ shortFlows.remove(fe);
+ midFlows.add(fe);
+ }
+ break;
+ case MID_FLOW:
+ if (liveTime >= longPollInterval) {
+ fe.setFlowLiveType(FlowLiveType.LONG_FLOW);
+ midFlows.remove(fe);
+ longFlows.add(fe);
+ }
+ break;
+ case LONG_FLOW:
+ if (fromLastSeen > entirePollInterval) {
+ log.trace("checkAndMoveLiveFlowInternal, flow is already removed at switch.");
+ return false;
+ }
+ break;
+ case UNKNOWN_FLOW: // Unknown flow is an internal error flow type, just fall through
+ default :
+ // Error Unknown Live Type
+ log.error("checkAndMoveLiveFlowInternal, Unknown Live Type error!"
+ + "AdaptiveStats collection thread for {}",
+ sw.getStringId());
+ return false;
+ }
+
+ log.debug("checkAndMoveLiveFlowInternal, FlowId=" + Long.toHexString(fe.id().value())
+ + ", state=" + fe.state()
+ + ", After liveType=" + fe.flowLiveType()
+ + ", liveTime=" + liveTime
+ + ", life=" + fe.life()
+ + ", bytes=" + fe.bytes()
+ + ", packets=" + fe.packets()
+ + ", fromLastSeen=" + fromLastSeen
+ + ", priority=" + fe.priority()
+ + ", selector=" + fe.selector().criteria()
+ + ", treatment=" + fe.treatment()
+ + " AdaptiveStats collection thread for {}",
+ sw.getStringId());
+
+ return true;
+ }
+
+ /**
+ * Check and move live type for all type flow entries in table at every calAndPollInterval time.
+ *
+ */
+ public void checkAndMoveLiveFlowAll() {
+ Set<TypedStoredFlowEntry> typedFlowEntries = getFlowEntriesInternal();
+
+ long calCurTime = System.currentTimeMillis();
+ typedFlowEntries.forEach(fe -> {
+ if (!checkAndMoveLiveFlowInternal(fe, calCurTime)) {
+ remove(fe);
+ }
+ });
+
+ // print table counts for debug
+ if (log.isDebugEnabled()) {
+ synchronized (this) {
+ long totalFlowCount = getFlowCount();
+ long shortFlowCount = shortFlows.size();
+ long midFlowCount = midFlows.size();
+ long longFlowCount = longFlows.size();
+ long immediateFlowCount = totalFlowCount - shortFlowCount - midFlowCount - longFlowCount;
+ long calTotalCount = addCount + addWithSetFlowLiveTypeCount - removeCount;
+
+ log.debug("--------------------------------------------------------------------------- for {}",
+ sw.getStringId());
+ log.debug("checkAndMoveLiveFlowAll, Total Flow_Count=" + totalFlowCount
+ + ", add - remove_Count=" + calTotalCount
+ + ", IMMEDIATE_FLOW_Count=" + immediateFlowCount
+ + ", SHORT_FLOW_Count=" + shortFlowCount
+ + ", MID_FLOW_Count=" + midFlowCount
+ + ", LONG_FLOW_Count=" + longFlowCount
+ + ", add_Count=" + addCount
+ + ", addWithSetFlowLiveType_Count=" + addWithSetFlowLiveTypeCount
+ + ", remove_Count=" + removeCount
+ + " AdaptiveStats collection thread for {}", sw.getStringId());
+ log.debug("--------------------------------------------------------------------------- for {}",
+ sw.getStringId());
+ if (totalFlowCount != calTotalCount) {
+ log.error("checkAndMoveLiveFlowAll, Real total flow count and "
+ + "calculated total flow count do NOT match, something is wrong internally "
+ + "or check counter value bound is over!");
+ }
+ if (immediateFlowCount < 0) {
+ log.error("checkAndMoveLiveFlowAll, IMMEDIATE_FLOW count is negative, "
+ + "something is wrong internally "
+ + "or check counter value bound is over!");
+ }
+ }
+ }
+ log.trace("checkAndMoveLiveFlowAll, AdaptiveStats for {}", sw.getStringId());
+ }
+
+ /**
+ * Remove the typed flow entry from table.
+ *
+ * @param rule the flow rule
+ *
+ */
+ public synchronized void remove(FlowRule rule) {
+ checkNotNull(rule);
+
+ TypedStoredFlowEntry removeStore = getFlowEntryInternal(rule);
+ if (removeStore != null) {
+ removeLiveFlowsInternal((TypedStoredFlowEntry) removeStore);
+ boolean result = getFlowEntriesInternal(rule.id()).remove(removeStore);
+
+ if (result) {
+ removeCount++;
+ }
+ }
+ }
+
+ // Remove the typed flow entry from corresponding table
+ private void removeLiveFlowsInternal(TypedStoredFlowEntry fe) {
+ switch (fe.flowLiveType()) {
+ case IMMEDIATE_FLOW:
+ // do nothing
+ break;
+ case SHORT_FLOW:
+ shortFlows.remove(fe);
+ break;
+ case MID_FLOW:
+ midFlows.remove(fe);
+ break;
+ case LONG_FLOW:
+ longFlows.remove(fe);
+ break;
+ default: // error in Flow Live Type
+ log.error("removeLiveFlowsInternal, Unknown Live Type error!");
+ break;
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java index de079e03..6374ca55 100644 --- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 Open Networking Laboratory + * Copyright 2015 Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import com.google.common.cache.RemovalCause; import com.google.common.cache.RemovalNotification; import com.google.common.collect.Maps; import com.google.common.collect.Sets; + import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; @@ -32,6 +33,7 @@ import org.onosproject.cfg.ComponentConfigService; import org.onosproject.core.ApplicationId; import org.onosproject.net.DeviceId; import org.onosproject.net.flow.CompletedBatchOperation; +import org.onosproject.net.flow.DefaultTableStatisticsEntry; import org.onosproject.net.flow.FlowEntry; import org.onosproject.net.flow.FlowRule; import org.onosproject.net.flow.FlowRuleBatchEntry; @@ -40,6 +42,7 @@ import org.onosproject.net.flow.FlowRuleExtPayLoad; import org.onosproject.net.flow.FlowRuleProvider; import org.onosproject.net.flow.FlowRuleProviderRegistry; import org.onosproject.net.flow.FlowRuleProviderService; +import org.onosproject.net.flow.TableStatisticsEntry; import org.onosproject.net.provider.AbstractProvider; import org.onosproject.net.provider.ProviderId; import org.onosproject.net.statistic.DefaultLoad; @@ -58,6 +61,8 @@ import org.projectfloodlight.openflow.protocol.OFErrorType; import org.projectfloodlight.openflow.protocol.OFFlowMod; import org.projectfloodlight.openflow.protocol.OFFlowRemoved; import org.projectfloodlight.openflow.protocol.OFFlowStatsReply; +import org.projectfloodlight.openflow.protocol.OFTableStatsReply; +import org.projectfloodlight.openflow.protocol.OFTableStatsEntry; import org.projectfloodlight.openflow.protocol.OFMessage; import org.projectfloodlight.openflow.protocol.OFPortStatus; import org.projectfloodlight.openflow.protocol.OFStatsReply; @@ -70,12 +75,14 @@ import java.util.Collections; import java.util.Dictionary; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.Timer; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Strings.isNullOrEmpty; import static org.onlab.util.Tools.get; import static org.slf4j.LoggerFactory.getLogger; @@ -99,11 +106,16 @@ public class OpenFlowRuleProvider extends AbstractProvider @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected ComponentConfigService cfgService; - private static final int DEFAULT_POLL_FREQUENCY = 10; + private static final int DEFAULT_POLL_FREQUENCY = 5; @Property(name = "flowPollFrequency", intValue = DEFAULT_POLL_FREQUENCY, label = "Frequency (in seconds) for polling flow statistics") private int flowPollFrequency = DEFAULT_POLL_FREQUENCY; + private static final boolean DEFAULT_ADAPTIVE_FLOW_SAMPLING = true; + @Property(name = "adaptiveFlowSampling", boolValue = DEFAULT_ADAPTIVE_FLOW_SAMPLING, + label = "Adaptive Flow Sampling is on or off") + private boolean adaptiveFlowSampling = DEFAULT_ADAPTIVE_FLOW_SAMPLING; + private FlowRuleProviderService providerService; private final InternalFlowProvider listener = new InternalFlowProvider(); @@ -111,7 +123,12 @@ public class OpenFlowRuleProvider extends AbstractProvider private Cache<Long, InternalCacheEntry> pendingBatches; private final Timer timer = new Timer("onos-openflow-collector"); + private final Map<Dpid, FlowStatsCollector> simpleCollectors = Maps.newHashMap(); + + // NewAdaptiveFlowStatsCollector Set + private final Map<Dpid, NewAdaptiveFlowStatsCollector> afsCollectors = Maps.newHashMap(); private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap(); + private final Map<Dpid, TableStatisticsCollector> tableStatsCollectors = Maps.newHashMap(); /** * Creates an OpenFlow host provider. @@ -128,9 +145,11 @@ public class OpenFlowRuleProvider extends AbstractProvider controller.addEventListener(listener); pendingBatches = createBatchCache(); + createCollectors(); - log.info("Started"); + log.info("Started with flowPollFrequency = {}, adaptiveFlowSampling = {}", + flowPollFrequency, adaptiveFlowSampling); } @Deactivate @@ -161,6 +180,20 @@ public class OpenFlowRuleProvider extends AbstractProvider } log.info("Settings: flowPollFrequency={}", flowPollFrequency); + + boolean newAdaptiveFlowSampling; + String s = get(properties, "adaptiveFlowSampling"); + newAdaptiveFlowSampling = isNullOrEmpty(s) ? adaptiveFlowSampling : Boolean.parseBoolean(s.trim()); + + if (newAdaptiveFlowSampling != adaptiveFlowSampling) { + // stop previous collector + stopCollectors(); + adaptiveFlowSampling = newAdaptiveFlowSampling; + // create new collectors + createCollectors(); + } + + log.info("Settings: adaptiveFlowSampling={}", adaptiveFlowSampling); } private Cache<Long, InternalCacheEntry> createBatchCache() { @@ -179,19 +212,43 @@ public class OpenFlowRuleProvider extends AbstractProvider } private void createCollector(OpenFlowSwitch sw) { - FlowStatsCollector fsc = new FlowStatsCollector(timer, sw, flowPollFrequency); - fsc.start(); - collectors.put(new Dpid(sw.getId()), fsc); + if (adaptiveFlowSampling) { + // NewAdaptiveFlowStatsCollector Constructor + NewAdaptiveFlowStatsCollector fsc = new NewAdaptiveFlowStatsCollector(sw, flowPollFrequency); + fsc.start(); + afsCollectors.put(new Dpid(sw.getId()), fsc); + } else { + FlowStatsCollector fsc = new FlowStatsCollector(timer, sw, flowPollFrequency); + fsc.start(); + simpleCollectors.put(new Dpid(sw.getId()), fsc); + } + TableStatisticsCollector tsc = new TableStatisticsCollector(timer, sw, flowPollFrequency); + tsc.start(); + tableStatsCollectors.put(new Dpid(sw.getId()), tsc); } private void stopCollectors() { - collectors.values().forEach(FlowStatsCollector::stop); - collectors.clear(); + if (adaptiveFlowSampling) { + // NewAdaptiveFlowStatsCollector Destructor + afsCollectors.values().forEach(NewAdaptiveFlowStatsCollector::stop); + afsCollectors.clear(); + } else { + simpleCollectors.values().forEach(FlowStatsCollector::stop); + simpleCollectors.clear(); + } + tableStatsCollectors.values().forEach(TableStatisticsCollector::stop); + tableStatsCollectors.clear(); } private void adjustRate() { DefaultLoad.setPollInterval(flowPollFrequency); - collectors.values().forEach(fsc -> fsc.adjustPollInterval(flowPollFrequency)); + if (adaptiveFlowSampling) { + // NewAdaptiveFlowStatsCollector calAndPollInterval + afsCollectors.values().forEach(fsc -> fsc.adjustCalAndPollInterval(flowPollFrequency)); + } else { + simpleCollectors.values().forEach(fsc -> fsc.adjustPollInterval(flowPollFrequency)); + } + tableStatsCollectors.values().forEach(tsc -> tsc.adjustPollInterval(flowPollFrequency)); } @Override @@ -202,8 +259,9 @@ public class OpenFlowRuleProvider extends AbstractProvider } private void applyRule(FlowRule flowRule) { - OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId() - .uri())); + Dpid dpid = Dpid.dpid(flowRule.deviceId().uri()); + OpenFlowSwitch sw = controller.getSwitch(dpid); + FlowRuleExtPayLoad flowRuleExtPayLoad = flowRule.payLoad(); if (hasPayload(flowRuleExtPayLoad)) { OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad()); @@ -212,6 +270,14 @@ public class OpenFlowRuleProvider extends AbstractProvider } sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(), Optional.empty()).buildFlowAdd()); + + if (adaptiveFlowSampling) { + // Add TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector + NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid); + if (collector != null) { + collector.addWithFlowRule(flowRule); + } + } } @Override @@ -222,8 +288,9 @@ public class OpenFlowRuleProvider extends AbstractProvider } private void removeRule(FlowRule flowRule) { - OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId() - .uri())); + Dpid dpid = Dpid.dpid(flowRule.deviceId().uri()); + OpenFlowSwitch sw = controller.getSwitch(dpid); + FlowRuleExtPayLoad flowRuleExtPayLoad = flowRule.payLoad(); if (hasPayload(flowRuleExtPayLoad)) { OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad()); @@ -232,6 +299,14 @@ public class OpenFlowRuleProvider extends AbstractProvider } sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(), Optional.empty()).buildFlowDel()); + + if (adaptiveFlowSampling) { + // Remove TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector + NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid); + if (collector != null) { + collector.removeFlows(flowRule); + } + } } @Override @@ -242,11 +317,12 @@ public class OpenFlowRuleProvider extends AbstractProvider @Override public void executeBatch(FlowRuleBatchOperation batch) { + checkNotNull(batch); pendingBatches.put(batch.id(), new InternalCacheEntry(batch)); - OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(batch.deviceId() - .uri())); + Dpid dpid = Dpid.dpid(batch.deviceId().uri()); + OpenFlowSwitch sw = controller.getSwitch(dpid); OFFlowMod mod; for (FlowRuleBatchEntry fbe : batch.getOperations()) { // flow is the third party privacy flow @@ -257,21 +333,35 @@ public class OpenFlowRuleProvider extends AbstractProvider sw.sendMsg(msg); continue; } - FlowModBuilder builder = FlowModBuilder.builder(fbe.target(), sw - .factory(), Optional.of(batch.id())); + FlowModBuilder builder = + FlowModBuilder.builder(fbe.target(), sw.factory(), Optional.of(batch.id())); + NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid); switch (fbe.operator()) { case ADD: mod = builder.buildFlowAdd(); + if (adaptiveFlowSampling && collector != null) { + // Add TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector + collector.addWithFlowRule(fbe.target()); + } break; case REMOVE: mod = builder.buildFlowDel(); + if (adaptiveFlowSampling && collector != null) { + // Remove TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector + collector.removeFlows(fbe.target()); + } break; case MODIFY: mod = builder.buildFlowMod(); + if (adaptiveFlowSampling && collector != null) { + // Add or Update TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector + // afsCollectors.get(dpid).addWithFlowRule(fbe.target()); //check if add is good or not + collector.addOrUpdateFlows((FlowEntry) fbe.target()); + } break; default: log.error("Unsupported batch operation {}; skipping flowmod {}", - fbe.operator(), fbe); + fbe.operator(), fbe); continue; } sw.sendMsg(mod); @@ -292,14 +382,28 @@ public class OpenFlowRuleProvider extends AbstractProvider @Override public void switchAdded(Dpid dpid) { + + OpenFlowSwitch sw = controller.getSwitch(dpid); + createCollector(controller.getSwitch(dpid)); } @Override public void switchRemoved(Dpid dpid) { - FlowStatsCollector collector = collectors.remove(dpid); - if (collector != null) { - collector.stop(); + if (adaptiveFlowSampling) { + NewAdaptiveFlowStatsCollector collector = afsCollectors.remove(dpid); + if (collector != null) { + collector.stop(); + } + } else { + FlowStatsCollector collector = simpleCollectors.remove(dpid); + if (collector != null) { + collector.stop(); + } + } + TableStatisticsCollector tsc = tableStatsCollectors.remove(dpid); + if (tsc != null) { + tsc.stop(); } } @@ -321,10 +425,20 @@ public class OpenFlowRuleProvider extends AbstractProvider FlowEntry fr = new FlowEntryBuilder(dpid, removed).build(); providerService.flowRemoved(fr); + + if (adaptiveFlowSampling) { + // Removed TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector + NewAdaptiveFlowStatsCollector collector = afsCollectors.get(dpid); + if (collector != null) { + collector.flowRemoved(fr); + } + } break; case STATS_REPLY: if (((OFStatsReply) msg).getStatsType() == OFStatsType.FLOW) { pushFlowMetrics(dpid, (OFFlowStatsReply) msg); + } else if (((OFStatsReply) msg).getStatsType() == OFStatsType.TABLE) { + pushTableStatistics(dpid, (OFTableStatsReply) msg); } break; case BARRIER_REPLY: @@ -370,11 +484,10 @@ public class OpenFlowRuleProvider extends AbstractProvider + " tell us which one."); } } - break; + default: log.debug("Unhandled message type: {}", msg.getType()); } - } @Override @@ -386,13 +499,68 @@ public class OpenFlowRuleProvider extends AbstractProvider private void pushFlowMetrics(Dpid dpid, OFFlowStatsReply replies) { DeviceId did = DeviceId.deviceId(Dpid.uri(dpid)); - OpenFlowSwitch sw = controller.getSwitch(dpid); List<FlowEntry> flowEntries = replies.getEntries().stream() .map(entry -> new FlowEntryBuilder(dpid, entry).build()) .collect(Collectors.toList()); - providerService.pushFlowMetrics(did, flowEntries); + if (adaptiveFlowSampling) { + NewAdaptiveFlowStatsCollector afsc = afsCollectors.get(dpid); + + synchronized (afsc) { + if (afsc.getFlowMissingXid() != NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID) { + log.debug("OpenFlowRuleProvider:pushFlowMetrics, flowMissingXid={}, " + + "OFFlowStatsReply Xid={}, for {}", + afsc.getFlowMissingXid(), replies.getXid(), dpid); + } + + // Check that OFFlowStatsReply Xid is same with the one of OFFlowStatsRequest? + if (afsc.getFlowMissingXid() != NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID) { + if (afsc.getFlowMissingXid() == replies.getXid()) { + // call entire flow stats update with flowMissing synchronization. + // used existing pushFlowMetrics + providerService.pushFlowMetrics(did, flowEntries); + } + // reset flowMissingXid to NO_FLOW_MISSING_XID + afsc.setFlowMissingXid(NewAdaptiveFlowStatsCollector.NO_FLOW_MISSING_XID); + + } else { + // call individual flow stats update + providerService.pushFlowMetricsWithoutFlowMissing(did, flowEntries); + } + + // Update TypedFlowEntry to deviceFlowEntries in NewAdaptiveFlowStatsCollector + afsc.pushFlowMetrics(flowEntries); + } + } else { + // call existing entire flow stats update with flowMissing synchronization + providerService.pushFlowMetrics(did, flowEntries); + } + } + + private void pushTableStatistics(Dpid dpid, OFTableStatsReply replies) { + + DeviceId did = DeviceId.deviceId(Dpid.uri(dpid)); + List<TableStatisticsEntry> tableStatsEntries = replies.getEntries().stream() + .map(entry -> buildTableStatistics(did, entry)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + providerService.pushTableStatistics(did, tableStatsEntries); + } + + private TableStatisticsEntry buildTableStatistics(DeviceId deviceId, + OFTableStatsEntry ofEntry) { + TableStatisticsEntry entry = null; + if (ofEntry != null) { + entry = new DefaultTableStatisticsEntry(deviceId, + ofEntry.getTableId().getValue(), + ofEntry.getActiveCount(), + ofEntry.getLookupCount().getValue(), + ofEntry.getMatchedCount().getValue()); + } + + return entry; + } } diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java new file mode 100644 index 00000000..922a470a --- /dev/null +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/TableStatisticsCollector.java @@ -0,0 +1,95 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.provider.of.flow.impl; + +import org.onlab.util.SharedExecutors; +import org.onosproject.openflow.controller.OpenFlowSwitch; +import org.onosproject.openflow.controller.RoleState; +import org.projectfloodlight.openflow.protocol.OFTableStatsRequest; +import org.slf4j.Logger; + +import java.util.Timer; +import java.util.TimerTask; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Collects Table statistics for the specified switch. + */ +class TableStatisticsCollector { + + private final Logger log = getLogger(getClass()); + + public static final int SECONDS = 1000; + + private final OpenFlowSwitch sw; + private Timer timer; + private TimerTask task; + + private int pollInterval; + + /** + * Creates a new table statistics collector for the given switch and poll frequency. + * + * @param timer timer to use for scheduling + * @param sw switch to pull + * @param pollInterval poll frequency in seconds + */ + TableStatisticsCollector(Timer timer, OpenFlowSwitch sw, int pollInterval) { + this.timer = timer; + this.sw = sw; + this.pollInterval = pollInterval; + } + + /** + * Adjusts poll frequency. + * + * @param pollInterval poll frequency in seconds + */ + synchronized void adjustPollInterval(int pollInterval) { + this.pollInterval = pollInterval; + task.cancel(); + task = new InternalTimerTask(); + timer.scheduleAtFixedRate(task, pollInterval * SECONDS, pollInterval * 1000); + } + + private class InternalTimerTask extends TimerTask { + @Override + public void run() { + if (sw.getRole() == RoleState.MASTER) { + log.trace("Collecting stats for {}", sw.getStringId()); + OFTableStatsRequest request = sw.factory().buildTableStatsRequest() + .build(); + sw.sendMsg(request); + } + } + } + + public synchronized void start() { + // Initially start polling quickly. Then drop down to configured value + log.debug("Starting Table Stats collection thread for {}", sw.getStringId()); + task = new InternalTimerTask(); + SharedExecutors.getTimer().scheduleAtFixedRate(task, 1 * SECONDS, + pollInterval * SECONDS); + } + + public synchronized void stop() { + log.debug("Stopping Table Stats collection thread for {}", sw.getStringId()); + task.cancel(); + task = null; + } + +} diff --git a/framework/src/onos/providers/ovsdb/device/src/test/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProviderTest.java b/framework/src/onos/providers/ovsdb/device/src/test/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProviderTest.java index 5e4c5677..7663a64d 100644 --- a/framework/src/onos/providers/ovsdb/device/src/test/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProviderTest.java +++ b/framework/src/onos/providers/ovsdb/device/src/test/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProviderTest.java @@ -27,6 +27,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.onlab.packet.IpAddress; +import org.onlab.packet.TpPort; import org.onosproject.net.DeviceId; import org.onosproject.net.MastershipRole; import org.onosproject.net.device.DeviceDescription; @@ -193,6 +194,10 @@ public class OvsdbDeviceProviderTest { return null; } + @Override + public void connect(IpAddress ip, TpPort port) { + + } } } diff --git a/framework/src/onos/providers/ovsdb/host/src/test/java/org/onosproject/ovsdb/provider/host/OvsdbHostProviderTest.java b/framework/src/onos/providers/ovsdb/host/src/test/java/org/onosproject/ovsdb/provider/host/OvsdbHostProviderTest.java index ad720c85..01e07dd8 100644 --- a/framework/src/onos/providers/ovsdb/host/src/test/java/org/onosproject/ovsdb/provider/host/OvsdbHostProviderTest.java +++ b/framework/src/onos/providers/ovsdb/host/src/test/java/org/onosproject/ovsdb/provider/host/OvsdbHostProviderTest.java @@ -24,7 +24,9 @@ import java.util.Set; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; +import org.onlab.packet.TpPort; import org.onosproject.net.DeviceId; import org.onosproject.net.HostId; import org.onosproject.net.host.HostDescription; @@ -159,6 +161,11 @@ public class OvsdbHostProviderTest { removeCount++; } + @Override + public void removeIpFromHost(HostId hostId, IpAddress ipAddress) { + + } + } private class OvsdbControllerTest implements OvsdbController { @@ -195,5 +202,10 @@ public class OvsdbHostProviderTest { public OvsdbClientService getOvsdbClient(OvsdbNodeId nodeId) { return null; } + + @Override + public void connect(IpAddress ip, TpPort port) { + + } } } diff --git a/framework/src/onos/tools/build/conf/pom.xml b/framework/src/onos/tools/build/conf/pom.xml index e4f8e9dc..29e4dea0 100644 --- a/framework/src/onos/tools/build/conf/pom.xml +++ b/framework/src/onos/tools/build/conf/pom.xml @@ -27,7 +27,7 @@ <groupId>org.onosproject</groupId> <artifactId>onos-build-conf</artifactId> - <version>1.0</version> + <version>1.1</version> <description>Various ONOS build settings</description> <properties> diff --git a/framework/src/onos/tools/build/conf/src/main/resources/onos/checkstyle.xml b/framework/src/onos/tools/build/conf/src/main/resources/onos/checkstyle.xml index 2908c891..1bb3b0bf 100644 --- a/framework/src/onos/tools/build/conf/src/main/resources/onos/checkstyle.xml +++ b/framework/src/onos/tools/build/conf/src/main/resources/onos/checkstyle.xml @@ -229,7 +229,10 @@ <!-- ONOS alows declarations inside of switch case blocks --> <property name="allowInSwitchCase" value="true"/> </module> - <module name="EmptyBlock"/> + <module name="EmptyBlock"> + <!-- allow empty block, as long as there's some comment --> + <property name="option" value="text"/> + </module> <module name="LeftCurly"/> <module name="NeedBraces"/> <module name="RightCurly"/> diff --git a/framework/src/onos/tools/build/docker/Dockerfile b/framework/src/onos/tools/build/docker/Dockerfile index fd61ad86..ee9666ab 100644 --- a/framework/src/onos/tools/build/docker/Dockerfile +++ b/framework/src/onos/tools/build/docker/Dockerfile @@ -45,11 +45,11 @@ RUN mkdir onos && \ # Ports -# 6633 - OpenFlow +# 6653 - OpenFlow # 8181 - GUI # 8101 - ONOS CLI # 9876 - ONOS CLUSTER COMMUNICATION -EXPOSE 6633 8181 8101 9876 +EXPOSE 6653 8181 8101 9876 # Get ready to run command WORKDIR /root/onos diff --git a/framework/src/onos/tools/build/onos-package b/framework/src/onos/tools/build/onos-package index 32794457..5566e60d 100755 --- a/framework/src/onos/tools/build/onos-package +++ b/framework/src/onos/tools/build/onos-package @@ -28,6 +28,10 @@ function build_stage_dir() { [ -f $KARAF_TAR ] && tar zxf $KARAF_TAR && rm -rf $ONOS_STAGE/$KARAF_DIST/demos mkdir bin + # Patch the log-file size in place to increase it to 10 MB + perl -pi.old -e "s/maxFileSize=1MB/maxFileSize=10MB/g" \ + $ONOS_STAGE/$KARAF_DIST/etc/org.ops4j.pax.logging.cfg + # Stage the ONOS admin scripts and patch in Karaf service wrapper extras cp -r $ONOS_ROOT/tools/package/bin . cp -r $ONOS_ROOT/tools/package/init $ONOS_STAGE/init diff --git a/framework/src/onos/tools/dev/bash_profile b/framework/src/onos/tools/dev/bash_profile index 5e161ccd..f39c2ce1 100644 --- a/framework/src/onos/tools/dev/bash_profile +++ b/framework/src/onos/tools/dev/bash_profile @@ -148,41 +148,41 @@ function nuke { spy "$@" | cut -c7-11 | xargs kill } -# Edit a cell file by providing a cell name. Opens the cell file in $EDITOR. -function vicell() { - local apply=false - local create=false - local cdf="" - local cpath="${ONOS_ROOT}/tools/test/cells/" - - if [ -z "$1" ] || [ "$1" = "-h" ] ; then - printf "usage: vicell [file] [options]\n\noptions:\n" - printf "\t-a: apply the cell after editing\n" - printf "\t-e: [editor] set EDITOR to [editor] (default *vi*)\n" - printf "\t-c: create cell file if none exist\n\n" - return 1 - fi - - while [ $# -gt 0 ]; do - case "$1" in - -a) apply=true ;; - -e) EDITOR=$2; shift ;; - -c) create=true ;; - *) cdf="$1" ;; - esac - shift - done - - if [ ! -e "${cpath}${cdf}" ] && [ "$create" = "false" ]; then - printf "${cdf} : no such cell\n" && return 1 - fi - - if [ -z "${EDITOR}" ] || [ -x "$(which ${EDITOR})" ]; then - unset EDITOR && vi ${cpath}${cdf} - else - $EDITOR ${cpath}${cdf} - fi - ($apply) && cell ${cdf} +# Edit a cell file by providing a cell name; opens the cell file in $EDITOR. +function vicell { + local apply=false + local create=false + local cdf="" + local cpath="${ONOS_ROOT}/tools/test/cells/" + + if [ -z "$1" ] || [ "$1" = "-h" ] ; then + printf "usage: vicell [file] [options]\n\noptions:\n" + printf "\t-a: apply the cell after editing\n" + printf "\t-e: [editor] set EDITOR to [editor] (default *vi*)\n" + printf "\t-c: create cell file if none exist\n\n" + return 1 + fi + + while [ $# -gt 0 ]; do + case "$1" in + -a) apply=true ;; + -e) EDITOR=$2; shift ;; + -c) create=true ;; + *) cdf="$1" ;; + esac + shift + done + + if [ ! -e "${cpath}${cdf}" ] && [ "$create" = "false" ]; then + printf "${cdf} : no such cell\n" && return 1 + fi + + if [ -z "${EDITOR}" ] || [ -x "$(which ${EDITOR})" ]; then + unset EDITOR && vi ${cpath}${cdf} + else + $EDITOR ${cpath}${cdf} + fi + ($apply) && cell ${cdf} } # autocomplete for certain utilities diff --git a/framework/src/onos/tools/dev/bin/onos-create-app b/framework/src/onos/tools/dev/bin/onos-create-app index 65b00b65..454bcd6e 100755 --- a/framework/src/onos/tools/dev/bin/onos-create-app +++ b/framework/src/onos/tools/dev/bin/onos-create-app @@ -11,7 +11,7 @@ type=${1:-bundle} [ $type = app ] && archetype=bundle || archetype=$type if [ "$1" = "-?" -o "$1" = "-h" -o "$1" = "--help" ]; then - echo "usage: $(basename $0) {app|bundle|ui|cli|api} groupId artifactId version package mvn-options" + echo "usage: $(basename $0) {app|bundle|ui|uitab|uitopo|cli|api} groupId artifactId version package mvn-options" echo " All arguments are optional" exit 1 fi diff --git a/framework/src/onos/tools/dev/header.txt b/framework/src/onos/tools/dev/header.txt index 6c18c92c..5b9dcb84 100644 --- a/framework/src/onos/tools/dev/header.txt +++ b/framework/src/onos/tools/dev/header.txt @@ -1,4 +1,4 @@ -Copyright $today.year Open Networking Laboratory +Copyright 2014-$today.year Open Networking Laboratory Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/framework/src/onos/tools/package/archetypes/pom.xml b/framework/src/onos/tools/package/archetypes/pom.xml index 525a2f68..12a23229 100644 --- a/framework/src/onos/tools/package/archetypes/pom.xml +++ b/framework/src/onos/tools/package/archetypes/pom.xml @@ -38,6 +38,8 @@ <module>bundle</module> <module>cli</module> <module>ui</module> + <module>uitab</module> + <module>uitopo</module> </modules> <build> diff --git a/framework/src/onos/tools/package/archetypes/ui/pom.xml b/framework/src/onos/tools/package/archetypes/ui/pom.xml index 8dd3a8e0..9bebe9da 100644 --- a/framework/src/onos/tools/package/archetypes/ui/pom.xml +++ b/framework/src/onos/tools/package/archetypes/ui/pom.xml @@ -26,6 +26,6 @@ <artifactId>onos-ui-archetype</artifactId> <packaging>maven-archetype</packaging> - <description>ONOS UI overlay archetype</description> + <description>ONOS UI Custom-View overlay archetype</description> </project> diff --git a/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/pom.xml b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/pom.xml index 2e1f091e..d67c181a 100644 --- a/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/pom.xml +++ b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/pom.xml @@ -22,7 +22,7 @@ <version>${version}</version> <packaging>bundle</packaging> - <description>ONOS OSGi UI bundle archetype</description> + <description>ONOS OSGi UI Custom-View bundle archetype</description> <url>http://onosproject.org</url> <properties> diff --git a/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/java/AppUiComponent.java b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/java/AppUiComponent.java index f40bcb5f..e44b34d5 100644 --- a/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/java/AppUiComponent.java +++ b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/java/AppUiComponent.java @@ -34,11 +34,14 @@ import org.slf4j.LoggerFactory; import java.util.List; /** - * Skeletal ONOS UI application component. + * Skeletal ONOS UI Custom-View application component. */ @Component(immediate = true) public class AppUiComponent { + private static final String VIEW_ID = "sampleCustom"; + private static final String VIEW_TEXT = "Sample Custom"; + private final Logger log = LoggerFactory.getLogger(getClass()); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) @@ -46,7 +49,7 @@ public class AppUiComponent { // List of application views private final List<UiView> uiViews = ImmutableList.of( - new UiView(UiView.Category.OTHER, "sample", "Sample") + new UiView(UiView.Category.OTHER, VIEW_ID, VIEW_TEXT) ); // Factory for UI message handlers diff --git a/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/java/AppUiMessageHandler.java b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/java/AppUiMessageHandler.java index d9d68b53..d6486328 100644 --- a/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/java/AppUiMessageHandler.java +++ b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/java/AppUiMessageHandler.java @@ -22,168 +22,56 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.ImmutableSet; import org.onosproject.ui.RequestHandler; import org.onosproject.ui.UiMessageHandler; -import org.onosproject.ui.table.TableModel; -import org.onosproject.ui.table.TableRequestHandler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.lang.Override; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; /** - * Skeletal ONOS UI message handler. - * <p> - * This example specifically supporting a "table" view. + * Skeletal ONOS UI Custom-View message handler. */ public class AppUiMessageHandler extends UiMessageHandler { - private static final String SAMPLE_DATA_REQ = "sampleDataRequest"; - private static final String SAMPLE_DATA_RESP = "sampleDataResponse"; - private static final String SAMPLES = "samples"; + private static final String SAMPLE_CUSTOM_DATA_REQ = "sampleCustomDataRequest"; + private static final String SAMPLE_CUSTOM_DATA_RESP = "sampleCustomDataResponse"; - private static final String SAMPLE_DETAIL_REQ = "sampleDetailsRequest"; - private static final String SAMPLE_DETAIL_RESP = "sampleDetailsResponse"; - private static final String DETAILS = "details"; - - private static final String ID = "id"; - private static final String LABEL = "label"; - private static final String CODE = "code"; - private static final String COMMENT = "comment"; - private static final String RESULT = "result"; - - private static final String[] COLUMN_IDS = { ID, LABEL, CODE }; + private static final String NUMBER = "number"; + private static final String SQUARE = "square"; + private static final String CUBE = "cube"; + private static final String MESSAGE = "message"; + private static final String MSG_FORMAT = "Next incrememt is %d units"; private final Logger log = LoggerFactory.getLogger(getClass()); + private long someNumber = 1; + private long someIncrement = 1; @Override protected Collection<RequestHandler> createRequestHandlers() { return ImmutableSet.of( - new SampleDataRequestHandler(), - new SampleDetailRequestHandler() + new SampleCustomDataRequestHandler() ); } - // handler for sample table requests - private final class SampleDataRequestHandler extends TableRequestHandler { - - private SampleDataRequestHandler() { - super(SAMPLE_DATA_REQ, SAMPLE_DATA_RESP, SAMPLES); - } - - // if necessary, override defaultColumnId() -- if it isn't "id" + // handler for sample data requests + private final class SampleCustomDataRequestHandler extends RequestHandler { - @Override - protected String[] getColumnIds() { - return COLUMN_IDS; - } - - @Override - protected void populateTable(TableModel tm, ObjectNode payload) { - // === set custom column cell formatters/comparators if need be... - // tm.setFormatter(CODE, new CodeFormatter()); - // tm.setComparator(CODE, new CodeComparator()); - - // === retrieve table row items from some service... - // SomeService ss = get(SomeService.class); - // List<Item> items = ss.getItems() - - // fake data for demonstration purposes... - List<Item> items = getItems(); - for (Item item: items) { - populateRow(tm.addRow(), item); - } - } - - private void populateRow(TableModel.Row row, Item item) { - row.cell(ID, item.id()) - .cell(LABEL, item.label()) - .cell(CODE, item.code()); - } - } - - - // handler for sample item details requests - private final class SampleDetailRequestHandler extends RequestHandler { - - private SampleDetailRequestHandler() { - super(SAMPLE_DETAIL_REQ); + private SampleCustomDataRequestHandler() { + super(SAMPLE_CUSTOM_DATA_REQ); } @Override public void process(long sid, ObjectNode payload) { - String id = string(payload, ID, "(none)"); - - // SomeService ss = get(SomeService.class); - // Item item = ss.getItemDetails(id) - - // fake data for demonstration purposes... - Item item = getItem(id); - - ObjectNode rootNode = MAPPER.createObjectNode(); - ObjectNode data = MAPPER.createObjectNode(); - rootNode.set(DETAILS, data); - - if (item == null) { - rootNode.put(RESULT, "Item with id '" + id + "' not found"); - log.warn("attempted to get item detail for id '{}'", id); - - } else { - rootNode.put(RESULT, "Found item with id '" + id + "'"); - - data.put(ID, item.id()); - data.put(LABEL, item.label()); - data.put(CODE, item.code()); - data.put(COMMENT, "Some arbitrary comment"); - } - - sendMessage(SAMPLE_DETAIL_RESP, 0, rootNode); - } - } - - - // =================================================================== - // NOTE: The code below this line is to create fake data for this - // sample code. Normally you would use existing services to - // provide real data. - - // Lookup a single item. - private static Item getItem(String id) { - // We realize this code is really inefficient, but - // it suffices for our purposes of demonstration... - for (Item item : getItems()) { - if (item.id().equals(id)) { - return item; - } + someIncrement++; + someNumber += someIncrement; + log.debug("Computing data for {}...", someNumber); + + ObjectNode result = objectNode(); + result.put(NUMBER, someNumber); + result.put(SQUARE, someNumber * someNumber); + result.put(CUBE, someNumber * someNumber * someNumber); + result.put(MESSAGE, String.format(MSG_FORMAT, someIncrement + 1)); + sendMessage(SAMPLE_CUSTOM_DATA_RESP, 0, result); } - return null; - } - - // Produce a list of items. - private static List<Item> getItems() { - List<Item> items = new ArrayList<>(); - items.add(new Item("item-1", "foo", 42)); - items.add(new Item("item-2", "bar", 99)); - items.add(new Item("item-3", "baz", 65)); - return items; - } - - // Simple model class to provide sample data - private static class Item { - private final String id; - private final String label; - private final int code; - - Item(String id, String label, int code) { - this.id = id; - this.label = label; - this.code = code; - } - - String id() { return id; } - String label() { return label; } - int code() { return code; } } }
\ No newline at end of file diff --git a/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.css b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.css new file mode 100644 index 00000000..ffeac0aa --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.css @@ -0,0 +1,48 @@ +/* css for sample app custom view */ + +#ov-sample-custom { + padding: 20px; +} +.light #ov-sample-custom { + color: navy; +} +.dark #ov-sample-custom { + color: #88f; +} + +#ov-sample-custom .button-panel { + margin: 10px; + width: 200px; +} + +.light #ov-sample-custom .button-panel { + background-color: #ccf; +} +.dark #ov-sample-custom .button-panel { + background-color: #444; +} + +#ov-sample-custom .my-button { + cursor: pointer; + padding: 4px; + text-align: center; +} + +.light #ov-sample-custom .my-button { + color: white; + background-color: #99d; +} +.dark #ov-sample-custom .my-button { + color: black; + background-color: #aaa; +} + +#ov-sample-custom .number { + font-size: 140%; + text-align: right; +} + +#ov-sample-custom .quote { + margin: 10px 20px; + font-style: italic; +}
\ No newline at end of file diff --git a/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.html b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.html new file mode 100644 index 00000000..d3d79a10 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.html @@ -0,0 +1,32 @@ +<!-- partial HTML --> +<div id="ov-sample-custom"> + <div class="button-panel"> + <div class="my-button" ng-click="getData()"> + Fetch Data + </div> + </div> + + <div class="data-panel"> + <table> + <tr> + <td> Number </td> + <td class="number"> {{data.number}} </td> + </tr> + <tr> + <td> Square </td> + <td class="number"> {{data.square}} </td> + </tr> + <tr> + <td> Cube </td> + <td class="number"> {{data.cube}} </td> + </tr> + </table> + + <p> + A message from our sponsors: + </p> + <p> + <span class="quote"> {{data.message}} </span> + </p> + </div> +</div> diff --git a/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.js b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.js new file mode 100644 index 00000000..21058640 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/app/view/sampleCustom/sampleCustom.js @@ -0,0 +1,69 @@ +// js for sample app custom view +(function () { + 'use strict'; + + // injected refs + var $log, $scope, wss, ks; + + // constants + var dataReq = 'sampleCustomDataRequest', + dataResp = 'sampleCustomDataResponse'; + + function addKeyBindings() { + var map = { + space: [getData, 'Fetch data from server'], + + _helpFormat: [ + ['space'] + ] + }; + + ks.keyBindings(map); + } + + function getData() { + wss.sendEvent(dataReq); + } + + function respDataCb(data) { + $scope.data = data; + $scope.$apply(); + } + + + angular.module('ovSampleCustom', []) + .controller('OvSampleCustomCtrl', + ['$log', '$scope', 'WebSocketService', 'KeyService', + + function (_$log_, _$scope_, _wss_, _ks_) { + $log = _$log_; + $scope = _$scope_; + wss = _wss_; + ks = _ks_; + + var handlers = {}; + $scope.data = {}; + + // data response handler + handlers[dataResp] = respDataCb; + wss.bindHandlers(handlers); + + addKeyBindings(); + + // custom click handler + $scope.getData = getData; + + // get data the first time... + getData(); + + // cleanup + $scope.$on('$destroy', function () { + wss.unbindHandlers(handlers); + ks.unbindKeys(); + $log.log('OvSampleCustomCtrl has been destroyed'); + }); + + $log.log('OvSampleCustomCtrl has been created'); + }]); + +}()); diff --git a/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/css.html b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/css.html index c4697256..4e7b7092 100644 --- a/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/css.html +++ b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/css.html @@ -1 +1 @@ -<link rel="stylesheet" href="app/view/sample/sample.css">
\ No newline at end of file +<link rel="stylesheet" href="app/view/sampleCustom/sampleCustom.css">
\ No newline at end of file diff --git a/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/js.html b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/js.html index 7cacc707..6550b85e 100644 --- a/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/js.html +++ b/framework/src/onos/tools/package/archetypes/ui/src/main/resources/archetype-resources/src/main/resources/js.html @@ -1 +1 @@ -<script src="app/view/sample/sample.js"></script>
\ No newline at end of file +<script src="app/view/sampleCustom/sampleCustom.js"></script>
\ No newline at end of file diff --git a/framework/src/onos/tools/package/archetypes/uitab/pom.xml b/framework/src/onos/tools/package/archetypes/uitab/pom.xml new file mode 100644 index 00000000..cb18f1f3 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitab/pom.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-archetypes</artifactId> + <version>1.4.0-SNAPSHOT</version> + </parent> + + <artifactId>onos-uitab-archetype</artifactId> + <packaging>maven-archetype</packaging> + + <description>ONOS UI Table-View overlay archetype</description> + +</project> diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/META-INF/maven/archetype-metadata.xml b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/META-INF/maven/archetype-metadata.xml new file mode 100644 index 00000000..a6273811 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/META-INF/maven/archetype-metadata.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<archetype-descriptor + xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd" + name="onos-uitab" partial="true" + xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <fileSets> + <fileSet filtered="true" packaged="true" encoding="UTF-8"> + <directory>src/main/java</directory> + <includes> + <include>**/*.java</include> + </includes> + </fileSet> + <fileSet filtered="true" packaged="false" encoding="UTF-8"> + <directory>src/main/resources</directory> + <includes> + <include>**/*.html</include> + <include>**/*.js</include> + <include>**/*.css</include> + </includes> + </fileSet> + </fileSets> +</archetype-descriptor> diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/pom.xml b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/pom.xml new file mode 100644 index 00000000..05a62b27 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/pom.xml @@ -0,0 +1,140 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>${groupId}</groupId> + <artifactId>${artifactId}</artifactId> + <version>${version}</version> + <packaging>bundle</packaging> + + <description>ONOS OSGi UI Table-View bundle archetype</description> + <url>http://onosproject.org</url> + + <properties> + <onos.version>1.4.0-SNAPSHOT</onos.version> + <!-- Uncomment to generate ONOS app from this module. + <onos.app.name>org.foo.app</onos.app.name> + <onos.app.origin>Foo, Inc.</onos.app.origin> + --> + </properties> + + <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + <version>${onos.version}</version> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-osgi</artifactId> + <version>${onos.version}</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.11</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + <version>${onos.version}</version> + <scope>test</scope> + <classifier>tests</classifier> + </dependency> + + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + <version>1.9.8</version> + <scope>provided</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <version>2.5.3</version> + <extensions>true</extensions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>2.5.1</version> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-scr-plugin</artifactId> + <version>1.20.0</version> + <executions> + <execution> + <id>generate-scr-srcdescriptor</id> + <goals> + <goal>scr</goal> + </goals> + </execution> + </executions> + <configuration> + <supportedProjectTypes> + <supportedProjectType>bundle</supportedProjectType> + <supportedProjectType>war</supportedProjectType> + </supportedProjectTypes> + </configuration> + </plugin> + <plugin> + <groupId>org.onosproject</groupId> + <artifactId>onos-maven-plugin</artifactId> + <version>1.5</version> + <executions> + <execution> + <id>cfg</id> + <phase>generate-resources</phase> + <goals> + <goal>cfg</goal> + </goals> + </execution> + <execution> + <id>swagger</id> + <phase>generate-sources</phase> + <goals> + <goal>swagger</goal> + </goals> + </execution> + <execution> + <id>app</id> + <phase>package</phase> + <goals> + <goal>app</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/java/AppUiTableComponent.java b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/java/AppUiTableComponent.java new file mode 100644 index 00000000..263564ce --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/java/AppUiTableComponent.java @@ -0,0 +1,80 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/* + * Copyright 2014,2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ${package}; + +import com.google.common.collect.ImmutableList; +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.ui.UiExtension; +import org.onosproject.ui.UiExtensionService; +import org.onosproject.ui.UiMessageHandlerFactory; +import org.onosproject.ui.UiView; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * Skeletal ONOS UI Table-View application component. + */ +@Component(immediate = true) +public class AppUiTableComponent { + + private static final String VIEW_ID = "sampleTable"; + private static final String VIEW_TEXT = "Sample Table"; + + private final Logger log = LoggerFactory.getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected UiExtensionService uiExtensionService; + + // List of application views + private final List<UiView> uiViews = ImmutableList.of( + new UiView(UiView.Category.OTHER, VIEW_ID, VIEW_TEXT) + ); + + // Factory for UI message handlers + private final UiMessageHandlerFactory messageHandlerFactory = + () -> ImmutableList.of( + new AppUiTableMessageHandler() + ); + + // Application UI extension + protected UiExtension extension = + new UiExtension.Builder(getClass().getClassLoader(), uiViews) + .resourcePath(VIEW_ID) + .messageHandlerFactory(messageHandlerFactory) + .build(); + + @Activate + protected void activate() { + uiExtensionService.register(extension); + log.info("Started"); + } + + @Deactivate + protected void deactivate() { + uiExtensionService.unregister(extension); + log.info("Stopped"); + } + +} diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/java/AppUiTableMessageHandler.java b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/java/AppUiTableMessageHandler.java new file mode 100644 index 00000000..a673f1fd --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/java/AppUiTableMessageHandler.java @@ -0,0 +1,190 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/* + * Copyright 2014,2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ${package}; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.ImmutableSet; +import org.onosproject.ui.RequestHandler; +import org.onosproject.ui.UiMessageHandler; +import org.onosproject.ui.table.TableModel; +import org.onosproject.ui.table.TableRequestHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.Override; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Skeletal ONOS UI Table-View message handler. + */ +public class AppUiTableMessageHandler extends UiMessageHandler { + + private static final String SAMPLE_TABLE_DATA_REQ = "sampleTableDataRequest"; + private static final String SAMPLE_TABLE_DATA_RESP = "sampleTableDataResponse"; + private static final String SAMPLE_TABLES = "sampleTables"; + + private static final String SAMPLE_TABLE_DETAIL_REQ = "sampleTableDetailsRequest"; + private static final String SAMPLE_TABLE_DETAIL_RESP = "sampleTableDetailsResponse"; + private static final String DETAILS = "details"; + + private static final String ID = "id"; + private static final String LABEL = "label"; + private static final String CODE = "code"; + private static final String COMMENT = "comment"; + private static final String RESULT = "result"; + + private static final String[] COLUMN_IDS = { ID, LABEL, CODE }; + + private final Logger log = LoggerFactory.getLogger(getClass()); + + + @Override + protected Collection<RequestHandler> createRequestHandlers() { + return ImmutableSet.of( + new SampleTableDataRequestHandler(), + new SampleTableDetailRequestHandler() + ); + } + + // handler for sample table requests + private final class SampleTableDataRequestHandler extends TableRequestHandler { + + private SampleTableDataRequestHandler() { + super(SAMPLE_TABLE_DATA_REQ, SAMPLE_TABLE_DATA_RESP, SAMPLE_TABLES); + } + + // if necessary, override defaultColumnId() -- if it isn't "id" + + @Override + protected String[] getColumnIds() { + return COLUMN_IDS; + } + + // if required, override createTableModel() to set column formatters / comparators + + @Override + protected void populateTable(TableModel tm, ObjectNode payload) { + // === NOTE: the table model supplied here will have been created + // via a call to createTableModel(). To assign non-default + // cell formatters or comparators to the table model, override + // createTableModel() and set them there. + + // === retrieve table row items from some service... + // SomeService ss = get(SomeService.class); + // List<Item> items = ss.getItems() + + // fake data for demonstration purposes... + List<Item> items = getItems(); + for (Item item: items) { + populateRow(tm.addRow(), item); + } + } + + private void populateRow(TableModel.Row row, Item item) { + row.cell(ID, item.id()) + .cell(LABEL, item.label()) + .cell(CODE, item.code()); + } + } + + + // handler for sample item details requests + private final class SampleTableDetailRequestHandler extends RequestHandler { + + private SampleTableDetailRequestHandler() { + super(SAMPLE_TABLE_DETAIL_REQ); + } + + @Override + public void process(long sid, ObjectNode payload) { + String id = string(payload, ID, "(none)"); + + // SomeService ss = get(SomeService.class); + // Item item = ss.getItemDetails(id) + + // fake data for demonstration purposes... + Item item = getItem(id); + + ObjectNode rootNode = objectNode(); + ObjectNode data = objectNode(); + rootNode.set(DETAILS, data); + + if (item == null) { + rootNode.put(RESULT, "Item with id '" + id + "' not found"); + log.warn("attempted to get item detail for id '{}'", id); + + } else { + rootNode.put(RESULT, "Found item with id '" + id + "'"); + + data.put(ID, item.id()); + data.put(LABEL, item.label()); + data.put(CODE, item.code()); + data.put(COMMENT, "Some arbitrary comment"); + } + + sendMessage(SAMPLE_TABLE_DETAIL_RESP, 0, rootNode); + } + } + + + // =================================================================== + // NOTE: The code below this line is to create fake data for this + // sample code. Normally you would use existing services to + // provide real data. + + // Lookup a single item. + private static Item getItem(String id) { + // We realize this code is really inefficient, but + // it suffices for our purposes of demonstration... + for (Item item : getItems()) { + if (item.id().equals(id)) { + return item; + } + } + return null; + } + + // Produce a list of items. + private static List<Item> getItems() { + List<Item> items = new ArrayList<>(); + items.add(new Item("item-1", "foo", 42)); + items.add(new Item("item-2", "bar", 99)); + items.add(new Item("item-3", "baz", 65)); + return items; + } + + // Simple model class to provide sample data + private static class Item { + private final String id; + private final String label; + private final int code; + + Item(String id, String label, int code) { + this.id = id; + this.label = label; + this.code = code; + } + + String id() { return id; } + String label() { return label; } + int code() { return code; } + } +}
\ No newline at end of file diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.css b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.css new file mode 100644 index 00000000..5eb551b3 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.css @@ -0,0 +1,35 @@ +/* css for sample table view */ + +#ov-sample-table h2 { + display: inline-block; +} + +/* Panel Styling */ +#ov-sample-table-item-details-panel.floatpanel { + position: absolute; + top: 115px; +} + +.light #ov-sample-table-item-details-panel.floatpanel { + background-color: rgb(229, 234, 237); +} +.dark #ov-sample-table-item-details-panel.floatpanel { + background-color: #3A4042; +} + +#ov-sample-table-item-details-panel h3 { + margin: 0; + font-size: large; +} + +#ov-sample-table-item-details-panel h4 { + margin: 0; +} + +#ov-sample-table-item-details-panel td { + padding: 5px; +} +#ov-sample-table-item-details-panel td.label { + font-style: italic; + opacity: 0.8; +} diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.html b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.html new file mode 100644 index 00000000..e20a94d7 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.html @@ -0,0 +1,46 @@ +<!-- partial HTML --> +<div id="ov-sample-table"> + <div class="tabular-header"> + <h2>Items ({{tableData.length}} total)</h2> + <div class="ctrl-btns"> + <div class="refresh" ng-class="{active: autoRefresh}" + icon icon-id="refresh" icon-size="36" + tooltip tt-msg="autoRefreshTip" + ng-click="toggleRefresh()"></div> + </div> + </div> + + <div class="summary-list" onos-table-resize> + + <div class="table-header" onos-sortable-header> + <table> + <tr> + <td colId="id" sortable>Item ID </td> + <td colId="label" sortable>Label </td> + <td colId="code" sortable>Code </td> + </tr> + </table> + </div> + + <div class="table-body"> + <table> + <tr ng-if="!tableData.length" class="no-data"> + <td colspan="3"> + No Items found + </td> + </tr> + + <tr ng-repeat="item in tableData track by $index" + ng-click="selectCallback($event, item)" + ng-class="{selected: item.id === selId}"> + <td>{{item.id}}</td> + <td>{{item.label}}</td> + <td>{{item.code}}</td> + </tr> + </table> + </div> + + </div> + + <ov-sample-table-item-details-panel></ov-sample-table-item-details-panel> +</div> diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.js b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.js new file mode 100644 index 00000000..7b925550 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTable/sampleTable.js @@ -0,0 +1,141 @@ +// js for sample app table view +(function () { + 'use strict'; + + // injected refs + var $log, $scope, fs, wss; + + // constants + var detailsReq = 'sampleTableDetailsRequest', + detailsResp = 'sampleTableDetailsResponse', + pName = 'ov-sample-table-item-details-panel', + + propOrder = ['id', 'label', 'code'], + friendlyProps = ['Item ID', 'Item Label', 'Special Code']; + + + function addProp(tbody, index, value) { + var tr = tbody.append('tr'); + + function addCell(cls, txt) { + tr.append('td').attr('class', cls).html(txt); + } + addCell('label', friendlyProps[index] + ' :'); + addCell('value', value); + } + + function populatePanel(panel) { + var title = panel.append('h3'), + tbody = panel.append('table').append('tbody'); + + title.text('Item Details'); + + propOrder.forEach(function (prop, i) { + addProp(tbody, i, $scope.panelDetails[prop]); + }); + + panel.append('hr'); + panel.append('h4').text('Comments'); + panel.append('p').text($scope.panelDetails.comment); + } + + function respDetailsCb(data) { + $scope.panelDetails = data.details; + $scope.$apply(); + } + + angular.module('ovSampleTable', []) + .controller('OvSampleTableCtrl', + ['$log', '$scope', 'TableBuilderService', + 'FnService', 'WebSocketService', + + function (_$log_, _$scope_, tbs, _fs_, _wss_) { + $log = _$log_; + $scope = _$scope_; + fs = _fs_; + wss = _wss_; + + var handlers = {}; + $scope.panelDetails = {}; + + // details response handler + handlers[detailsResp] = respDetailsCb; + wss.bindHandlers(handlers); + + // custom selection callback + function selCb($event, row) { + if ($scope.selId) { + wss.sendEvent(detailsReq, { id: row.id }); + } else { + $scope.hidePanel(); + } + $log.debug('Got a click on:', row); + } + + // TableBuilderService creating a table for us + tbs.buildTable({ + scope: $scope, + tag: 'sampleTable', + selCb: selCb + }); + + // cleanup + $scope.$on('$destroy', function () { + wss.unbindHandlers(handlers); + $log.log('OvSampleTableCtrl has been destroyed'); + }); + + $log.log('OvSampleTableCtrl has been created'); + }]) + + .directive('ovSampleTableItemDetailsPanel', ['PanelService', 'KeyService', + function (ps, ks) { + return { + restrict: 'E', + link: function (scope, element, attrs) { + // insert details panel with PanelService + // create the panel + var panel = ps.createPanel(pName, { + width: 200, + margin: 20, + hideMargin: 0 + }); + panel.hide(); + scope.hidePanel = function () { panel.hide(); }; + + function closePanel() { + if (panel.isVisible()) { + $scope.selId = null; + panel.hide(); + return true; + } + return false; + } + + // create key bindings to handle panel + ks.keyBindings({ + esc: [closePanel, 'Close the details panel'], + _helpFormat: ['esc'] + }); + ks.gestureNotes([ + ['click', 'Select a row to show item details'] + ]); + + // update the panel's contents when the data is changed + scope.$watch('panelDetails', function () { + if (!fs.isEmptyObject(scope.panelDetails)) { + panel.empty(); + populatePanel(panel); + panel.show(); + } + }); + + // cleanup on destroyed scope + scope.$on('$destroy', function () { + ks.unbindKeys(); + ps.destroyPanel(pName); + }); + } + }; + }]); +}()); diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/sampleTable/css.html b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/sampleTable/css.html new file mode 100644 index 00000000..26112b0d --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/sampleTable/css.html @@ -0,0 +1 @@ +<link rel="stylesheet" href="app/view/sampleTable/sampleTable.css">
\ No newline at end of file diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/sampleTable/js.html b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/sampleTable/js.html new file mode 100644 index 00000000..4bfa2169 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitab/src/main/resources/archetype-resources/src/main/resources/sampleTable/js.html @@ -0,0 +1 @@ +<script src="app/view/sampleTable/sampleTable.js"></script>
\ No newline at end of file diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/test/resources/projects/basic/archetype.properties b/framework/src/onos/tools/package/archetypes/uitab/src/test/resources/projects/basic/archetype.properties new file mode 100644 index 00000000..a1213b40 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitab/src/test/resources/projects/basic/archetype.properties @@ -0,0 +1,21 @@ +# +# Copyright 2014 Open Networking Laboratory +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +#Thu Dec 04 09:24:50 PST 2014 +package=it.pkg +version=0.1-SNAPSHOT +groupId=archetype.it +artifactId=basic diff --git a/framework/src/onos/tools/package/archetypes/uitab/src/test/resources/projects/basic/goal.txt b/framework/src/onos/tools/package/archetypes/uitab/src/test/resources/projects/basic/goal.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitab/src/test/resources/projects/basic/goal.txt diff --git a/framework/src/onos/tools/package/archetypes/uitopo/pom.xml b/framework/src/onos/tools/package/archetypes/uitopo/pom.xml new file mode 100644 index 00000000..6ed7c871 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitopo/pom.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-archetypes</artifactId> + <version>1.4.0-SNAPSHOT</version> + </parent> + + <artifactId>onos-uitopo-archetype</artifactId> + <packaging>maven-archetype</packaging> + + <description>ONOS UI Topology-Overlay overlay archetype</description> + +</project> diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/META-INF/maven/archetype-metadata.xml b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/META-INF/maven/archetype-metadata.xml new file mode 100644 index 00000000..26a9082d --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/META-INF/maven/archetype-metadata.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<archetype-descriptor + xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0 http://maven.apache.org/xsd/archetype-descriptor-1.0.0.xsd" + name="onos-uitopo" partial="true" + xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <fileSets> + <fileSet filtered="true" packaged="true" encoding="UTF-8"> + <directory>src/main/java</directory> + <includes> + <include>**/*.java</include> + </includes> + </fileSet> + <fileSet filtered="true" packaged="false" encoding="UTF-8"> + <directory>src/main/resources</directory> + <includes> + <include>**/*.html</include> + <include>**/*.js</include> + <include>**/*.css</include> + </includes> + </fileSet> + </fileSets> +</archetype-descriptor> diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/pom.xml b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/pom.xml new file mode 100644 index 00000000..f0688ec2 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/pom.xml @@ -0,0 +1,140 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>${groupId}</groupId> + <artifactId>${artifactId}</artifactId> + <version>${version}</version> + <packaging>bundle</packaging> + + <description>ONOS OSGi UI Topology-Overlay bundle archetype</description> + <url>http://onosproject.org</url> + + <properties> + <onos.version>1.4.0-SNAPSHOT</onos.version> + <!-- Uncomment to generate ONOS app from this module. + <onos.app.name>org.foo.app</onos.app.name> + <onos.app.origin>Foo, Inc.</onos.app.origin> + --> + </properties> + + <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + <version>${onos.version}</version> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-osgi</artifactId> + <version>${onos.version}</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.11</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + <version>${onos.version}</version> + <scope>test</scope> + <classifier>tests</classifier> + </dependency> + + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + <version>1.9.8</version> + <scope>provided</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <version>2.5.3</version> + <extensions>true</extensions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>2.5.1</version> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-scr-plugin</artifactId> + <version>1.20.0</version> + <executions> + <execution> + <id>generate-scr-srcdescriptor</id> + <goals> + <goal>scr</goal> + </goals> + </execution> + </executions> + <configuration> + <supportedProjectTypes> + <supportedProjectType>bundle</supportedProjectType> + <supportedProjectType>war</supportedProjectType> + </supportedProjectTypes> + </configuration> + </plugin> + <plugin> + <groupId>org.onosproject</groupId> + <artifactId>onos-maven-plugin</artifactId> + <version>1.5</version> + <executions> + <execution> + <id>cfg</id> + <phase>generate-resources</phase> + <goals> + <goal>cfg</goal> + </goals> + </execution> + <execution> + <id>swagger</id> + <phase>generate-sources</phase> + <goals> + <goal>swagger</goal> + </goals> + </execution> + <execution> + <id>app</id> + <phase>package</phase> + <goals> + <goal>app</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovComponent.java b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovComponent.java new file mode 100644 index 00000000..fa62a784 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovComponent.java @@ -0,0 +1,89 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/* + * Copyright 2014,2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ${package}; + +import com.google.common.collect.ImmutableList; +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.ui.UiExtension; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * Skeletal ONOS UI Topology-Overlay application component. + */ +@Component(immediate = true) +public class AppUiTopovComponent { + + private static final ClassLoader CL = AppUiTopovComponent.class.getClassLoader(); + private static final String VIEW_ID = "sampleTopov"; + + private final Logger log = LoggerFactory.getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected UiExtensionService uiExtensionService; + + // List of application views + private final List<UiView> uiViews = ImmutableList.of( + new UiViewHidden(VIEW_ID) + ); + + // Factory for UI message handlers + private final UiMessageHandlerFactory messageHandlerFactory = + () -> ImmutableList.of( + new AppUiTopovMessageHandler() + ); + + // Factory for UI topology overlays + private final UiTopoOverlayFactory topoOverlayFactory = + () -> ImmutableList.of( + new AppUiTopovOverlay() + ); + + // Application UI extension + protected UiExtension extension = + new UiExtension.Builder(CL, uiViews) + .resourcePath(VIEW_ID) + .messageHandlerFactory(messageHandlerFactory) + .topoOverlayFactory(topoOverlayFactory) + .build(); + + @Activate + protected void activate() { + uiExtensionService.register(extension); + log.info("Started"); + } + + @Deactivate + protected void deactivate() { + uiExtensionService.unregister(extension); + log.info("Stopped"); + } + +} diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovMessageHandler.java b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovMessageHandler.java new file mode 100644 index 00000000..fe28186b --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovMessageHandler.java @@ -0,0 +1,319 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/* + * Copyright 2014,2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ${package}; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; +import org.onlab.osgi.ServiceDirectory; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.Element; +import org.onosproject.net.HostId; +import org.onosproject.net.Link; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.host.HostService; +import org.onosproject.net.link.LinkService; +import org.onosproject.ui.RequestHandler; +import org.onosproject.ui.UiConnection; +import org.onosproject.ui.UiMessageHandler; +import org.onosproject.ui.topo.Highlights; +import org.onosproject.ui.topo.TopoJson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; + +/** + * Skeletal ONOS UI Topology-Overlay message handler. + */ +public class AppUiTopovMessageHandler extends UiMessageHandler { + + private static final String SAMPLE_DISPLAY_START = "sampleDisplayStart"; + private static final String SAMPLE_DISPLAY_UPDATE = "sampleDisplayUpdate"; + private static final String SAMPLE_DISPLAY_STOP = "sampleDisplayStop"; + + private static final String ID = "id"; + private static final String MODE = "mode"; + + private static final long UPDATE_PERIOD_MS = 1000; + + private static final Link[] EMPTY_LINK_SET = new Link[0]; + + private enum Mode { IDLE, MOUSE, LINK } + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private DeviceService deviceService; + private HostService hostService; + private LinkService linkService; + + private final Timer timer = new Timer("sample-overlay"); + private TimerTask demoTask = null; + private Mode currentMode = Mode.IDLE; + private Element elementOfNote; + private Link[] linkSet = EMPTY_LINK_SET; + private int linkIndex; + + + // ===============-=-=-=-=-=-======================-=-=-=-=-=-=-================================ + + + @Override + public void init(UiConnection connection, ServiceDirectory directory) { + super.init(connection, directory); + deviceService = directory.get(DeviceService.class); + hostService = directory.get(HostService.class); + linkService = directory.get(LinkService.class); + } + + @Override + protected Collection<RequestHandler> createRequestHandlers() { + return ImmutableSet.of( + new DisplayStartHandler(), + new DisplayUpdateHandler(), + new DisplayStopHandler() + ); + } + + // === ------------------------- + // === Handler classes + + private final class DisplayStartHandler extends RequestHandler { + public DisplayStartHandler() { + super(SAMPLE_DISPLAY_START); + } + + @Override + public void process(long sid, ObjectNode payload) { + String mode = string(payload, MODE); + + log.debug("Start Display: mode [{}]", mode); + clearState(); + clearForMode(); + + switch (mode) { + case "mouse": + currentMode = Mode.MOUSE; + cancelTask(); + sendMouseData(); + break; + + case "link": + currentMode = Mode.LINK; + scheduleTask(); + initLinkSet(); + sendLinkData(); + break; + + default: + currentMode = Mode.IDLE; + cancelTask(); + break; + } + } + } + + private final class DisplayUpdateHandler extends RequestHandler { + public DisplayUpdateHandler() { + super(SAMPLE_DISPLAY_UPDATE); + } + + @Override + public void process(long sid, ObjectNode payload) { + String id = string(payload, ID); + log.debug("Update Display: id [{}]", id); + if (!Strings.isNullOrEmpty(id)) { + updateForMode(id); + } else { + clearForMode(); + } + } + } + + private final class DisplayStopHandler extends RequestHandler { + public DisplayStopHandler() { + super(SAMPLE_DISPLAY_STOP); + } + + @Override + public void process(long sid, ObjectNode payload) { + log.debug("Stop Display"); + cancelTask(); + clearState(); + clearForMode(); + } + } + + // === ------------ + + private void clearState() { + currentMode = Mode.IDLE; + elementOfNote = null; + linkSet = EMPTY_LINK_SET; + } + + private void updateForMode(String id) { + log.debug("host service: {}", hostService); + log.debug("device service: {}", deviceService); + + try { + HostId hid = HostId.hostId(id); + log.debug("host id {}", hid); + elementOfNote = hostService.getHost(hid); + log.debug("host element {}", elementOfNote); + + } catch (Exception e) { + try { + DeviceId did = DeviceId.deviceId(id); + log.debug("device id {}", did); + elementOfNote = deviceService.getDevice(did); + log.debug("device element {}", elementOfNote); + + } catch (Exception e2) { + log.debug("Unable to process ID [{}]", id); + elementOfNote = null; + } + } + + switch (currentMode) { + case MOUSE: + sendMouseData(); + break; + + case LINK: + sendLinkData(); + break; + + default: + break; + } + + } + + private void clearForMode() { + sendHighlights(new Highlights()); + } + + private void sendHighlights(Highlights highlights) { + sendMessage(TopoJson.highlightsMessage(highlights)); + } + + + private void sendMouseData() { + if (elementOfNote != null && elementOfNote instanceof Device) { + DeviceId devId = (DeviceId) elementOfNote.id(); + Set<Link> links = linkService.getDeviceEgressLinks(devId); + sendHighlights(fromLinks(links, devId)); + } + // Note: could also process Host, if available + } + + private Highlights fromLinks(Set<Link> links, DeviceId devId) { + DemoLinkMap linkMap = new DemoLinkMap(); + if (links != null) { + log.debug("Processing {} links", links.size()); + links.forEach(linkMap::add); + } else { + log.debug("No egress links found for device {}", devId); + } + + Highlights highlights = new Highlights(); + + for (DemoLink dlink : linkMap.biLinks()) { + dlink.makeImportant().setLabel("Yo!"); + highlights.add(dlink.highlight(null)); + } + return highlights; + } + + private void initLinkSet() { + Set<Link> links = new HashSet<>(); + for (Link link : linkService.getActiveLinks()) { + links.add(link); + } + linkSet = links.toArray(new Link[links.size()]); + linkIndex = 0; + log.debug("initialized link set to {}", linkSet.length); + } + + private void sendLinkData() { + DemoLinkMap linkMap = new DemoLinkMap(); + for (Link link : linkSet) { + linkMap.add(link); + } + DemoLink dl = linkMap.add(linkSet[linkIndex]); + dl.makeImportant().setLabel(Integer.toString(linkIndex)); + log.debug("sending link data (index {})", linkIndex); + + linkIndex += 1; + if (linkIndex >= linkSet.length) { + linkIndex = 0; + } + + Highlights highlights = new Highlights(); + for (DemoLink dlink : linkMap.biLinks()) { + highlights.add(dlink.highlight(null)); + } + + sendHighlights(highlights); + } + + private synchronized void scheduleTask() { + if (demoTask == null) { + log.debug("Starting up demo task..."); + demoTask = new DisplayUpdateTask(); + timer.schedule(demoTask, UPDATE_PERIOD_MS, UPDATE_PERIOD_MS); + } else { + log.debug("(demo task already running"); + } + } + + private synchronized void cancelTask() { + if (demoTask != null) { + demoTask.cancel(); + demoTask = null; + } + } + + + private class DisplayUpdateTask extends TimerTask { + @Override + public void run() { + try { + switch (currentMode) { + case LINK: + sendLinkData(); + break; + + default: + break; + } + } catch (Exception e) { + log.warn("Unable to process demo task: {}", e.getMessage()); + log.debug("Oops", e); + } + } + } + +}
\ No newline at end of file diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovOverlay.java b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovOverlay.java new file mode 100644 index 00000000..98999825 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/AppUiTopovOverlay.java @@ -0,0 +1,75 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/* + * Copyright 2014,2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ${package}; + +import org.onosproject.ui.UiTopoOverlay; +import org.onosproject.ui.topo.ButtonId; +import org.onosproject.ui.topo.PropertyPanel; +import org.onosproject.ui.topo.TopoConstants.CoreButtons; +import org.onosproject.ui.topo.TopoConstants.Glyphs; + +import static org.onosproject.ui.topo.TopoConstants.Properties.*; + +/** + * Our topology overlay. + */ +public class AppUiTopovOverlay extends UiTopoOverlay { + + // NOTE: this must match the ID defined in sampleTopov.js + private static final String OVERLAY_ID = "meowster-overlay"; + + private static final String MY_TITLE = "My App Rocks!"; + private static final String MY_VERSION = "Beta-1.0.0042"; + private static final String MY_DEVICE_TITLE = "I changed the title"; + + private static final ButtonId FOO_BUTTON = new ButtonId("foo"); + private static final ButtonId BAR_BUTTON = new ButtonId("bar"); + + public AppUiTopovOverlay() { + super(OVERLAY_ID); + } + + + @Override + public void modifySummary(PropertyPanel pp) { + pp.title(MY_TITLE) + .typeId(Glyphs.CROWN) + .removeProps( + TOPOLOGY_SSCS, + INTENTS, + TUNNELS, + FLOWS, + VERSION + ) + .addProp(VERSION, MY_VERSION); + } + + @Override + public void modifyDeviceDetails(PropertyPanel pp) { + pp.title(MY_DEVICE_TITLE); + pp.removeProps(LATITUDE, LONGITUDE); + + pp.addButton(FOO_BUTTON) + .addButton(BAR_BUTTON); + + pp.removeButtons(CoreButtons.SHOW_PORT_VIEW) + .removeButtons(CoreButtons.SHOW_GROUP_VIEW); + } + +} diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/DemoLink.java b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/DemoLink.java new file mode 100644 index 00000000..4a97f7bd --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/DemoLink.java @@ -0,0 +1,57 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/* + * Copyright 2014,2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ${package}; + +import org.onosproject.net.Link; +import org.onosproject.net.LinkKey; +import org.onosproject.ui.topo.BiLink; +import org.onosproject.ui.topo.LinkHighlight; +import org.onosproject.ui.topo.LinkHighlight.Flavor; + +/** + * Our demo concrete class of a bi-link. We give it state so we can decide + * how to create link highlights. + */ +public class DemoLink extends BiLink { + + private boolean important = false; + private String label = null; + + public DemoLink(LinkKey key, Link link) { + super(key, link); + } + + public DemoLink makeImportant() { + important = true; + return this; + } + + public DemoLink setLabel(String label) { + this.label = label; + return this; + } + + @Override + public LinkHighlight highlight(Enum<?> anEnum) { + Flavor flavor = important ? Flavor.PRIMARY_HIGHLIGHT + : Flavor.SECONDARY_HIGHLIGHT; + return new LinkHighlight(this.linkId(), flavor) + .setLabel(label); + } +} diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/DemoLinkMap.java b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/DemoLinkMap.java new file mode 100644 index 00000000..cc13d998 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/java/DemoLinkMap.java @@ -0,0 +1,33 @@ +#set( $symbol_pound = '#' ) +#set( $symbol_dollar = '$' ) +#set( $symbol_escape = '\' ) +/* + * Copyright 2014,2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ${package}; + +import org.onosproject.net.Link; +import org.onosproject.net.LinkKey; +import org.onosproject.ui.topo.BiLinkMap; + +/** + * Our concrete link map. + */ +public class DemoLinkMap extends BiLinkMap<DemoLink> { + @Override + protected DemoLink create(LinkKey linkKey, Link link) { + return new DemoLink(linkKey, link); + } +} diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopov.css b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopov.css new file mode 100644 index 00000000..cbf460f9 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopov.css @@ -0,0 +1,2 @@ +/* css for sample app topology overlay */ + diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopov.html b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopov.html new file mode 100644 index 00000000..b1c9acbb --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopov.html @@ -0,0 +1,4 @@ +<!-- partial HTML --> +<div id="ov-sample-topov"> + <p>This is a hidden view .. just a placeholder to house the javascript</p> +</div> diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopovDemo.js b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopovDemo.js new file mode 100644 index 00000000..0b82d811 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopovDemo.js @@ -0,0 +1,104 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + Sample Demo module. This contains the "business logic" for the topology + overlay that we are implementing. + */ + +(function () { + 'use strict'; + + // injected refs + var $log, fs, flash, wss; + + // constants + var displayStart = 'sampleDisplayStart', + displayUpdate = 'sampleDisplayUpdate', + displayStop = 'sampleDisplayStop'; + + // internal state + var currentMode = null; + + + // === --------------------------- + // === Helper functions + + function sendDisplayStart(mode) { + wss.sendEvent(displayStart, { + mode: mode + }); + } + + function sendDisplayUpdate(what) { + wss.sendEvent(displayUpdate, { + id: what ? what.id : '' + }); + } + + function sendDisplayStop() { + wss.sendEvent(displayStop); + } + + // === --------------------------- + // === Main API functions + + function startDisplay(mode) { + if (currentMode === mode) { + $log.debug('(in mode', mode, 'already)'); + } else { + currentMode = mode; + sendDisplayStart(mode); + flash.flash('Starting display mode: ' + mode); + } + } + + function updateDisplay(m) { + if (currentMode) { + sendDisplayUpdate(m); + } + } + + function stopDisplay() { + if (currentMode) { + currentMode = null; + sendDisplayStop(); + flash.flash('Canceling display mode'); + return true; + } + return false; + } + + // === --------------------------- + // === Module Factory Definition + + angular.module('ovSampleTopov', []) + .factory('SampleTopovDemoService', + ['$log', 'FnService', 'FlashService', 'WebSocketService', + + function (_$log_, _fs_, _flash_, _wss_) { + $log = _$log_; + fs = _fs_; + flash = _flash_; + wss = _wss_; + + return { + startDisplay: startDisplay, + updateDisplay: updateDisplay, + stopDisplay: stopDisplay + }; + }]); +}()); diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopovOverlay.js b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopovOverlay.js new file mode 100644 index 00000000..12875e1f --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/app/view/sampleTopov/sampleTopovOverlay.js @@ -0,0 +1,143 @@ +// sample topology overlay - client side +// +// This is the glue that binds our business logic (in sampleTopovDemo.js) +// to the overlay framework. + +(function () { + 'use strict'; + + // injected refs + var $log, tov, stds; + + // internal state should be kept in the service module (not here) + + // our overlay definition + var overlay = { + // NOTE: this must match the ID defined in AppUiTopoOverlay + overlayId: 'meowster-overlay', + glyphId: '*star4', + tooltip: 'Sample Meowster Topo Overlay', + + // These glyphs get installed using the overlayId as a prefix. + // e.g. 'star4' is installed as 'meowster-overlay-star4' + // They can be referenced (from this overlay) as '*star4' + // That is, the '*' prefix stands in for 'meowster-overlay-' + glyphs: { + star4: { + vb: '0 0 8 8', + d: 'M1,4l2,-1l1,-2l1,2l2,1l-2,1l-1,2l-1,-2z' + }, + banner: { + vb: '0 0 6 6', + d: 'M1,1v4l2,-2l2,2v-4z' + } + }, + + activate: function () { + $log.debug("Sample topology overlay ACTIVATED"); + }, + deactivate: function () { + stds.stopDisplay(); + $log.debug("Sample topology overlay DEACTIVATED"); + }, + + // detail panel button definitions + buttons: { + foo: { + gid: 'chain', + tt: 'A FOO action', + cb: function (data) { + $log.debug('FOO action invoked with data:', data); + } + }, + bar: { + gid: '*banner', + tt: 'A BAR action', + cb: function (data) { + $log.debug('BAR action invoked with data:', data); + } + } + }, + + // Key bindings for traffic overlay buttons + // NOTE: fully qual. button ID is derived from overlay-id and key-name + keyBindings: { + 0: { + cb: function () { stds.stopDisplay(); }, + tt: 'Cancel Display Mode', + gid: 'xMark' + }, + V: { + cb: function () { stds.startDisplay('mouse'); }, + tt: 'Start Mouse Mode', + gid: '*banner' + }, + F: { + cb: function () { stds.startDisplay('link'); }, + tt: 'Start Link Mode', + gid: 'chain' + }, + G: { + cb: buttonCallback, + tt: 'Uses the G key', + gid: 'crown' + }, + + _keyOrder: [ + '0', 'V', 'F', 'G' + ] + }, + + hooks: { + // hook for handling escape key + // Must return true to consume ESC, false otherwise. + escape: function () { + // Must return true to consume ESC, false otherwise. + return stds.stopDisplay(); + }, + + // hooks for when the selection changes... + empty: function () { + selectionCallback('empty'); + }, + single: function (data) { + selectionCallback('single', data); + }, + multi: function (selectOrder) { + selectionCallback('multi', selectOrder); + tov.addDetailButton('foo'); + tov.addDetailButton('bar'); + }, + mouseover: function (m) { + // m has id, class, and type properties + $log.debug('mouseover:', m); + stds.updateDisplay(m); + }, + mouseout: function () { + $log.debug('mouseout'); + stds.updateDisplay(); + } + } + }; + + + function buttonCallback(x) { + $log.debug('Toolbar-button callback', x); + } + + function selectionCallback(x, d) { + $log.debug('Selection callback', x, d); + } + + // invoke code to register with the overlay service + angular.module('ovSampleTopov') + .run(['$log', 'TopoOverlayService', 'SampleTopovDemoService', + + function (_$log_, _tov_, _stds_) { + $log = _$log_; + tov = _tov_; + stds = _stds_; + tov.register(overlay); + }]); + +}()); diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/sampleTopov/css.html b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/sampleTopov/css.html new file mode 100644 index 00000000..0ed6f53c --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/sampleTopov/css.html @@ -0,0 +1 @@ +<link rel="stylesheet" href="app/view/sampleTopov/sampleTopov.css">
\ No newline at end of file diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/sampleTopov/js.html b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/sampleTopov/js.html new file mode 100644 index 00000000..4fed1f08 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitopo/src/main/resources/archetype-resources/src/main/resources/sampleTopov/js.html @@ -0,0 +1,2 @@ +<script src="app/view/sampleTopov/sampleTopovDemo.js"></script> +<script src="app/view/sampleTopov/sampleTopovOverlay.js"></script>
\ No newline at end of file diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/test/resources/projects/basic/archetype.properties b/framework/src/onos/tools/package/archetypes/uitopo/src/test/resources/projects/basic/archetype.properties new file mode 100644 index 00000000..a1213b40 --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitopo/src/test/resources/projects/basic/archetype.properties @@ -0,0 +1,21 @@ +# +# Copyright 2014 Open Networking Laboratory +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +#Thu Dec 04 09:24:50 PST 2014 +package=it.pkg +version=0.1-SNAPSHOT +groupId=archetype.it +artifactId=basic diff --git a/framework/src/onos/tools/package/archetypes/uitopo/src/test/resources/projects/basic/goal.txt b/framework/src/onos/tools/package/archetypes/uitopo/src/test/resources/projects/basic/goal.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/framework/src/onos/tools/package/archetypes/uitopo/src/test/resources/projects/basic/goal.txt diff --git a/framework/src/onos/tools/package/config/samples/component-cfg.json b/framework/src/onos/tools/package/config/samples/component-cfg.json new file mode 100644 index 00000000..f1168e44 --- /dev/null +++ b/framework/src/onos/tools/package/config/samples/component-cfg.json @@ -0,0 +1,5 @@ +{ + "org.onosproject.proxyarp.ProxyArp": { + "ipv6NeighborDiscovery": true + } +}
\ No newline at end of file diff --git a/framework/src/onos/tools/package/config/samples/network-cfg.json b/framework/src/onos/tools/package/config/samples/network-cfg.json new file mode 100644 index 00000000..c2af8b81 --- /dev/null +++ b/framework/src/onos/tools/package/config/samples/network-cfg.json @@ -0,0 +1,66 @@ +{ + "ports" : { + "of:0000000000000002/1" : { + "interfaces" : [ + { + "ips" : [ "192.168.10.101/24" ], + "mac" : "08:9e:01:82:38:68", + "vlan" : "100" + } + ] + }, + "of:0000000000000002/20" : { + "interfaces" : [ + { + "ips" : [ "192.168.20.101/24" ], + "mac" : "08:9e:01:82:38:68", + "vlan" : "200" + } + ] + } + }, + "devices" : { + "of:0000000000000002" : { + "segmentrouting" : { + "name" : "Leaf-R1", + "nodeSid" : 101, + "routerIp" : "10.0.1.254", + "routerMac" : "00:00:00:00:01:80", + "isEdgeRouter" : true, + "adjacencySids" : [ + { "sid" : 100, "port" : [2, 3] }, + { "sid" : 200, "port" : [4, 5] } + ] + } + }, + "of:0000000000000191" : { + "segmentrouting" : { + "name" : "Spine-R1", + "nodeSid" : 105, + "routerIp" : "192.168.0.11", + "routerMac" : "00:00:01:00:11:80", + "isEdgeRouter" : false + } + } + }, + "apps" : { + "org.onosproject.router" : { + "bgp" : { + "bgpSpeakers" : [ + { + "connectPoint" : "of:00000000000000aa/10", + "peers" : [ + "192.168.10.1" + ] + }, + { + "connectPoint" : "of:00000000000000aa/20", + "peers" : [ + "192.168.20.1" + ] + } + ] + } + } + } +} diff --git a/framework/src/onos/tools/test/bin/ogroup-opts b/framework/src/onos/tools/test/bin/ogroup-opts index 41842bdd..f02b7a8f 100644 --- a/framework/src/onos/tools/test/bin/ogroup-opts +++ b/framework/src/onos/tools/test/bin/ogroup-opts @@ -44,7 +44,7 @@ function _cell-opts () { fi } -complete -F _cell-opts cell +complete -F _cell-opts cell vicell # Tab completion settings for onos-create-app. diff --git a/framework/src/onos/tools/test/bin/onos b/framework/src/onos/tools/test/bin/onos index 89197db5..780a90dc 100755 --- a/framework/src/onos/tools/test/bin/onos +++ b/framework/src/onos/tools/test/bin/onos @@ -3,6 +3,27 @@ # ONOS remote command-line client. # ----------------------------------------------------------------------------- +function _usage () { +cat << _EOF_ +usage: + $(basename $0) [-w] [node] + +flags: +- -w : Waits for ONOS instance to reach run-level 100, i.e. to be fully started. + +options: +- [node] : the node to attach to + +summary: + ONOS remote command-line client. + + The -w flag depends on 'onos-wait-for-start'. If [node] is unspecified, \$OCI + is used. + +_EOF_ +} +[ "$1" = "-h" ] && _usage && exit 0 + [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 . $ONOS_ROOT/tools/build/envDefaults . $ONOS_ROOT/tools/test/bin/find-node.sh diff --git a/framework/src/onos/tools/test/bin/onos-archetypes-test b/framework/src/onos/tools/test/bin/onos-archetypes-test index 7ae00332..331c4332 100755 --- a/framework/src/onos/tools/test/bin/onos-archetypes-test +++ b/framework/src/onos/tools/test/bin/onos-archetypes-test @@ -3,6 +3,19 @@ # Builds a set of projects using ONOS archetypes. # ----------------------------------------------------------------------------- +function _usage () { +cat << _EOF_ +usage: + $(basename $0) + +summary: + Builds a set of projects using ONOS archetypes. + +_EOF_ +} + +[ "$1" = "-h" ] && _usage && exit 0 + set -e export AROOT=/tmp/foo diff --git a/framework/src/onos/tools/test/bin/onos-batch b/framework/src/onos/tools/test/bin/onos-batch index 67864a22..ae288916 100755 --- a/framework/src/onos/tools/test/bin/onos-batch +++ b/framework/src/onos/tools/test/bin/onos-batch @@ -3,6 +3,28 @@ # Executes selected set of ONOS commands using the batch mode. # ----------------------------------------------------------------------------- +function _usage () { +cat << _EOF_ +usage: + $(basename $0) [node] <commands> + +options: +- [node] <commands> : node to run <commands> + +summary: + Executes selected set of ONOS commands using the batch mode. + + <commands> is a comma-separated list of ONOS CLI commands. + + If [node] isn't specified, the defualt target becomes \$OCI. When no commands + are specified, the commands 'summary','intents','flows', and 'hosts' are + executed against \$OCI. + +_EOF_ +} + +[ "$1" = "-h" ] && _usage && exit 0 + [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 . $ONOS_ROOT/tools/build/envDefaults diff --git a/framework/src/onos/tools/test/bin/onos-config b/framework/src/onos/tools/test/bin/onos-config index 5c3ab024..348cb839 100755 --- a/framework/src/onos/tools/test/bin/onos-config +++ b/framework/src/onos/tools/test/bin/onos-config @@ -3,6 +3,28 @@ # Remotely configures & starts ONOS for the first time. # ----------------------------------------------------------------------------- +function _usage () { +cat << _EOF_ +usage: + $(basename $0) [node] + +options: +- [node] : The node to configure + +summary: + Remotely configures and starts ONOS for the first time. + + The procedure for configruing a node include determining base features, + applications to load at startup, and clustering and logical network view + configurations, among others. + + If [node] isn't specified, the defualt target becomes \$OCI. + +_EOF_ +} + +[ "$1" = "-h" ] && _usage && exit 0 + [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 . $ONOS_ROOT/tools/build/envDefaults diff --git a/framework/src/onos/tools/test/bin/onos-install b/framework/src/onos/tools/test/bin/onos-install index 7384c2e3..139944e4 100755 --- a/framework/src/onos/tools/test/bin/onos-install +++ b/framework/src/onos/tools/test/bin/onos-install @@ -3,6 +3,32 @@ # Remotely pushes bits to a remote node and installs ONOS on it. # ----------------------------------------------------------------------------- +function _usage () { +cat << _EOF_ +usage: + $(basename $0) [-fn] [-m] <settings> [node] + +flags: +- -f : forces uninstall of currently installed ONOS +- -n : do not copy over onos.conf upstart configuration file. +- -m <settings> : pass <settings> XML file to remote maven installation + +options: +- [node] : remote node to install ONOS on. + +summary: + Remotely pushes bits to a remote node and installs ONOS on it. + + The [-n] flag assumes that Upstart is used. The [-f] flag depends on + and 'onos-config'. + + If [node] is not specified the default target is \$OCI. + +_EOF_ +} + +[ "$1" = "-h" ] && _usage && exit 0 + [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 . $ONOS_ROOT/tools/build/envDefaults diff --git a/framework/src/onos/tools/test/bin/onos-push-bits b/framework/src/onos/tools/test/bin/onos-push-bits index 8c9276fc..4d425c63 100755 --- a/framework/src/onos/tools/test/bin/onos-push-bits +++ b/framework/src/onos/tools/test/bin/onos-push-bits @@ -2,6 +2,24 @@ # ----------------------------------------------------------------------------- # Remotely pushes bits to a remote node in preparation for install. # ----------------------------------------------------------------------------- +function _usage () { +cat << _EOF_ +usage: + $(basename $0) [node] + +options: +- [node] : the target node to prime for installation + +summary: + Remotely pushes bits to a remote node in preparation for install. + + $(basename $0) is invoked as part of 'onos-install', and shouldn't be + directly invoked for the most part. + +_EOF_ +} + +[ "$1" = "-h" ] && _usage && exit 0 [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 . $ONOS_ROOT/tools/build/envDefaults diff --git a/framework/src/onos/tools/test/bin/onos-service b/framework/src/onos/tools/test/bin/onos-service index cc694911..35764e6f 100755 --- a/framework/src/onos/tools/test/bin/onos-service +++ b/framework/src/onos/tools/test/bin/onos-service @@ -34,7 +34,6 @@ fi case $2 in start|stop|restart|status) - # Select the target if [ "${1}" = "--cell" ]; then nodes=$(env | sort | egrep "OC[0-9]+" | cut -d= -f2) diff --git a/framework/src/onos/tools/test/bin/onos-set-controllers b/framework/src/onos/tools/test/bin/onos-set-controllers index 5b3cd6f7..27cc16c8 100755 --- a/framework/src/onos/tools/test/bin/onos-set-controllers +++ b/framework/src/onos/tools/test/bin/onos-set-controllers @@ -6,7 +6,7 @@ controllers="" for node in $ONOS_INSTANCES; do - controllers="$controllers tcp:$node:${OF_PORT:-6633}" + controllers="$controllers tcp:$node:${OF_PORT:-6653}" done ssh ${ONOS_USER:-sdn}@$OCN " diff --git a/framework/src/onos/tools/test/bin/onos-uninstall b/framework/src/onos/tools/test/bin/onos-uninstall index 7a8b9a5f..ff8ff536 100755 --- a/framework/src/onos/tools/test/bin/onos-uninstall +++ b/framework/src/onos/tools/test/bin/onos-uninstall @@ -3,6 +3,24 @@ # Remotely stops & uninstalls ONOS on the specified node. # ----------------------------------------------------------------------------- +function _usage () { +cat << _EOF_ +usage: + $(basename $0) [node] + +options: +- [node] : The remote instance to uninstall ONOS from. + +summary: + Remotely stops and uninstalls ONOS on the specified node. + + If [node] isn't specified, \$OCI becomes the target. + +_EOF_ +} + +[ "$1" = "-h" ] && _usage && exit 0 + [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 . $ONOS_ROOT/tools/build/envDefaults @@ -16,9 +34,12 @@ ssh $remote " [ -z \"\$(ps -ef | grep karaf.jar | grep -v grep)\" ] && break sleep 1 done - [ -z \"\$(ps -ef | grep karaf.jar | grep -v grep)\" ] || echo 'ONOS failed to stop.' + [ -z \"\$(ps -ef | grep karaf.jar | grep -v grep)\" ] || \ + (echo 'ONOS failed to stop.'; status=1) # Remove onos directory and init file - sudo rm -fr $ONOS_INSTALL_DIR - [ -f /etc/init/onos.conf ] && sudo rm /etc/init/onos.conf + [ -d $ONOS_INSTALL_DIR ] && sudo rm -fr $ONOS_INSTALL_DIR + [ -f /etc/init/onos.conf ] && sudo rm -f /etc/init/onos.conf + + exit \${status:-0}; " diff --git a/framework/src/onos/tools/test/bin/onos-watch b/framework/src/onos/tools/test/bin/onos-watch index 28e88c2f..11962f9e 100755 --- a/framework/src/onos/tools/test/bin/onos-watch +++ b/framework/src/onos/tools/test/bin/onos-watch @@ -2,6 +2,30 @@ # ----------------------------------------------------------------------------- # Monitors selected set of ONOS commands using the system watch command. # ----------------------------------------------------------------------------- +function _usage () { +cat << _EOF_ +usage: + $(basename $0) [node] <commands> [watchflags] + +options: +- [node] <commands> : the node to run the commands against +- [watchflags] : flags to be passed to the watch command. + +summary: + Monitors selected set of ONOS commands using the system watch command. + + <commands> is a comma-sepatarted list of ONOS CLI commands. If no commands + are supplied, the commands run are 'summary', 'intents', 'flows', and + 'hosts' against \$OCI. + + Note that [watchflags] only applies to platforms with the Linux-like watch + command. For other platforms, the default behavior of watch (refresh every 2 + s) is emulated. + +_EOF_ +} + +[ "$1" = "-h" ] && _usage && exit 0 [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 . $ONOS_ROOT/tools/build/envDefaults @@ -14,4 +38,10 @@ aux=/tmp/onos-watch.$$ trap "rm -f $aux" EXIT echo "$commands" | tr ',' '\n' > $aux -watch $3 "onos $node -b <$aux 2>/dev/null" + +# emulate watch if not Linux. +if [ "$(uname)" != "Linux" ]; then + while clear; "onos $node -b <$aux 2>/dev/null" ; do sleep 2; done +else + watch $3 "onos $node -b <$aux 2>/dev/null" +fi diff --git a/framework/src/onos/tools/test/cells/andrea b/framework/src/onos/tools/test/cells/andrea new file mode 100644 index 00000000..1f9f22fe --- /dev/null +++ b/framework/src/onos/tools/test/cells/andrea @@ -0,0 +1,11 @@ +# Andrea's ProxMox ONOS instances 1,2,3 & ONOS mininet box + +export ONOS_NIC="10.128.12.*" +export OC1="10.128.12.1" +export OC2="10.128.12.2" +export OC3="10.128.12.3" +export OCN="10.128.12.4" + +export OCT=$OC1 +export ONOS_USE_SSH=true +export ONOS_APPS=drivers,openflow,proxyarp,ovsdb diff --git a/framework/src/onos/tools/test/cells/tomx b/framework/src/onos/tools/test/cells/tomx index 3e528ee8..91036e67 100644 --- a/framework/src/onos/tools/test/cells/tomx +++ b/framework/src/onos/tools/test/cells/tomx @@ -7,4 +7,5 @@ export OC3="10.128.11.3" export OCN="10.128.11.4" export OCT=$OC1 -export ONOS_APPS=drivers,openflow,proxyarp
\ No newline at end of file +export ONOS_USE_SSH=true +export ONOS_APPS=drivers,openflow,proxyarp,mobility
\ No newline at end of file diff --git a/framework/src/onos/tools/test/scenarios/archetypes.xml b/framework/src/onos/tools/test/scenarios/archetypes.xml index 8244a32f..5440b55f 100644 --- a/framework/src/onos/tools/test/scenarios/archetypes.xml +++ b/framework/src/onos/tools/test/scenarios/archetypes.xml @@ -44,6 +44,24 @@ <step name="Verify-UI" requires="^" exec="onos-check-views ${OCI} id=sample"/> + <step name="Create-App-UI-Table-Overlay" requires="Reinstall-App-With-UI" + exec="onos-create-app uitab org.test.app test-app 1.2.3 org.test.app -DinteractiveMode=false"/> + <step name="Build-App-With-UI-Table" requires="^" + exec="mvn clean install"/> + <step name="Reinstall-App-With-UI-Table" requires="^,~Verify-UI" + exec="onos-app ${OCI} reinstall! target/test-app-1.2.3.oar"/> + <step name="Verify-UI-Table" requires="^" + exec="onos-check-views ${OCI} id=sample"/> + + <step name="Create-App-UI-Topo-Overlay" requires="Reinstall-App-With-UI-Table" + exec="onos-create-app uitopo org.test.app test-app 1.2.3 org.test.app -DinteractiveMode=false"/> + <step name="Build-App-With-UI-Topo" requires="^" + exec="mvn clean install"/> + <step name="Reinstall-App-With-UI-Topo" requires="^,~Verify-UI-Table" + exec="onos-app ${OCI} reinstall! target/test-app-1.2.3.oar"/> + <step name="Verify-UI-Topo" requires="^" + exec="onos-check-views ${OCI} id=sample"/> + <step name="Uninstall-App" requires="^" exec="onos-app ${OCI} uninstall org.foo.app"/> </group> diff --git a/framework/src/onos/tools/test/topos/onos.py b/framework/src/onos/tools/test/topos/onos.py index ae6045e3..f9d4ba23 100755 --- a/framework/src/onos/tools/test/topos/onos.py +++ b/framework/src/onos/tools/test/topos/onos.py @@ -31,7 +31,7 @@ class ONOS( Controller ): Controller.__init__( self, name, **kwargs ) # the following have been done for us: #self.ip = ip ('127.0.0.1') - #self.port = port (6633) + #self.port = port (6653) #self.protocol = protocol ('tcp') #self.checkListening() diff --git a/framework/src/onos/tools/test/topos/optical2.py b/framework/src/onos/tools/test/topos/optical2.py index 36cdbadd..b778592c 100644 --- a/framework/src/onos/tools/test/topos/optical2.py +++ b/framework/src/onos/tools/test/topos/optical2.py @@ -69,7 +69,7 @@ topos = {'optical': ( lambda: OpticalTopo() )} def run(): - c = RemoteController('c','127.0.0.1',6633) + c = RemoteController('c','127.0.0.1',6653) net = Mininet( topo=OpticalTopo(),controller=None,autoSetMacs=True) net.addController(c) net.start() diff --git a/framework/src/onos/tools/test/topos/solar.py b/framework/src/onos/tools/test/topos/solar.py index ea11b664..f316162a 100644 --- a/framework/src/onos/tools/test/topos/solar.py +++ b/framework/src/onos/tools/test/topos/solar.py @@ -23,7 +23,7 @@ class Solar(object): # We are creating the controller with local-loopback on purpose to avoid # having the switches connect immediately. Instead, we'll set controller # explicitly for each switch after configuring it as we want. - self.ctrls = [ RemoteController(cname, cip, 6633) for cip in cips ] + self.ctrls = [ RemoteController(cname, cip, 6653) for cip in cips ] self.net = Mininet(controller=RemoteController, switch = OVSKernelSwitch, build=False) diff --git a/framework/src/onos/tools/test/topos/sys-nonlinear-10.config b/framework/src/onos/tools/test/topos/sys-nonlinear-10.config index 2b999d39..b3a718dd 100644 --- a/framework/src/onos/tools/test/topos/sys-nonlinear-10.config +++ b/framework/src/onos/tools/test/topos/sys-nonlinear-10.config @@ -56,7 +56,7 @@ [{switch,1, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:01"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -66,7 +66,7 @@ {switch,2, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:02"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -76,7 +76,7 @@ {switch,3, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:03"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -86,7 +86,7 @@ {switch,4, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:04"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -96,7 +96,7 @@ {switch,5, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:05"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -107,7 +107,7 @@ {switch,7, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:07"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -118,7 +118,7 @@ {switch,8, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:08"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -128,7 +128,7 @@ {switch,9, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:09"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -138,7 +138,7 @@ {switch,10, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:0A"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -148,7 +148,7 @@ {switch,6, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:06"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, diff --git a/framework/src/onos/tools/test/topos/sys-nonlinear-4.config b/framework/src/onos/tools/test/topos/sys-nonlinear-4.config index b843146a..b1451bee 100644 --- a/framework/src/onos/tools/test/topos/sys-nonlinear-4.config +++ b/framework/src/onos/tools/test/topos/sys-nonlinear-4.config @@ -18,7 +18,7 @@ [{switch,1, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:01"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -28,7 +28,7 @@ {switch,3, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:03"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -37,7 +37,7 @@ {switch,2, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:02"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -47,7 +47,7 @@ {switch,4, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:04"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, diff --git a/framework/src/onos/tools/test/topos/sys.config b/framework/src/onos/tools/test/topos/sys.config index 2b999d39..b3a718dd 100644 --- a/framework/src/onos/tools/test/topos/sys.config +++ b/framework/src/onos/tools/test/topos/sys.config @@ -56,7 +56,7 @@ [{switch,1, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:01"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -66,7 +66,7 @@ {switch,2, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:02"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -76,7 +76,7 @@ {switch,3, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:03"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -86,7 +86,7 @@ {switch,4, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:04"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -96,7 +96,7 @@ {switch,5, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:05"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -107,7 +107,7 @@ {switch,7, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:07"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -118,7 +118,7 @@ {switch,8, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:08"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -128,7 +128,7 @@ {switch,9, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:09"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -138,7 +138,7 @@ {switch,10, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:0A"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, @@ -148,7 +148,7 @@ {switch,6, [{backend,linc_us4_oe}, {datapath_id,"00:00:ff:ff:ff:ff:ff:06"}, - {controllers,[{"Switch0-Controller","10.1.8.147",6633,tcp}]}, + {controllers,[{"Switch0-Controller","10.1.8.147",6653,tcp}]}, {controllers_listener,disabled}, {queues_status,disabled}, {ports, diff --git a/framework/src/onos/utils/junit/src/main/java/org/onlab/junit/TestUtils.java b/framework/src/onos/utils/junit/src/main/java/org/onlab/junit/TestUtils.java index 1afc4948..686a9a59 100644 --- a/framework/src/onos/utils/junit/src/main/java/org/onlab/junit/TestUtils.java +++ b/framework/src/onos/utils/junit/src/main/java/org/onlab/junit/TestUtils.java @@ -64,27 +64,40 @@ public final class TestUtils { /** * Gets the field, bypassing scope restriction. * - * @param subject Object where the field belongs + * @param subject Object where the field belongs * @param fieldName name of the field to get + * @param <T> subject type + * @param <U> fieldO value type * @return value of the field. - * @param <T> subject type - * @param <U> field value type * @throws TestUtilsException if there are reflection errors while getting - * the field + * the field */ public static <T, U> U getField(T subject, String fieldName) throws TestUtilsException { try { + NoSuchFieldException exception = null; @SuppressWarnings("unchecked") - Class<T> clazz = (Class<T>) subject.getClass(); - Field field = clazz.getDeclaredField(fieldName); - field.setAccessible(true); + Class clazz = subject.getClass(); + while (clazz != null) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); - @SuppressWarnings("unchecked") - U result = (U) field.get(subject); - return result; - } catch (NoSuchFieldException | SecurityException | - IllegalArgumentException | IllegalAccessException e) { + @SuppressWarnings("unchecked") + U result = (U) field.get(subject); + return result; + } catch (NoSuchFieldException e) { + exception = e; + if (clazz == clazz.getSuperclass()) { + break; + } + clazz = clazz.getSuperclass(); + } + } + throw new TestUtilsException("Field not found. " + fieldName, exception); + + } catch (SecurityException | + IllegalArgumentException | IllegalAccessException e) { throw new TestUtilsException("getField failed", e); } } diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DisjointPathPair.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DisjointPathPair.java index b62d3b24..206a34c8 100644 --- a/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DisjointPathPair.java +++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/graph/DisjointPathPair.java @@ -19,52 +19,67 @@ package org.onlab.graph; import java.util.List; import java.util.Objects; -import java.util.Set; -import static com.google.common.collect.ImmutableSet.of; import static com.google.common.base.MoreObjects.toStringHelper; - +/** + * Pair of disjoint paths. + * + * @param <V> type of vertex + * @param <E> type of edge + */ public class DisjointPathPair<V extends Vertex, E extends Edge<V>> implements Path<V, E> { - public Path<V, E> path1, path2; - boolean usingPath1 = true; + + private Path<V, E> primary, secondary; + boolean primaryActive = true; /** - * Creates a Disjoint Path Pair from two paths. + * Creates a disjoint path pair from two paths. * - * @param p1 first path - * @param p2 second path + * @param primary primary path + * @param secondary secondary path */ - public DisjointPathPair(Path<V, E> p1, Path<V, E> p2) { - path1 = p1; - path2 = p2; + public DisjointPathPair(Path<V, E> primary, Path<V, E> secondary) { + this.primary = primary; + this.secondary = secondary; } @Override public V src() { - return path1.src(); + return primary.src(); } @Override public V dst() { - return path1.dst(); + return primary.dst(); + } + + /** + * Returns the primary path. + * + * @return primary path + */ + public Path<V, E> primary() { + return primary; + } + + /** + * Returns the secondary path. + * + * @return primary path + */ + public Path<V, E> secondary() { + return secondary; } @Override public double cost() { - if (!hasBackup()) { - return path1.cost(); - } - return path1.cost() + path2.cost(); + return hasBackup() ? primary.cost() + secondary.cost() : primary.cost(); } @Override public List<E> edges() { - if (usingPath1 || !hasBackup()) { - return path1.edges(); - } else { - return path2.edges(); - } + return primaryActive || !hasBackup() ? primary.edges() : secondary.edges(); } /** @@ -73,7 +88,7 @@ public class DisjointPathPair<V extends Vertex, E extends Edge<V>> implements Pa * @return boolean representing whether it has backup */ public boolean hasBackup() { - return path2 != null && path2.edges() != null; + return secondary != null && secondary.edges() != null; } @Override @@ -88,13 +103,8 @@ public class DisjointPathPair<V extends Vertex, E extends Edge<V>> implements Pa @Override public int hashCode() { - Set<Path<V, E>> paths; - if (!hasBackup()) { - paths = of(path1); - } else { - paths = of(path1, path2); - } - return Objects.hash(paths); + return hasBackup() ? Objects.hash(primary) + Objects.hash(secondary) : + Objects.hash(primary); } @Override @@ -106,10 +116,10 @@ public class DisjointPathPair<V extends Vertex, E extends Edge<V>> implements Pa final DisjointPathPair other = (DisjointPathPair) obj; return Objects.equals(this.src(), other.src()) && Objects.equals(this.dst(), other.dst()) && - (Objects.equals(this.path1, other.path1) && - Objects.equals(this.path2, other.path2)) || - (Objects.equals(this.path1, other.path2) && - Objects.equals(this.path2, other.path1)); + (Objects.equals(this.primary, other.primary) && + Objects.equals(this.secondary, other.secondary)) || + (Objects.equals(this.primary, other.secondary) && + Objects.equals(this.secondary, other.primary)); } return false; } @@ -120,9 +130,6 @@ public class DisjointPathPair<V extends Vertex, E extends Edge<V>> implements Pa * @return number of paths */ public int size() { - if (hasBackup()) { - return 2; - } - return 1; + return hasBackup() ? 2 : 1; } } diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrGroup.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrGroup.java index 891a0193..be4ab19a 100644 --- a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrGroup.java +++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrGroup.java @@ -241,7 +241,7 @@ public class PIMAddrGroup { return false; } final PIMAddrGroup other = (PIMAddrGroup) obj; - if (this.family != this.family) { + if (this.family != other.family) { return false; } diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrSource.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrSource.java index 2d4a7816..21526408 100644 --- a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrSource.java +++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrSource.java @@ -265,7 +265,7 @@ public class PIMAddrSource { return false; } final PIMAddrSource other = (PIMAddrSource) obj; - if (this.family != this.family) { + if (this.family != other.family) { return false; } diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrUnicast.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrUnicast.java index 0c2d676b..a6ba3895 100644 --- a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrUnicast.java +++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrUnicast.java @@ -166,7 +166,7 @@ public class PIMAddrUnicast { return false; } final PIMAddrUnicast other = (PIMAddrUnicast) obj; - if (this.family != this.family) { + if (this.family != other.family) { return false; } diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/HexDump.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/HexDump.java new file mode 100755 index 00000000..cfb79390 --- /dev/null +++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/HexDump.java @@ -0,0 +1,57 @@ +/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.util;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * HexDump class an utility to dump buffer in hex format.
+ */
+public final class HexDump {
+ protected static final Logger log = LoggerFactory.getLogger(HexDump.class);
+
+ private HexDump() {
+ }
+
+ /**
+ * Dump the buffer content in hex format.
+ *
+ * @param buff buffer content to dump in hex format
+ */
+ public static void dump(ChannelBuffer buff) {
+ try {
+ byte[] yTemp;
+ yTemp = buff.array();
+
+ int iStartIndex = buff.readerIndex();
+ int iEndIndex = buff.writerIndex();
+ do {
+ StringBuilder sb = new StringBuilder();
+ for (int k = 0; (k < 16) && (iStartIndex < iEndIndex); ++k) {
+ if (0 == k % 4) {
+ sb.append(String.format(" ")); // blank after 4 bytes
+ }
+ sb.append(String.format("%02X ", yTemp[iStartIndex++]));
+ }
+ log.debug(sb.toString());
+ } while (iStartIndex < iEndIndex);
+ } catch (Exception e) {
+ log.error("[HexDump] Invalid buffer: " + e.toString());
+ }
+ }
+}
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Tools.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Tools.java index abc48ccf..1b788145 100644 --- a/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Tools.java +++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/util/Tools.java @@ -299,12 +299,14 @@ public abstract class Tools { * * @param path file path * @return file contents + * @deprecated in Emu release */ + @Deprecated public static List<String> slurp(File path) { - try { + try ( BufferedReader br = new BufferedReader( new InputStreamReader(new FileInputStream(path), StandardCharsets.UTF_8)); - + ) { List<String> lines = new ArrayList<>(); String line; while ((line = br.readLine()) != null) { diff --git a/framework/src/onos/utils/misc/src/test/java/org/onlab/graph/SRLGGraphSearchTest.java b/framework/src/onos/utils/misc/src/test/java/org/onlab/graph/SRLGGraphSearchTest.java index 885fbe5c..8bfd270c 100644 --- a/framework/src/onos/utils/misc/src/test/java/org/onlab/graph/SRLGGraphSearchTest.java +++ b/framework/src/onos/utils/misc/src/test/java/org/onlab/graph/SRLGGraphSearchTest.java @@ -17,44 +17,37 @@ package org.onlab.graph; import org.junit.Test; -import java.util.Set; + +import java.util.HashMap; import java.util.HashSet; import java.util.Map; -import java.util.HashMap; +import java.util.Set; import static com.google.common.collect.ImmutableSet.of; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; - - +import static org.onlab.graph.GraphPathSearch.ALL_PATHS; /** * Test of the Suurballe backup path algorithm. */ public class SRLGGraphSearchTest extends BreadthFirstSearchTest { + @Override protected AbstractGraphPathSearch<TestVertex, TestEdge> graphSearch() { - return new SRLGGraphSearch<TestVertex, TestEdge>(null); + return new SRLGGraphSearch<>(null); } - public void setWeights() { - weight = new EdgeWeight<TestVertex, TestEdge>() { - @Override - public double weight(TestEdge edge) { - return edge.weight(); - } - }; - } public void setDefaultWeights() { weight = null; } + @Override public void defaultGraphTest() { - } @Override public void defaultHopCountWeight() { - } @Test @@ -66,34 +59,34 @@ public class SRLGGraphSearchTest extends BreadthFirstSearchTest { TestEdge dC = new TestEdge(D, C, 1); Graph<TestVertex, TestEdge> graph = new AdjacencyListsGraph<>(of(A, B, C, D), of(aB, bC, aD, dC)); - Map<TestEdge, Integer> riskProfile = new HashMap<TestEdge, Integer>(); + Map<TestEdge, Integer> riskProfile = new HashMap<>(); riskProfile.put(aB, 0); riskProfile.put(bC, 0); riskProfile.put(aD, 1); riskProfile.put(dC, 1); - SRLGGraphSearch<TestVertex, TestEdge> search = - new SRLGGraphSearch<TestVertex, TestEdge>(2, riskProfile); - Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, C, weight, GraphPathSearch.ALL_PATHS).paths(); + SRLGGraphSearch<TestVertex, TestEdge> search = new SRLGGraphSearch<>(2, riskProfile); + Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, C, weight, ALL_PATHS).paths(); System.out.println("\n\n\n" + paths + "\n\n\n"); - assertTrue("one disjoint path pair found", paths.size() == 1); + assertEquals("one disjoint path pair found", 1, paths.size()); checkIsDisjoint(paths.iterator().next(), riskProfile); } + public void checkIsDisjoint(Path<TestVertex, TestEdge> p, Map<TestEdge, Integer> risks) { assertTrue("The path is not a DisjointPathPair", (p instanceof DisjointPathPair)); DisjointPathPair<TestVertex, TestEdge> q = (DisjointPathPair) p; - Set<Integer> p1Risks = new HashSet<Integer>(); - Set<Integer> p2Risks = new HashSet<Integer>(); - for (TestEdge e: q.edges()) { + Set<Integer> p1Risks = new HashSet<>(); + for (TestEdge e : q.edges()) { p1Risks.add(risks.get(e)); } if (!q.hasBackup()) { return; } - Path<TestVertex, TestEdge> pq = q.path2; + Path<TestVertex, TestEdge> pq = q.secondary(); for (TestEdge e: pq.edges()) { assertTrue("The paths are not disjoint", !p1Risks.contains(risks.get(e))); } } + @Test public void complexGraphTest() { setDefaultWeights(); @@ -105,16 +98,15 @@ public class SRLGGraphSearchTest extends BreadthFirstSearchTest { TestEdge bE = new TestEdge(B, E, 1); Graph<TestVertex, TestEdge> graph = new AdjacencyListsGraph<>(of(A, B, C, D, E), of(aB, bC, aD, dC, cE, bE)); - Map<TestEdge, Integer> riskProfile = new HashMap<TestEdge, Integer>(); + Map<TestEdge, Integer> riskProfile = new HashMap<>(); riskProfile.put(aB, 0); riskProfile.put(bC, 0); riskProfile.put(aD, 1); riskProfile.put(dC, 1); riskProfile.put(cE, 2); riskProfile.put(bE, 3); - SRLGGraphSearch<TestVertex, TestEdge> search = - new SRLGGraphSearch<TestVertex, TestEdge>(4, riskProfile); - Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, E, weight, GraphPathSearch.ALL_PATHS).paths(); + SRLGGraphSearch<TestVertex, TestEdge> search = new SRLGGraphSearch<>(4, riskProfile); + search.search(graph, A, E, weight, ALL_PATHS).paths(); } @Test @@ -128,19 +120,19 @@ public class SRLGGraphSearchTest extends BreadthFirstSearchTest { TestEdge cE = new TestEdge(C, E, 1); Graph<TestVertex, TestEdge> graph = new AdjacencyListsGraph<>(of(A, B, C, D, E), of(aB, bE, aD, dE, aC, cE)); - Map<TestEdge, Integer> riskProfile = new HashMap<TestEdge, Integer>(); + Map<TestEdge, Integer> riskProfile = new HashMap<>(); riskProfile.put(aB, 0); riskProfile.put(bE, 1); riskProfile.put(aD, 2); riskProfile.put(dE, 3); riskProfile.put(aC, 4); riskProfile.put(cE, 5); - SRLGGraphSearch<TestVertex, TestEdge> search = - new SRLGGraphSearch<TestVertex, TestEdge>(6, riskProfile); - Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, E, weight, GraphPathSearch.ALL_PATHS).paths(); + SRLGGraphSearch<TestVertex, TestEdge> search = new SRLGGraphSearch<>(6, riskProfile); + Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, E, weight, ALL_PATHS).paths(); assertTrue("> one disjoint path pair found", paths.size() >= 1); checkIsDisjoint(paths.iterator().next(), riskProfile); } + @Test public void onePath() { setDefaultWeights(); @@ -150,17 +142,17 @@ public class SRLGGraphSearchTest extends BreadthFirstSearchTest { TestEdge dC = new TestEdge(D, C, 1); Graph<TestVertex, TestEdge> graph = new AdjacencyListsGraph<>(of(A, B, C, D), of(aB, bC, aD, dC)); - Map<TestEdge, Integer> riskProfile = new HashMap<TestEdge, Integer>(); + Map<TestEdge, Integer> riskProfile = new HashMap<>(); riskProfile.put(aB, 0); riskProfile.put(bC, 0); riskProfile.put(aD, 1); riskProfile.put(dC, 0); - SRLGGraphSearch<TestVertex, TestEdge> search = - new SRLGGraphSearch<TestVertex, TestEdge>(2, riskProfile); - Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, C, weight, GraphPathSearch.ALL_PATHS).paths(); + SRLGGraphSearch<TestVertex, TestEdge> search = new SRLGGraphSearch<>(2, riskProfile); + Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, C, weight, ALL_PATHS).paths(); System.out.println(paths); assertTrue("no disjoint path pairs found", paths.size() == 0); } + @Test public void noPath() { setDefaultWeights(); @@ -175,9 +167,8 @@ public class SRLGGraphSearchTest extends BreadthFirstSearchTest { riskProfile.put(bC, 0); riskProfile.put(aD, 1); riskProfile.put(dC, 0); - SRLGGraphSearch<TestVertex, TestEdge> search = - new SRLGGraphSearch<>(2, riskProfile); - Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, E, weight, GraphPathSearch.ALL_PATHS).paths(); + SRLGGraphSearch<TestVertex, TestEdge> search = new SRLGGraphSearch<>(2, riskProfile); + Set<Path<TestVertex, TestEdge>> paths = search.search(graph, A, E, weight, ALL_PATHS).paths(); assertTrue("no disjoint path pairs found", paths.size() == 0); } } diff --git a/framework/src/onos/utils/misc/src/test/java/org/onlab/util/AbstractAccumulatorTest.java b/framework/src/onos/utils/misc/src/test/java/org/onlab/util/AbstractAccumulatorTest.java index 02f0deb1..db7224ad 100644 --- a/framework/src/onos/utils/misc/src/test/java/org/onlab/util/AbstractAccumulatorTest.java +++ b/framework/src/onos/utils/misc/src/test/java/org/onlab/util/AbstractAccumulatorTest.java @@ -15,23 +15,24 @@ */ package org.onlab.util; -import org.junit.Ignore; import org.junit.Test; import java.util.List; -import java.util.Timer; import java.util.stream.IntStream; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.onlab.junit.TestTools.assertAfter; -import static org.onlab.junit.TestTools.delay; /** * Tests the operation of the accumulator. */ public class AbstractAccumulatorTest { - private final Timer timer = new Timer(); + + private final ManuallyAdvancingTimer timer = new ManuallyAdvancingTimer(); + @Test public void basics() throws Exception { @@ -42,7 +43,6 @@ public class AbstractAccumulatorTest { assertEquals("incorrect idle ms", 70, accumulator.maxIdleMillis()); } - @Ignore("FIXME: timing sensitive test failing randomly.") @Test public void eventTrigger() { TestAccumulator accumulator = new TestAccumulator(); @@ -52,43 +52,40 @@ public class AbstractAccumulatorTest { accumulator.add(new TestItem("d")); assertTrue("should not have fired yet", accumulator.batch.isEmpty()); accumulator.add(new TestItem("e")); - delay(20); + timer.advanceTimeMillis(20, 10); assertFalse("should have fired", accumulator.batch.isEmpty()); assertEquals("incorrect batch", "abcde", accumulator.batch); } - @Ignore("FIXME: timing sensitive test failing randomly.") @Test public void timeTrigger() { TestAccumulator accumulator = new TestAccumulator(); accumulator.add(new TestItem("a")); - delay(30); + timer.advanceTimeMillis(30, 1); assertTrue("should not have fired yet", accumulator.batch.isEmpty()); accumulator.add(new TestItem("b")); - delay(30); + timer.advanceTimeMillis(30, 1); assertTrue("should not have fired yet", accumulator.batch.isEmpty()); accumulator.add(new TestItem("c")); - delay(30); + timer.advanceTimeMillis(30, 1); assertTrue("should not have fired yet", accumulator.batch.isEmpty()); accumulator.add(new TestItem("d")); - delay(60); + timer.advanceTimeMillis(10, 10); assertFalse("should have fired", accumulator.batch.isEmpty()); assertEquals("incorrect batch", "abcd", accumulator.batch); } - @Ignore("FIXME: timing sensitive test failing randomly.") @Test public void idleTrigger() { TestAccumulator accumulator = new TestAccumulator(); accumulator.add(new TestItem("a")); assertTrue("should not have fired yet", accumulator.batch.isEmpty()); accumulator.add(new TestItem("b")); - delay(80); + timer.advanceTimeMillis(70, 10); assertFalse("should have fired", accumulator.batch.isEmpty()); assertEquals("incorrect batch", "ab", accumulator.batch); } - @Ignore("FIXME: timing sensitive test failing randomly.") @Test public void readyIdleTrigger() { TestAccumulator accumulator = new TestAccumulator(); @@ -96,30 +93,28 @@ public class AbstractAccumulatorTest { accumulator.add(new TestItem("a")); assertTrue("should not have fired yet", accumulator.batch.isEmpty()); accumulator.add(new TestItem("b")); - delay(80); + timer.advanceTimeMillis(80, 1); assertTrue("should not have fired yet", accumulator.batch.isEmpty()); accumulator.ready = true; - delay(80); + timer.advanceTimeMillis(80, 10); assertFalse("should have fired", accumulator.batch.isEmpty()); assertEquals("incorrect batch", "ab", accumulator.batch); } - @Ignore("FIXME: timing sensitive test failing randomly.") @Test public void readyLongTrigger() { TestAccumulator accumulator = new TestAccumulator(); accumulator.ready = false; - delay(120); + timer.advanceTimeMillis(120, 1); assertTrue("should not have fired yet", accumulator.batch.isEmpty()); accumulator.add(new TestItem("a")); assertTrue("should not have fired yet", accumulator.batch.isEmpty()); accumulator.ready = true; - delay(80); + timer.advanceTimeMillis(120, 10); assertFalse("should have fired", accumulator.batch.isEmpty()); assertEquals("incorrect batch", "a", accumulator.batch); } - @Ignore("FIXME: timing sensitive test failing randomly.") @Test public void readyMaxTrigger() { TestAccumulator accumulator = new TestAccumulator(); @@ -133,16 +128,16 @@ public class AbstractAccumulatorTest { assertTrue("should not have fired yet", accumulator.batch.isEmpty()); accumulator.ready = true; accumulator.add(new TestItem("g")); - delay(5); + timer.advanceTimeMillis(10, 10); assertFalse("should have fired", accumulator.batch.isEmpty()); assertEquals("incorrect batch", "abcdefg", accumulator.batch); } - @Ignore("FIXME: timing sensitive test failing randomly.") @Test public void stormTest() { TestAccumulator accumulator = new TestAccumulator(); IntStream.range(0, 1000).forEach(i -> accumulator.add(new TestItem("#" + i))); + timer.advanceTimeMillis(1); assertAfter(100, () -> assertEquals("wrong item count", 1000, accumulator.itemCount)); assertEquals("wrong batch count", 200, accumulator.batchCount); } @@ -180,5 +175,4 @@ public class AbstractAccumulatorTest { return ready; } } - } diff --git a/framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimer.java b/framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimer.java new file mode 100644 index 00000000..4116cbef --- /dev/null +++ b/framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimer.java @@ -0,0 +1,504 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onlab.util; + +import com.google.common.collect.Lists; +import org.onlab.junit.TestUtils; +import org.slf4j.Logger; + +import java.util.Date; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.TimerTask; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.onlab.junit.TestTools.delay; +import static org.slf4j.LoggerFactory.getLogger; + + +/** + * Provides manually scheduled timer utility. All schedulable methods are subject to overflow (you can set a period of + * max long). Additionally if a skip skips a period of time greater than one period for a periodic task that task will + * only be executed once for that skip and scheduled it's period after the last execution. + */ +public class ManuallyAdvancingTimer extends java.util.Timer { + + /* States whether or not the static values from timer task have been set ensures population will only occur once.*/ + private boolean staticsPopulated = false; + + /* Virgin value from timer task */ + private int virginState; + + /* Scheduled value from timer task */ + private int scheduledState; + + /* Executed value from timer task */ + private int executedState; + + /* Cancelled value from timer task */ + private int cancelledState; + + private final Logger logger = getLogger(getClass()); + + /* Service for executing timer tasks */ + private final ExecutorService executorService = Executors.newSingleThreadExecutor(); + + /* Internal time representation independent of system time, manually advanced */ + private final TimerKeeper timerKeeper = new TimerKeeper(); + + /* Data structure for tracking tasks */ + private final TaskQueue queue = new TaskQueue(); + + @Override + public void schedule(TimerTask task, long delay) { + if (!staticsPopulated) { + populateStatics(task); + } + if (!submitTask(task, delay > 0 ? timerKeeper.currentTimeInMillis() + delay : + timerKeeper.currentTimeInMillis() - delay, 0)) { + logger.error("Failed to submit task"); + } + } + + @Override + public void schedule(TimerTask task, Date time) { + if (!staticsPopulated) { + populateStatics(task); + } + if (!submitTask(task, time.getTime(), 0)) { + logger.error("Failed to submit task"); + } + } + + @Override + public void schedule(TimerTask task, long delay, long period) { + if (!staticsPopulated) { + populateStatics(task); + } + if (!submitTask(task, delay > 0 ? timerKeeper.currentTimeInMillis() + delay : + timerKeeper.currentTimeInMillis() - delay, period)) { + logger.error("Failed to submit task"); + } + } + + @Override + public void schedule(TimerTask task, Date firstTime, long period) { + if (!staticsPopulated) { + populateStatics(task); + } + if (!submitTask(task, firstTime.getTime(), period)) { + logger.error("Failed to submit task"); + } + } + + /*################################################WARNING################################################*/ + /* Schedule at fixed rate methods do not work exactly as in the java timer. They are clones of the periodic + *scheduling methods. */ + @Override + public void scheduleAtFixedRate(TimerTask task, long delay, long period) { + if (!staticsPopulated) { + populateStatics(task); + } + if (!submitTask(task, delay > 0 ? timerKeeper.currentTimeInMillis() + delay : + timerKeeper.currentTimeInMillis() - delay, period)) { + logger.error("Failed to submit task"); + } + } + + @Override + public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) { + if (!staticsPopulated) { + populateStatics(task); + } + if (!submitTask(task, firstTime.getTime(), period)) { + logger.error("Failed to submit task"); + } + } + + @Override + public void cancel() { + executorService.shutdown(); + queue.clear(); + } + + @Override + public int purge() { + return queue.removeCancelled(); + } + + /** + * Returns the virtual current time in millis. + * + * @return long representing simulated current time. + */ + public long currentTimeInMillis() { + return timerKeeper.currentTimeInMillis(); + } + + /** + * Returns the new simulated current time in millis after advancing the absolute value of millis to advance. + * Triggers event execution of all events scheduled for execution at times up to and including the returned time. + * Passing in the number zero has no effect. + * + * @param millisToAdvance the number of millis to advance. + * @return a long representing the current simulated time in millis + */ + public long advanceTimeMillis(long millisToAdvance) { + return timerKeeper.advanceTimeMillis(millisToAdvance); + } + + /** + * Advances the virtual time a certain number of millis triggers execution delays a certain amount to + * allow time for execution. + * + * @param virtualTimeAdvance the time to be advances in millis of simulated time. + * @param realTimeDelay the time to delay in real time to allow for processing. + */ + public void advanceTimeMillis(long virtualTimeAdvance, int realTimeDelay) { + timerKeeper.advanceTimeMillis(virtualTimeAdvance); + delay(realTimeDelay); + } + + /** + * Sets up the task and submits it to the queue. + * + * @param task the task to be added to the queue + * @param runtime the first runtime of the task + * @param period the period between runs thereafter + * @return returns true if the task was successfully submitted, false otherwise + */ + private boolean submitTask(TimerTask task, long runtime, long period) { + checkNotNull(task); + try { + TestUtils.setField(task, "state", scheduledState); + TestUtils.setField(task, "nextExecutionTime", runtime); + TestUtils.setField(task, "period", period); + } catch (TestUtils.TestUtilsException e) { + e.printStackTrace(); + return false; + } + queue.insertOrdered(task); + return true; + } + + /** + * Executes the given task (only if it is in the scheduled state) and proceeds to reschedule it or mark it as + * executed. Does not remove from the queue (this must be done outside). + * + * @param task the timer task to be executed + */ + private boolean executeTask(TimerTask task) { + checkNotNull(task); + int currentState; + try { + currentState = TestUtils.getField(task, "state"); + } catch (TestUtils.TestUtilsException e) { + logger.error("Could not get state of task."); + e.printStackTrace(); + return false; + } + //If cancelled or already executed stop here. + if (currentState == executedState || currentState == cancelledState) { + return false; + } else if (currentState == virginState) { + logger.error("Task was set for execution without being scheduled."); + return false; + } else if (currentState == scheduledState) { + long period; + + try { + period = TestUtils.getField(task, "period"); + } catch (TestUtils.TestUtilsException e) { + logger.error("Could not read period of task."); + e.printStackTrace(); + return false; + } + //Period of zero means one time execution. + if (period == 0) { + try { + TestUtils.setField(task, "state", executedState); + } catch (TestUtils.TestUtilsException e) { + logger.error("Could not set executed state."); + e.printStackTrace(); + return false; + } + executorService.execute(task); + return true; + } else { + //Calculate next execution time, using absolute value of period + long nextTime = (period > 0) ? (timerKeeper.currentTimeInMillis() + period) : + (timerKeeper.currentTimeInMillis() - period); + try { + TestUtils.setField(task, "nextExecutionTime", nextTime); + } catch (TestUtils.TestUtilsException e) { + logger.error("Could not set next execution time."); + e.printStackTrace(); + return false; + } + //Schedule next execution + queue.insertOrdered(task); + executorService.execute(task); + return true; + } + } + logger.error("State property of {} is in an illegal state and did not execute.", task); + return false; + } + + /** + * Executes all tasks in the queue scheduled for execution up to and including the current time. + * + * @return the total number of tasks run, -1 if failure + */ + private int executeEventsUpToPresent() { + int totalRun = 0; + if (queue.isEmpty()) { + return -1; + } + TimerTask currTask = queue.peek(); + long currExecTime; + try { + currExecTime = TestUtils.getField(currTask, "nextExecutionTime"); + } catch (TestUtils.TestUtilsException e) { + e.printStackTrace(); + throw new RuntimeException("Could not get nextExecutionTime"); + } + while (currExecTime <= timerKeeper.currentTimeInMillis()) { + if (executeTask(queue.pop())) { + totalRun++; + } + if (queue.isEmpty()) { + break; + } + currTask = queue.peek(); + try { + currExecTime = TestUtils.getField(currTask, "nextExecutionTime"); + } catch (TestUtils.TestUtilsException e) { + e.printStackTrace(); + throw new RuntimeException("Could not get nextExecutionTime"); + } + } + return totalRun; + } + + /** + * Populates the static fields from timer task. Should only be called once. + */ + private void populateStatics(TimerTask task) { + try { + virginState = TestUtils.getField(task, "VIRGIN"); + scheduledState = TestUtils.getField(task, "SCHEDULED"); + executedState = TestUtils.getField(task, "EXECUTED"); + cancelledState = TestUtils.getField(task, "CANCELLED"); + staticsPopulated = true; + } catch (TestUtils.TestUtilsException e) { + e.printStackTrace(); + } + } + + /** + * A class used to maintain the virtual time. + */ + private class TimerKeeper { + + private long currentTime = 0; + + /** + * Returns the virtual current time in millis. + * + * @return long representing simulated current time. + */ + long currentTimeInMillis() { + return currentTime; + } + + /** + * Returns the new simulated current time in millis after advancing the absolute value of millis to advance. + * Triggers event execution of all events scheduled for execution at times up to and including the returned + * time. Passing in the number zero has no effect. + * + * @param millisToAdvance the number of millis to advance. + * @return a long representing the current simulated time in millis + */ + long advanceTimeMillis(long millisToAdvance) { + currentTime = (millisToAdvance >= 0) ? (currentTime + millisToAdvance) : (currentTime - millisToAdvance); + if (millisToAdvance != 0) { + executeEventsUpToPresent(); + } + return currentTime; + } + } + + /** + * A queue backed by a linked list. Keeps elements sorted in ascending order of execution time. All calls are safe + * even on empty queue's. + */ + private class TaskQueue { + private final LinkedList<TimerTask> taskList = Lists.newLinkedList(); + + /** + * Adds the task to the queue in ascending order of scheduled execution. If execution time has already passed + * execute immediately. + * + * @param task the task to be added to the queue + */ + void insertOrdered(TimerTask task) { + //Using O(N) insertion because random access is expensive in linked lists worst case is 2N links followed + // for binary insertion vs N for simple insertion. + checkNotNull(task); + if (!staticsPopulated) { + populateStatics(task); + } + long insertTime; + try { + insertTime = TestUtils.getField(task, "nextExecutionTime"); + TestUtils.setField(task, "state", scheduledState); + } catch (TestUtils.TestUtilsException e) { + e.printStackTrace(); + return; + } + //If the task was scheduled in the past or for the current time run it immediately and do not add to the + // queue, subsequent executions will be scheduled as normal + if (insertTime <= timerKeeper.currentTimeInMillis()) { + executeTask(task); + return; + } + + Iterator<TimerTask> iter = taskList.iterator(); + int positionCounter = 0; + long nextTaskTime; + TimerTask currentTask; + while (iter.hasNext()) { + currentTask = iter.next(); + try { + nextTaskTime = TestUtils.getField(currentTask, "nextExecutionTime"); + } catch (TestUtils.TestUtilsException e) { + e.printStackTrace(); + return; + } + if (insertTime < nextTaskTime) { + taskList.add(positionCounter, task); + return; + } + positionCounter++; + } + taskList.addLast(task); + } + + /** + * Returns the first item in the queue (next scheduled for execution) without removing it, returns null if the + * queue is empty. + * + * @return the next TimerTask to run or null if the queue is empty + */ + TimerTask peek() { + if (taskList.isEmpty()) { + return null; + } + return taskList.getFirst(); + } + + /** + * Returns and removes the first item in the queue or null if it is empty. + * + * @return the first element of the queue or null if the queue is empty + */ + TimerTask pop() { + if (taskList.isEmpty()) { + return null; + } + return taskList.pop(); + } + + /** + * Performs a sort on the set of timer tasks, earliest task is first. Does nothing if queue is empty. + */ + void sort() { + if (taskList.isEmpty()) { + return; + } + taskList.sort((o1, o2) -> { + checkNotNull(o1); + checkNotNull(o2); + long executionTimeOne; + long executionTimeTwo; + try { + executionTimeOne = TestUtils.getField(o1, "nextExecutionTime"); + executionTimeTwo = TestUtils.getField(o2, "nextExecutionTime"); + } catch (TestUtils.TestUtilsException e) { + e.printStackTrace(); + throw new RuntimeException("Could not get next execution time."); + } + if (executionTimeOne == executionTimeTwo) { + return 0; + } else if (executionTimeOne < executionTimeTwo) { + return -1; + } else { + return 1; + } + }); + } + + /** + * Returns whether the queue is currently empty. + * + * @return true if the queue is empty, false otherwise + */ + boolean isEmpty() { + return taskList.isEmpty(); + } + + /** + * Clears the underlying list of the queue. + */ + void clear() { + taskList.clear(); + } + + /** + * Removes all cancelled tasks from the queue. Has no effect on behavior. + * + * @return returns the total number of items removed, -1 if list is empty or failure occurs. + */ + int removeCancelled() { + if (taskList.isEmpty()) { + return -1; + } + int removedCount = 0; + Iterator<TimerTask> taskIterator = taskList.iterator(); + TimerTask currTask; + int currState; + while (taskIterator.hasNext()) { + currTask = taskIterator.next(); + try { + currState = TestUtils.getField(currTask, "state"); + } catch (TestUtils.TestUtilsException e) { + logger.error("Could not get task state."); + e.printStackTrace(); + return -1; + } + if (currState == cancelledState) { + removedCount++; + taskIterator.remove(); + } + } + return removedCount; + } + } +} diff --git a/framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimerTest.java b/framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimerTest.java new file mode 100644 index 00000000..b8e1e85e --- /dev/null +++ b/framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimerTest.java @@ -0,0 +1,263 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onlab.util; + +import com.google.common.collect.Lists; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Date; +import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.onlab.junit.TestTools.delay; + +/** + * Testing class for manually advancing timer. + */ +public class ManuallyAdvancingTimerTest { + + private ManuallyAdvancingTimer timer; + + /* Generates unique id's for TestTasks */ + private AtomicInteger idGenerator; + + /* Tracks TestTasks in order of creation, tasks are automatically added at creation. */ + private ArrayList<TestTask> taskList; + + /* Total number of tasks run */ + private AtomicInteger tasksRunCount; + + // FIXME if this class fails first try increasing the real time delay to account for heavy system load. + private static final int REAL_TIME_DELAY = 1; + + /** + * Sets up the testing environment. + */ + @Before + public void setup() { + timer = new ManuallyAdvancingTimer(); + idGenerator = new AtomicInteger(1); + tasksRunCount = new AtomicInteger(0); + taskList = Lists.newArrayList(); + } + + /** + * Tests the one time schedule with delay. + * + * @throws Exception throws an exception if the test fails + */ + @Test + public void testScheduleByDelay() throws Exception { + /* Test scheduling in the future as normal. */ + timer.schedule(new TestTask(), 10); + timer.advanceTimeMillis(5); + assertFalse(taskList.get(0).hasRun()); + timer.advanceTimeMillis(10, REAL_TIME_DELAY); + assertTrue(taskList.get(0).hasRun()); + + /* Test scheduling with negative numbers */ + timer.schedule(new TestTask(), -10); + timer.advanceTimeMillis(5); + assertFalse(taskList.get(1).hasRun()); + timer.advanceTimeMillis(10, REAL_TIME_DELAY); + assertTrue(taskList.get(1).hasRun()); + + /* Reset list, counter and timer for next test */ + taskList.clear(); + idGenerator.set(1); + tasksRunCount.set(0); + + for (int i = 0; i < 50; i++) { + timer.schedule(new TestTask(), i); + } + /* Test that a task scheduled for present is run and not placed in the queue */ + assertEquals("Only the first task should have run.", 1, tasksRunCount.get()); + + for (int i = 2; i <= 50; i++) { + timer.advanceTimeMillis(1, REAL_TIME_DELAY); + assertEquals("One task should be executed per loop", i, tasksRunCount.get()); + } + /* Below tests ordered insertion, this will only be done once, it is the same for all schedule methods. */ + + tasksRunCount.set(0); + + for (int i = 0; i < 10; i++) { + timer.schedule(new TestTask(), 500); + } + + assertEquals("No new tasks should have been run since run count reset.", 0, tasksRunCount.get()); + timer.schedule(new TestTask(), 10); + assertEquals("No new tasks should have been run since run count reset.", 0, tasksRunCount.get()); + timer.advanceTimeMillis(10, REAL_TIME_DELAY); + assertEquals("One new tasks should have been run since run count reset.", 1, tasksRunCount.get()); + timer.advanceTimeMillis(510, REAL_TIME_DELAY); + assertEquals("Eleven new tasks should have been run since run count reset.", 11, tasksRunCount.get()); + } + + /** + * Tests scheduling for a particular date or time which may be in the past. + * + * @throws Exception throws an exception if the test fails + */ + @Test + public void testScheduleByDate() throws Exception { + /* Tests basic scheduling for future times. */ + timer.schedule(new TestTask(), new Date(10)); + timer.advanceTimeMillis(5); + assertFalse(taskList.get(0).hasRun()); + timer.advanceTimeMillis(10, REAL_TIME_DELAY); + assertTrue(taskList.get(0).hasRun()); + + /* Test scheduling with past times numbers */ + timer.schedule(new TestTask(), new Date(0)); + delay(REAL_TIME_DELAY); + assertTrue(taskList.get(1).hasRun()); + + /* Tests cancellation on non-periodic events */ + TestTask task = new TestTask(); + timer.schedule(task, new Date(timer.currentTimeInMillis() + 10)); + task.cancel(); + timer.advanceTimeMillis(12, REAL_TIME_DELAY); + assertFalse(task.hasRun()); + + } + + /** + * Test scheduling beginning after a delay and recurring periodically. + * + * @throws Exception throws an exception if the test fails + */ + @Test + public void testScheduleByDelayPeriodic() throws Exception { + /* Test straightforward periodic execution */ + timer.schedule(new TestTask(), 0, 10); + delay(REAL_TIME_DELAY); + assertEquals("Task should have run once when added.", 1, taskList.get(0).timesRun()); + + /* Tests whether things that are not added to the queue are scheduled for future executions (ones which execute + immediately on add). */ + timer.advanceTimeMillis(10, REAL_TIME_DELAY); + assertEquals("Task should have run once when added.", 2, taskList.get(0).timesRun()); + + /* Tests whether cancellation works on periodic events. */ + taskList.get(0).cancel(); + + timer.advanceTimeMillis(10, REAL_TIME_DELAY); + assertEquals("The task should not have run another time.", 2, taskList.get(0).timesRun()); + + TestTask task = new TestTask(); + timer.schedule(task, 0, 10); + timer.advanceTimeMillis(100, REAL_TIME_DELAY); + assertEquals("Should have run immeditaley and subsequently once during the larger skip", task.timesRun(), 2); + + } + + /** + * Test scheduling beginning at a specified date and recurring periodically. + * + * @throws Exception throws an exception if the test fails + */ + @Test + public void testScheduleByDatePeriodic() throws Exception { + /* Test straightforward periodic execution */ + timer.schedule(new TestTask(), new Date(timer.currentTimeInMillis()), 10); + delay(REAL_TIME_DELAY); + assertEquals("Task should have run once when added.", 1, taskList.get(0).timesRun()); + + /* Tests whether things that are not added to the queue are scheduled for future executions (ones which execute + immediately on add). */ + timer.advanceTimeMillis(10, REAL_TIME_DELAY); + assertEquals("Task should have run once when added.", 2, taskList.get(0).timesRun()); + + /* Tests whether cancellation works on periodic events. */ + taskList.get(0).cancel(); + + timer.advanceTimeMillis(10, REAL_TIME_DELAY); + assertEquals("The task should not have run another time.", 2, taskList.get(0).timesRun()); + + TestTask task = new TestTask(); + timer.schedule(task, new Date(timer.currentTimeInMillis()), 10); + timer.advanceTimeMillis(100, REAL_TIME_DELAY); + assertEquals("Should have run immediately and subsequently once during the larger skip", task.timesRun(), 2); + } + + /* Schedule at fixed rate runs exactly like the two scheduling methods just tested so tests are not included */ + + /** + * Timer task with added functions to make it better for testing. + */ + private class TestTask extends TimerTask { + + /* Remains true once the task has been run at least once */ + private boolean hasRun; + + /* Unique id per event. */ + private int id; + + /* Specifies the number of times an event has run */ + private int timesRun; + + /** + * Constructor initializes id, timesRun, and id fields. + */ + public TestTask() { + id = idGenerator.getAndIncrement(); + timesRun = 0; + hasRun = false; + taskList.add(this); + } + + @Override + public void run() { + this.hasRun = true; + tasksRunCount.incrementAndGet(); + timesRun++; + } + + /** + * Returns whether this event has run. + * + * @return true if the event has run, false otherwise. + */ + public boolean hasRun() { + return hasRun; + } + + /** + * Returns the number of times this task has run. + * + * @return an int representing the number of times this task has been run + */ + public int timesRun() { + return timesRun; + } + + /** + * Returns the unique identifier of this task. + * + * @return a unique integer identifier + */ + public int getId() { + return id; + } + } +}
\ No newline at end of file diff --git a/framework/src/onos/utils/osgi/src/test/java/org/onlab/osgi/ComponentContextAdapter.java b/framework/src/onos/utils/osgi/src/test/java/org/onlab/osgi/ComponentContextAdapter.java index 0278ccd6..b8718c3c 100644 --- a/framework/src/onos/utils/osgi/src/test/java/org/onlab/osgi/ComponentContextAdapter.java +++ b/framework/src/onos/utils/osgi/src/test/java/org/onlab/osgi/ComponentContextAdapter.java @@ -22,14 +22,53 @@ import org.osgi.service.component.ComponentContext; import org.osgi.service.component.ComponentInstance; import java.util.Dictionary; +import java.util.Enumeration; /** * Adapter implementation of OSGI component context. */ public class ComponentContextAdapter implements ComponentContext { + private static class MockDictionary extends Dictionary { + + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public Enumeration keys() { + return null; + } + + @Override + public Enumeration elements() { + return null; + } + + @Override + public Object get(Object key) { + return null; + } + + @Override + public Object put(Object key, Object value) { + return null; + } + + @Override + public Object remove(Object key) { + return null; + } + } + @Override public Dictionary getProperties() { - return null; + return new MockDictionary(); } @Override diff --git a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java index 325e191b..0e88e34e 100644 --- a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java +++ b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/FlowsWebResource.java @@ -66,9 +66,9 @@ public class FlowsWebResource extends AbstractWebResource { public Response getFlows() { final Iterable<Device> devices = get(DeviceService.class).getDevices(); for (final Device device : devices) { - final Iterable<FlowEntry> deviceEntries = service.getFlowEntries(device.id()); - if (deviceEntries != null) { - for (final FlowEntry entry : deviceEntries) { + final Iterable<FlowEntry> flowEntries = service.getFlowEntries(device.id()); + if (flowEntries != null) { + for (final FlowEntry entry : flowEntries) { flowsNode.add(codec(FlowEntry.class).encode(entry, this)); } } @@ -88,13 +88,13 @@ public class FlowsWebResource extends AbstractWebResource { @Produces(MediaType.APPLICATION_JSON) @Path("{deviceId}") public Response getFlowByDeviceId(@PathParam("deviceId") String deviceId) { - final Iterable<FlowEntry> deviceEntries = + final Iterable<FlowEntry> flowEntries = service.getFlowEntries(DeviceId.deviceId(deviceId)); - if (!deviceEntries.iterator().hasNext()) { + if (!flowEntries.iterator().hasNext()) { throw new ItemNotFoundException(DEVICE_NOT_FOUND); } - for (final FlowEntry entry : deviceEntries) { + for (final FlowEntry entry : flowEntries) { flowsNode.add(codec(FlowEntry.class).encode(entry, this)); } return ok(root).build(); @@ -113,13 +113,13 @@ public class FlowsWebResource extends AbstractWebResource { @Path("{deviceId}/{flowId}") public Response getFlowByDeviceIdAndFlowId(@PathParam("deviceId") String deviceId, @PathParam("flowId") long flowId) { - final Iterable<FlowEntry> deviceEntries = + final Iterable<FlowEntry> flowEntries = service.getFlowEntries(DeviceId.deviceId(deviceId)); - if (!deviceEntries.iterator().hasNext()) { + if (!flowEntries.iterator().hasNext()) { throw new ItemNotFoundException(DEVICE_NOT_FOUND); } - for (final FlowEntry entry : deviceEntries) { + for (final FlowEntry entry : flowEntries) { if (entry.id().value() == flowId) { flowsNode.add(codec(FlowEntry.class).encode(entry, this)); } @@ -175,14 +175,14 @@ public class FlowsWebResource extends AbstractWebResource { @Path("{deviceId}/{flowId}") public void deleteFlowByDeviceIdAndFlowId(@PathParam("deviceId") String deviceId, @PathParam("flowId") long flowId) { - final Iterable<FlowEntry> deviceEntries = + final Iterable<FlowEntry> flowEntries = service.getFlowEntries(DeviceId.deviceId(deviceId)); - if (!deviceEntries.iterator().hasNext()) { + if (!flowEntries.iterator().hasNext()) { throw new ItemNotFoundException(DEVICE_NOT_FOUND); } - StreamSupport.stream(deviceEntries.spliterator(), false) + StreamSupport.stream(flowEntries.spliterator(), false) .filter(entry -> entry.id().value() == flowId) .forEach(service::removeFlowRules); } diff --git a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java index 9e2b6273..808fcc16 100644 --- a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java +++ b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/NetworkConfigWebResource.java @@ -49,73 +49,77 @@ public class NetworkConfigWebResource extends AbstractWebResource { public Response download() { NetworkConfigService service = get(NetworkConfigService.class); ObjectNode root = mapper().createObjectNode(); - service.getSubjectClasses().forEach(sc -> - produceJson(service, newObject(root, service.getSubjectFactory(sc).subjectKey()), sc)); + service.getSubjectClasses().forEach(sc -> { + SubjectFactory subjectFactory = service.getSubjectFactory(sc); + produceJson(service, newObject(root, subjectFactory.subjectClassKey()), + subjectFactory, sc); + }); return ok(root).build(); } /** * Get all network configuration for a subject class. * - * @param subjectKey subject class key + * @param subjectClassKey subject class key * @return network configuration JSON */ @GET - @Path("{subjectKey}") + @Path("{subjectClassKey}") @Produces(MediaType.APPLICATION_JSON) @SuppressWarnings("unchecked") - public Response download(@PathParam("subjectKey") String subjectKey) { + public Response download(@PathParam("subjectClassKey") String subjectClassKey) { NetworkConfigService service = get(NetworkConfigService.class); ObjectNode root = mapper().createObjectNode(); - produceJson(service, root, service.getSubjectFactory(subjectKey).subjectClass()); + SubjectFactory subjectFactory = service.getSubjectFactory(subjectClassKey); + produceJson(service, root, subjectFactory, subjectFactory.subjectClass()); return ok(root).build(); } /** - * Get all network configuration for a subject. + * Get all network configuration for a subjectKey. * - * @param subjectKey subject class key - * @param subject subject key + * @param subjectClassKey subjectKey class key + * @param subjectKey subjectKey key * @return network configuration JSON */ @GET - @Path("{subjectKey}/{subject}") + @Path("{subjectClassKey}/{subjectKey}") @Produces(MediaType.APPLICATION_JSON) @SuppressWarnings("unchecked") - public Response download(@PathParam("subjectKey") String subjectKey, - @PathParam("subject") String subject) { + public Response download(@PathParam("subjectClassKey") String subjectClassKey, + @PathParam("subjectKey") String subjectKey) { NetworkConfigService service = get(NetworkConfigService.class); ObjectNode root = mapper().createObjectNode(); - produceSubjectJson(service, root, - service.getSubjectFactory(subjectKey).createSubject(subject)); + SubjectFactory subjectFactory = service.getSubjectFactory(subjectClassKey); + produceSubjectJson(service, root, subjectFactory.createSubject(subjectKey)); return ok(root).build(); } /** - * Get specific network configuration for a subject. + * Get specific network configuration for a subjectKey. * - * @param subjectKey subject class key - * @param subject subject key - * @param configKey configuration class key + * @param subjectClassKey subjectKey class key + * @param subjectKey subjectKey key + * @param configKey configuration class key * @return network configuration JSON */ @GET - @Path("{subjectKey}/{subject}/{configKey}") + @Path("{subjectClassKey}/{subjectKey}/{configKey}") @Produces(MediaType.APPLICATION_JSON) @SuppressWarnings("unchecked") - public Response download(@PathParam("subjectKey") String subjectKey, - @PathParam("subject") String subject, + public Response download(@PathParam("subjectClassKey") String subjectClassKey, + @PathParam("subjectKey") String subjectKey, @PathParam("configKey") String configKey) { NetworkConfigService service = get(NetworkConfigService.class); - return ok(service.getConfig(service.getSubjectFactory(subjectKey).createSubject(subject), - service.getConfigClass(subjectKey, configKey)).node()).build(); + return ok(service.getConfig(service.getSubjectFactory(subjectClassKey).createSubject(subjectKey), + service.getConfigClass(subjectClassKey, configKey)).node()).build(); } @SuppressWarnings("unchecked") private void produceJson(NetworkConfigService service, ObjectNode node, - Class subjectClass) { + SubjectFactory subjectFactory, Class subjectClass) { service.getSubjects(subjectClass).forEach(s -> - produceSubjectJson(service, newObject(node, s.toString()), s)); + produceSubjectJson(service, newObject(node, subjectFactory.subjectKey(s)), s)); } private void produceSubjectJson(NetworkConfigService service, ObjectNode node, @@ -128,8 +132,8 @@ public class NetworkConfigWebResource extends AbstractWebResource { * Upload bulk network configuration. * * @param request network configuration JSON rooted at the top node - * @throws IOException if unable to parse the request * @return empty response + * @throws IOException if unable to parse the request */ @POST @Consumes(MediaType.APPLICATION_JSON) @@ -146,78 +150,78 @@ public class NetworkConfigWebResource extends AbstractWebResource { /** * Upload multiple network configurations for a subject class. * - * @param subjectKey subject class key - * @param request network configuration JSON rooted at the top node + * @param subjectClassKey subject class key + * @param request network configuration JSON rooted at the top node * @return empty response * @throws IOException if unable to parse the request */ @POST - @Path("{subjectKey}") + @Path("{subjectClassKey}") @Consumes(MediaType.APPLICATION_JSON) @SuppressWarnings("unchecked") - public Response upload(@PathParam("subjectKey") String subjectKey, + public Response upload(@PathParam("subjectClassKey") String subjectClassKey, InputStream request) throws IOException { NetworkConfigService service = get(NetworkConfigService.class); ObjectNode root = (ObjectNode) mapper().readTree(request); - consumeJson(service, root, service.getSubjectFactory(subjectKey)); + consumeJson(service, root, service.getSubjectFactory(subjectClassKey)); return Response.ok().build(); } /** - * Upload mutliple network configurations for a subject. + * Upload mutliple network configurations for a subjectKey. * - * @param subjectKey subject class key - * @param subject subject key - * @param request network configuration JSON rooted at the top node + * @param subjectClassKey subjectKey class key + * @param subjectKey subjectKey key + * @param request network configuration JSON rooted at the top node * @return empty response * @throws IOException if unable to parse the request */ @POST - @Path("{subjectKey}/{subject}") + @Path("{subjectClassKey}/{subjectKey}") @Consumes(MediaType.APPLICATION_JSON) @SuppressWarnings("unchecked") - public Response upload(@PathParam("subjectKey") String subjectKey, - @PathParam("subject") String subject, + public Response upload(@PathParam("subjectClassKey") String subjectClassKey, + @PathParam("subjectKey") String subjectKey, InputStream request) throws IOException { NetworkConfigService service = get(NetworkConfigService.class); ObjectNode root = (ObjectNode) mapper().readTree(request); consumeSubjectJson(service, root, - service.getSubjectFactory(subjectKey).createSubject(subject), - subjectKey); + service.getSubjectFactory(subjectClassKey).createSubject(subjectKey), + subjectClassKey); return Response.ok().build(); } /** - * Upload specific network configuration for a subject. + * Upload specific network configuration for a subjectKey. * - * @param subjectKey subject class key - * @param subject subject key - * @param configKey configuration class key - * @param request network configuration JSON rooted at the top node + * @param subjectClassKey subjectKey class key + * @param subjectKey subjectKey key + * @param configKey configuration class key + * @param request network configuration JSON rooted at the top node * @return empty response * @throws IOException if unable to parse the request */ @POST - @Path("{subjectKey}/{subject}/{configKey}") + @Path("{subjectClassKey}/{subjectKey}/{configKey}") @Consumes(MediaType.APPLICATION_JSON) @SuppressWarnings("unchecked") - public Response upload(@PathParam("subjectKey") String subjectKey, - @PathParam("subject") String subject, + public Response upload(@PathParam("subjectClassKey") String subjectClassKey, + @PathParam("subjectKey") String subjectKey, @PathParam("configKey") String configKey, InputStream request) throws IOException { NetworkConfigService service = get(NetworkConfigService.class); ObjectNode root = (ObjectNode) mapper().readTree(request); - service.applyConfig(service.getSubjectFactory(subjectKey).createSubject(subject), - service.getConfigClass(subjectKey, configKey), root); + service.applyConfig(service.getSubjectFactory(subjectClassKey).createSubject(subjectKey), + service.getConfigClass(subjectClassKey, configKey), root); return Response.ok().build(); } private void consumeJson(NetworkConfigService service, ObjectNode classNode, SubjectFactory subjectFactory) { classNode.fieldNames().forEachRemaining(s -> - consumeSubjectJson(service, (ObjectNode) classNode.path(s), - subjectFactory.createSubject(s), - subjectFactory.subjectKey())); + consumeSubjectJson(service, (ObjectNode) classNode.path(s), + subjectFactory.createSubject(s), + subjectFactory.subjectClassKey())); } private void consumeSubjectJson(NetworkConfigService service, @@ -225,7 +229,7 @@ public class NetworkConfigWebResource extends AbstractWebResource { String subjectKey) { subjectNode.fieldNames().forEachRemaining(c -> service.applyConfig(subject, service.getConfigClass(subjectKey, c), - (ObjectNode) subjectNode.path(c))); + subjectNode.path(c))); } @@ -241,64 +245,62 @@ public class NetworkConfigWebResource extends AbstractWebResource { service.getSubjectClasses() .forEach(subjectClass -> service.getSubjects(subjectClass) .forEach(subject -> service.getConfigs(subject) - .forEach(config -> service - .removeConfig(subject, config.getClass())))); + .forEach(config -> service.removeConfig(subject, config.getClass())))); return Response.ok().build(); } /** * Clear all network configurations for a subject class. * - * @param subjectKey subject class key + * @param subjectClassKey subject class key * @return empty response */ @DELETE - @Path("{subjectKey}") + @Path("{subjectClassKey}") @SuppressWarnings("unchecked") - public Response delete(@PathParam("subjectKey") String subjectKey) { + public Response delete(@PathParam("subjectClassKey") String subjectClassKey) { NetworkConfigService service = get(NetworkConfigService.class); - service.getSubjects(service.getSubjectFactory(subjectKey).getClass()) + service.getSubjects(service.getSubjectFactory(subjectClassKey).getClass()) .forEach(subject -> service.getConfigs(subject) - .forEach(config -> service - .removeConfig(subject, config.getClass()))); + .forEach(config -> service.removeConfig(subject, config.getClass()))); return Response.ok().build(); } /** - * Clear all network configurations for a subject. + * Clear all network configurations for a subjectKey. * - * @param subjectKey subject class key - * @param subject subject key + * @param subjectClassKey subjectKey class key + * @param subjectKey subjectKey key * @return empty response */ @DELETE - @Path("{subjectKey}/{subject}") + @Path("{subjectClassKey}/{subjectKey}") @SuppressWarnings("unchecked") - public Response delete(@PathParam("subjectKey") String subjectKey, - @PathParam("subject") String subject) { + public Response delete(@PathParam("subjectClassKey") String subjectClassKey, + @PathParam("subjectKey") String subjectKey) { NetworkConfigService service = get(NetworkConfigService.class); - Object s = service.getSubjectFactory(subjectKey).createSubject(subject); + Object s = service.getSubjectFactory(subjectClassKey).createSubject(subjectKey); service.getConfigs(s).forEach(c -> service.removeConfig(s, c.getClass())); return Response.ok().build(); } /** - * Clear specific network configuration for a subject. + * Clear specific network configuration for a subjectKey. * - * @param subjectKey subject class key - * @param subject subject key - * @param configKey configuration class key + * @param subjectClassKey subjectKey class key + * @param subjectKey subjectKey key + * @param configKey configuration class key * @return empty response */ @DELETE - @Path("{subjectKey}/{subject}/{configKey}") + @Path("{subjectClassKey}/{subjectKey}/{configKey}") @SuppressWarnings("unchecked") - public Response delete(@PathParam("subjectKey") String subjectKey, - @PathParam("subject") String subject, + public Response delete(@PathParam("subjectClassKey") String subjectClassKey, + @PathParam("subjectKey") String subjectKey, @PathParam("configKey") String configKey) { NetworkConfigService service = get(NetworkConfigService.class); - service.removeConfig(service.getSubjectFactory(subjectKey).createSubject(subject), - service.getConfigClass(subjectKey, configKey)); + service.removeConfig(service.getSubjectFactory(subjectClassKey).createSubject(subjectKey), + service.getConfigClass(subjectClassKey, configKey)); return Response.ok().build(); } diff --git a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/PathsWebResource.java b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/PathsWebResource.java index baa1b1e6..9714690c 100644 --- a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/PathsWebResource.java +++ b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/PathsWebResource.java @@ -15,7 +15,11 @@ */ package org.onosproject.rest.resources; -import java.util.Set; +import org.onosproject.net.DeviceId; +import org.onosproject.net.ElementId; +import org.onosproject.net.HostId; +import org.onosproject.net.topology.PathService; +import org.onosproject.rest.AbstractWebResource; import javax.ws.rs.GET; import javax.ws.rs.Path; @@ -23,14 +27,7 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; - -import org.onosproject.net.DeviceId; -import org.onosproject.net.ElementId; -import org.onosproject.net.HostId; -import org.onosproject.net.topology.PathService; - -import com.fasterxml.jackson.databind.node.ObjectNode; -import org.onosproject.rest.AbstractWebResource; +import java.util.Set; /** * Compute paths in the network graph. @@ -50,6 +47,17 @@ public class PathsWebResource extends AbstractWebResource { } /** + * Returns either host id or device id, depending on the ID format. + * + * @param id host or device id string + * @return element id + */ + private ElementId elementId(String id) { + ElementId elementId = isHostId(id); + return elementId != null ? elementId : DeviceId.deviceId(id); + } + + /** * Get all shortest paths between any two hosts or devices. * Returns array of all shortest paths between any two elements. * @@ -63,23 +71,27 @@ public class PathsWebResource extends AbstractWebResource { public Response getPath(@PathParam("src") String src, @PathParam("dst") String dst) { PathService pathService = get(PathService.class); - - ElementId srcElement = isHostId(src); - ElementId dstElement = isHostId(dst); - - if (srcElement == null) { - // Doesn't look like a host, assume it is a device - srcElement = DeviceId.deviceId(src); - } - - if (dstElement == null) { - // Doesn't look like a host, assume it is a device - dstElement = DeviceId.deviceId(dst); - } - - Set<org.onosproject.net.Path> paths = pathService.getPaths(srcElement, dstElement); - ObjectNode root = encodeArray(org.onosproject.net.Path.class, "paths", paths); - return ok(root).build(); + Set<org.onosproject.net.Path> paths = + pathService.getPaths(elementId(src), elementId(dst)); + return ok(encodeArray(org.onosproject.net.Path.class, "paths", paths)).build(); } + /** + * Get all shortest disjoint paths between any two hosts or devices. + * Returns array of all shortest disjoint paths between any two elements. + * + * @param src source identifier + * @param dst destination identifier + * @return path data + */ + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("{src}/{dst}/disjoint") + public Response getDisjointPath(@PathParam("src") String src, + @PathParam("dst") String dst) { + PathService pathService = get(PathService.class); + Set<org.onosproject.net.DisjointPath> paths = + pathService.getDisjointPaths(elementId(src), elementId(dst)); + return ok(encodeArray(org.onosproject.net.DisjointPath.class, "paths", paths)).build(); + } } diff --git a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java index 2ffa2295..c91cb6d0 100644 --- a/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java +++ b/framework/src/onos/web/api/src/main/java/org/onosproject/rest/resources/StatisticsWebResource.java @@ -21,6 +21,7 @@ import java.util.stream.StreamSupport; import javax.ws.rs.GET; import javax.ws.rs.Path; +import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; @@ -31,7 +32,12 @@ import javax.ws.rs.core.UriInfo; import org.onosproject.codec.JsonCodec; import org.onosproject.net.ConnectPoint; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; import org.onosproject.net.Link; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.flow.FlowRuleService; +import org.onosproject.net.flow.TableStatisticsEntry; import org.onosproject.net.link.LinkService; import org.onosproject.net.statistic.Load; import org.onosproject.net.statistic.StatisticService; @@ -92,4 +98,59 @@ public class StatisticsWebResource extends AbstractWebResource { result.set("loads", loads); return ok(result).build(); } + + /** + * Get table statistics for all tables of all devices. + * + * @return JSON encoded array of table statistics + */ + @GET + @Path("flows/tables") + @Produces(MediaType.APPLICATION_JSON) + public Response getTableStatistics() { + final FlowRuleService service = get(FlowRuleService.class); + final Iterable<Device> devices = get(DeviceService.class).getDevices(); + final ObjectNode root = mapper().createObjectNode(); + final ArrayNode rootArrayNode = root.putArray("device-table-statistics"); + for (final Device device : devices) { + final ObjectNode deviceStatsNode = mapper().createObjectNode(); + deviceStatsNode.put("device", device.id().toString()); + final ArrayNode statisticsNode = deviceStatsNode.putArray("table-statistics"); + final Iterable<TableStatisticsEntry> tableStatsEntries = service.getFlowTableStatistics(device.id()); + if (tableStatsEntries != null) { + for (final TableStatisticsEntry entry : tableStatsEntries) { + statisticsNode.add(codec(TableStatisticsEntry.class).encode(entry, this)); + } + } + rootArrayNode.add(deviceStatsNode); + } + + return ok(root).build(); + } + + /** + * Get table statistics for all tables of a specified device. + * + * @param deviceId device ID + * @return JSON encoded array of table statistics + */ + @GET + @Path("flows/tables/{deviceId}") + @Produces(MediaType.APPLICATION_JSON) + public Response getTableStatisticsByDeviceId(@PathParam("deviceId") String deviceId) { + final FlowRuleService service = get(FlowRuleService.class); + final Iterable<TableStatisticsEntry> tableStatisticsEntries = + service.getFlowTableStatistics(DeviceId.deviceId(deviceId)); + final ObjectNode root = mapper().createObjectNode(); + final ArrayNode rootArrayNode = root.putArray("table-statistics"); + + final ObjectNode deviceStatsNode = mapper().createObjectNode(); + deviceStatsNode.put("device", deviceId); + final ArrayNode statisticsNode = deviceStatsNode.putArray("table-statistics"); + for (final TableStatisticsEntry entry : tableStatisticsEntries) { + statisticsNode.add(codec(TableStatisticsEntry.class).encode(entry, this)); + } + rootArrayNode.add(deviceStatsNode); + return ok(root).build(); + } } diff --git a/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/DeviceViewMessageHandler.java b/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/DeviceViewMessageHandler.java index fb83cdd7..53b16a65 100644 --- a/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/DeviceViewMessageHandler.java +++ b/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/DeviceViewMessageHandler.java @@ -25,12 +25,16 @@ import org.onosproject.net.Device; import org.onosproject.net.DeviceId; import org.onosproject.net.Link; import org.onosproject.net.Port; +import org.onosproject.net.config.NetworkConfigService; +import org.onosproject.net.config.basics.BasicDeviceConfig; import org.onosproject.net.device.DeviceService; import org.onosproject.net.link.LinkService; import org.onosproject.ui.RequestHandler; import org.onosproject.ui.UiMessageHandler; import org.onosproject.ui.table.TableModel; import org.onosproject.ui.table.TableRequestHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collection; @@ -38,7 +42,10 @@ import java.util.Collections; import java.util.List; import java.util.Set; +import static com.google.common.base.Strings.emptyToNull; +import static com.google.common.base.Strings.isNullOrEmpty; import static org.apache.commons.lang.WordUtils.capitalizeFully; +import static org.onosproject.net.DeviceId.deviceId; /** * Message handler for device view related messages. @@ -53,6 +60,11 @@ public class DeviceViewMessageHandler extends UiMessageHandler { private static final String DEV_DETAILS_RESP = "deviceDetailsResponse"; private static final String DETAILS = "details"; + private static final String DEV_NAME_CHANGE_REQ = "deviceNameChangeRequest"; + private static final String DEV_NAME_CHANGE_RESP = "deviceNameChangeResponse"; + + private static final String ZERO_URI = "of:0000000000000000"; + private static final String ID = "id"; private static final String TYPE = "type"; private static final String AVAILABLE = "available"; @@ -72,25 +84,41 @@ public class DeviceViewMessageHandler extends UiMessageHandler { private static final String ENABLED = "enabled"; private static final String SPEED = "speed"; private static final String NAME = "name"; + private static final String WARN = "warn"; private static final String[] COL_IDS = { - AVAILABLE, AVAILABLE_IID, TYPE_IID, ID, - NUM_PORTS, MASTER_ID, MFR, HW, SW, + AVAILABLE, AVAILABLE_IID, TYPE_IID, + NAME, ID, MASTER_ID, NUM_PORTS, MFR, HW, SW, PROTOCOL, CHASSIS_ID, SERIAL }; private static final String ICON_ID_ONLINE = "active"; private static final String ICON_ID_OFFLINE = "inactive"; + private final Logger log = LoggerFactory.getLogger(getClass()); + + @Override protected Collection<RequestHandler> createRequestHandlers() { return ImmutableSet.of( new DataRequestHandler(), + new NameChangeHandler(), new DetailRequestHandler() ); } + // Get friendly name of the device from the annotations + private static String deviceName(Device device) { + String name = device.annotations().value(AnnotationKeys.NAME); + return isNullOrEmpty(name) ? device.id().toString() : name; + } + + private static String deviceProtocol(Device device) { + String protocol = device.annotations().value(PROTOCOL); + return protocol != null ? protocol : ""; + } + private static String getTypeIconId(Device d) { return DEV_ICON_PREFIX + d.type().toString(); } @@ -121,16 +149,15 @@ public class DeviceViewMessageHandler extends UiMessageHandler { boolean available = ds.isAvailable(id); String iconId = available ? ICON_ID_ONLINE : ICON_ID_OFFLINE; - String protocol = dev.annotations().value(PROTOCOL); - row.cell(ID, id) + .cell(NAME, deviceName(dev)) .cell(AVAILABLE, available) .cell(AVAILABLE_IID, iconId) .cell(TYPE_IID, getTypeIconId(dev)) .cell(MFR, dev.manufacturer()) .cell(HW, dev.hwVersion()) .cell(SW, dev.swVersion()) - .cell(PROTOCOL, protocol != null ? protocol : "") + .cell(PROTOCOL, deviceProtocol(dev)) .cell(NUM_PORTS, ds.getPorts(id).size()) .cell(MASTER_ID, ms.getMasterFor(id)); } @@ -144,15 +171,16 @@ public class DeviceViewMessageHandler extends UiMessageHandler { @Override public void process(long sid, ObjectNode payload) { - String id = string(payload, "id", "of:0000000000000000"); + String id = string(payload, ID, ZERO_URI); - DeviceId deviceId = DeviceId.deviceId(id); + DeviceId deviceId = deviceId(id); DeviceService service = get(DeviceService.class); MastershipService ms = get(MastershipService.class); Device device = service.getDevice(deviceId); - ObjectNode data = MAPPER.createObjectNode(); + ObjectNode data = objectNode(); data.put(ID, deviceId.toString()); + data.put(NAME, deviceName(device)); data.put(TYPE, capitalizeFully(device.type().toString())); data.put(TYPE_IID, getTypeIconId(device)); data.put(MFR, device.manufacturer()); @@ -161,9 +189,9 @@ public class DeviceViewMessageHandler extends UiMessageHandler { data.put(SERIAL, device.serialNumber()); data.put(CHASSIS_ID, device.chassisId().toString()); data.put(MASTER_ID, ms.getMasterFor(deviceId).toString()); - data.put(PROTOCOL, device.annotations().value(PROTOCOL)); + data.put(PROTOCOL, deviceProtocol(device)); - ArrayNode ports = MAPPER.createArrayNode(); + ArrayNode ports = arrayNode(); List<Port> portList = new ArrayList<>(service.getPorts(deviceId)); Collections.sort(portList, (p1, p2) -> { @@ -176,13 +204,13 @@ public class DeviceViewMessageHandler extends UiMessageHandler { } data.set(PORTS, ports); - ObjectNode rootNode = MAPPER.createObjectNode(); + ObjectNode rootNode = objectNode(); rootNode.set(DETAILS, data); sendMessage(DEV_DETAILS_RESP, 0, rootNode); } private ObjectNode portData(Port p, DeviceId id) { - ObjectNode port = MAPPER.createObjectNode(); + ObjectNode port = objectNode(); LinkService ls = get(LinkService.class); String name = p.annotations().value(AnnotationKeys.PORT_NAME); @@ -206,4 +234,29 @@ public class DeviceViewMessageHandler extends UiMessageHandler { return port; } } + + + // handler for changing device friendly name + private final class NameChangeHandler extends RequestHandler { + private NameChangeHandler() { + super(DEV_NAME_CHANGE_REQ); + } + + @Override + public void process(long sid, ObjectNode payload) { + DeviceId deviceId = deviceId(string(payload, ID, ZERO_URI)); + String name = emptyToNull(string(payload, NAME, null)); + log.debug("Name change request: {} -- '{}'", deviceId, name); + + NetworkConfigService service = get(NetworkConfigService.class); + BasicDeviceConfig cfg = + service.addConfig(deviceId, BasicDeviceConfig.class); + + // Name attribute missing from the payload (or empty string) + // means that the friendly name should be unset. + cfg.name(name); + cfg.apply(); + sendMessage(DEV_NAME_CHANGE_RESP, 0, payload); + } + } } diff --git a/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/ProcessorViewMessageHandler.java b/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/ProcessorViewMessageHandler.java new file mode 100644 index 00000000..5d97504e --- /dev/null +++ b/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/ProcessorViewMessageHandler.java @@ -0,0 +1,108 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.ui.impl; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.ImmutableSet; +import org.onosproject.net.packet.PacketProcessorEntry; +import org.onosproject.net.packet.PacketService; +import org.onosproject.ui.RequestHandler; +import org.onosproject.ui.UiMessageHandler; +import org.onosproject.ui.table.TableModel; +import org.onosproject.ui.table.TableRequestHandler; +import org.onosproject.ui.table.cell.NumberFormatter; + +import java.util.Collection; + +import static org.onosproject.net.packet.PacketProcessor.ADVISOR_MAX; +import static org.onosproject.net.packet.PacketProcessor.DIRECTOR_MAX; + +/** + * Message handler for packet processor view related messages. + */ +public class ProcessorViewMessageHandler extends UiMessageHandler { + + private static final String PROCESSOR_DATA_REQ = "processorDataRequest"; + private static final String PROCESSOR_DATA_RESP = "processorDataResponse"; + private static final String PROCESSORS = "processors"; + + private static final String OBSERVER = "observer"; + private static final String DIRECTOR = "director"; + private static final String ADVISOR = "advisor"; + + private static final String ID = "id"; + private static final String TYPE = "type"; + private static final String PRIORITY = "priority"; + private static final String PROCESSOR = "processor"; + private static final String PACKETS = "packets"; + private static final String AVG_MS = "avgMillis"; + + private static final long NANOS_IN_MS = 1_000_000; + + private static final String[] COL_IDS = { + ID, TYPE, PRIORITY, PROCESSOR, PACKETS, AVG_MS + }; + + @Override + protected Collection<RequestHandler> createRequestHandlers() { + return ImmutableSet.of(new ProcessorDataRequest()); + } + + // handler for packet processor table requests + private final class ProcessorDataRequest extends TableRequestHandler { + private ProcessorDataRequest() { + super(PROCESSOR_DATA_REQ, PROCESSOR_DATA_RESP, PROCESSORS); + } + + @Override + protected String[] getColumnIds() { + return COL_IDS; + } + + @Override + protected TableModel createTableModel() { + TableModel tm = super.createTableModel(); + tm.setFormatter(AVG_MS, new NumberFormatter()); + return tm; + } + + @Override + protected void populateTable(TableModel tm, ObjectNode payload) { + PacketService ps = get(PacketService.class); + ps.getProcessors().forEach(entry -> populateRow(tm.addRow(), entry)); + } + + private void populateRow(TableModel.Row row, PacketProcessorEntry entry) { + row.cell(ID, entry.priority()) + .cell(TYPE, processorType(entry.priority())) + .cell(PRIORITY, processorPriority(entry.priority())) + .cell(PROCESSOR, entry.processor().getClass().getName()) + .cell(PACKETS, entry.invocations()) + .cell(AVG_MS, (double) entry.averageNanos() / NANOS_IN_MS); + } + + private String processorType(int p) { + return p > DIRECTOR_MAX ? OBSERVER : p > ADVISOR_MAX ? DIRECTOR : ADVISOR; + } + + private int processorPriority(int p) { + return p > DIRECTOR_MAX ? (p - DIRECTOR_MAX - 1) : + p > ADVISOR_MAX ? (p - ADVISOR_MAX - 1) : (p - 1); + } + + } +} diff --git a/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java b/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java index 2bd0bb61..86cf038b 100644 --- a/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java +++ b/framework/src/onos/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java @@ -77,6 +77,7 @@ public class UiExtensionManager implements UiExtensionService, SpriteService { new UiView(PLATFORM, "app", "Applications", "nav_apps"), new UiView(PLATFORM, "settings", "Settings", "nav_settings"), new UiView(PLATFORM, "cluster", "Cluster Nodes", "nav_cluster"), + new UiView(PLATFORM, "processor", "Packet Processors", "nav_processors"), new UiView(NETWORK, "topo", "Topology", "nav_topo"), new UiView(NETWORK, "device", "Devices", "nav_devs"), new UiViewHidden("flow"), @@ -102,6 +103,7 @@ public class UiExtensionManager implements UiExtensionService, SpriteService { new ApplicationViewMessageHandler(), new SettingsViewMessageHandler(), new ClusterViewMessageHandler(), + new ProcessorViewMessageHandler(), new TunnelViewMessageHandler() ); diff --git a/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/glyph.js b/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/glyph.js index 838a2ac0..28f262a1 100644 --- a/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/glyph.js +++ b/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/glyph.js @@ -55,6 +55,15 @@ unknown: "M35,40a5,5,0,0,1,5-5h30a5,5,0,0,1,5,5v30a5,5,0,0,1-5,5" + "h-30a5,5,0,0,1-5-5z", + query: "M51.4,69.9c0-0.9,0-1.6,0-2.1c0-2.7,0.4-5.1,1.2-7.1" + + "c0.6-1.5,1.5-3,2.8-4.5c0.9-1.1,2.6-2.7,5.1-4.8c2.4-2.1,4-3.8," + + "4.8-5.1 c0.7-1.3,1.1-2.6,1.1-4.1c0-2.7-1.1-5.1-3.2-7.1c-2.1-2" + + "-4.8-3.1-7.9-3.1c-3,0-5.5,0.9-7.5,2.8c-2,1.9-3.3,4.8-4,8.7l-7.2" + + "-0.8 c0.7-5.3,2.6-9.3,5.8-12.1c3.2-2.8,7.5-4.2,12.8-4.2c5.6,0," + + "10.1,1.5,13.4,4.5c3.3,3,5,6.7,5,10.9c0,2.5-0.6,4.8-1.8,6.8 " + + "s-3.5,4.6-6.9,7.6c-2.3,2-3.8,3.5-4.5,4.4c-0.7,1-1.2,2-1.6,3.3" + + "c-0.3,1.2-0.5,3.2-0.6,6H51.4z M51,83.8v-7.9h8v7.9H51z", + node: "M15,100a5,5,0,0,1-5-5v-65a5,5,0,0,1,5-5h80a5,5,0,0,1,5,5" + "v65a5,5,0,0,1-5,5zM14,22.5l11-11a10,3,0,0,1,10-2h40a10,3,0,0,1," + "10,2l11,11zM16,35a5,5,0,0,1,10,0a5,5,0,0,1-10,0z", @@ -84,6 +93,22 @@ "M50,29l12,0,0-8,18,13-18,13,0-8-12,0zM60,57l-12,0,0-8-18,13," + "18,13,0-8,12,0z", + microwave: "M85,71.2c-8.9,10.5-29.6,8.7-45.3-3.5C23.9,55.4,19.8," + + "37,28.6,26.5C29.9,38.6,71.5,69.9,85,71.2z M92.7,76.2M16.2,15 " + + "M69.5,100.7v-4c0-1.4-1.2-2.2-2.6-2.2H19.3c-1.4,0-2.8,0.7-2.8,2.2" + + "v3.9c0,0.7,0.8,1,1.5,1h50.3C69,101.5,69.5,101.3,69.5,100.7z " + + "M77.3,7.5l0,3.7c9,0.1,16.3,7.1,16.2,15.7l3.9,0C97.5,16.3,88.5," + + "7.6,77.3,7.5z M77.6,14.7l0,2.5c5.3,0,9.7,4.2,9.6,9.3l2.6,0C89.9" + + ",20,84.4,14.7,77.6,14.7z M82.3,22.2c-1.3-1.2-2.9-1.9-4.7-1.9" + + "l0,1.2c1.4,0,2.8,0.6,3.8,1.5c1,1,1.6,2.3,1.6,3.7l1.3,0C84.3,25.1," + + "83.6,23.4,82.3,22.2z M38.9,69.5l-5.1,23h16.5l-2.5-17.2C44.1,73.3," + + "38.9,69.5,38.9,69.5zM58.1,54.1c13.7,10.1,26.5,16.8,29.2,13.7" + + "c2.7-3.1-5.6-13-19.3-24.4 M62.9,34.2 M62,37.9C47.7,27.3,33.7,20," + + "31,23.1c-2.7,3.2,7,14.2,20.6,26 M73.9,25.7c-2.9,0.1-5.2,2.3-5.1," + + "4.8c0,0.7,0.2,1.4,0.6,2l0,0L53.8,49.7l3.3,2.5L72.7,35l-0.4-0.3" + + "c0.6,0.2,1.3,0.3,1.9,0.3c2.9-0.1,5.2-2.3,5.1-4.9C79.3,27.6,76.8," + + "25.6,73.9,25.7z", + chain: "M60.4,77.6c-4.9,5.2-9.6,11.3-15.3,16.3c-8.6,7.5-20.4,6.8" + "-28-0.8c-7.7-7.7-8.4-19.6-0.8-28.4c6.5-7.4,13.5-14.4,20.9-20.9" + "c7.5-6.7,19.2-6.7,26.5-0.8c3.5,2.8,4.4,6.1,2.2,8.7c-2.7,3.1" + @@ -570,6 +595,10 @@ return glyphs.get(id); } + function glyphDefined(id) { + return glyphs.has(id); + } + // Note: defs should be a D3 selection of a single <defs> element function loadDefs(defs, glyphIds, noClear) { var list = fs.isA(glyphIds) || ids(), @@ -633,6 +662,7 @@ registerGlyphSet: registerGlyphSet, ids: ids, glyph: glyph, + glyphDefined: glyphDefined, loadDefs: loadDefs, addGlyph: addGlyph }; diff --git a/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/icon.js b/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/icon.js index ba794313..15b44bc9 100644 --- a/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/icon.js +++ b/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/icon.js @@ -64,7 +64,8 @@ nav_devs: 'switch', nav_links: 'ports', nav_hosts: 'endstation', - nav_intents: 'relatedIntents' + nav_intents: 'relatedIntents', + nav_processors: 'allTraffic' }; function ensureIconLibDefs() { @@ -162,6 +163,7 @@ // Returns the D3 selection of the icon. function addDeviceIcon(elem, glyphId) { var cfg = config.device, + gid = gs.glyphDefined(glyphId) ? glyphId : 'query', g = elem.append('g') .attr('class', 'svgIcon deviceIcon'); @@ -174,7 +176,7 @@ }); g.append('use').attr({ - 'xlink:href': '#' + glyphId, + 'xlink:href': '#' + gid, width: cfg.dim, height: cfg.dim }); diff --git a/framework/src/onos/web/gui/src/main/webapp/app/fw/util/keys.js b/framework/src/onos/web/gui/src/main/webapp/app/fw/util/keys.js index 2985565c..5ff4f7f4 100644 --- a/framework/src/onos/web/gui/src/main/webapp/app/fw/util/keys.js +++ b/framework/src/onos/web/gui/src/main/webapp/app/fw/util/keys.js @@ -25,6 +25,7 @@ // internal state var enabled = true, + globalEnabled = true, keyHandler = { globalKeys: {}, maskedKeys: {}, @@ -116,6 +117,9 @@ } function quickHelp(view, key, code, ev) { + if (!globalEnabled) { + return false; + } qhs.showQuickHelp(keyHandler); return true; } @@ -126,6 +130,9 @@ } function toggleTheme(view, key, code, ev) { + if (!globalEnabled) { + return false; + } ts.toggleTheme(); return true; } @@ -173,6 +180,23 @@ keyHandler.viewGestures = []; } + function checkNotGlobal(o) { + var oops = []; + if (fs.isO(o)) { + angular.forEach(o, function (val, key) { + if (keyHandler.globalKeys[key]) { + oops.push(key); + } + }); + if (oops.length) { + $log.warn('Ignoring reserved global key(s):', oops.join(',')); + oops.forEach(function (key) { + delete o[key]; + }); + } + } + } + angular.module('onosUtil') .factory('KeyService', ['$log', 'FnService', 'ThemeService', 'NavService', @@ -208,7 +232,11 @@ }, enableKeys: function (b) { enabled = b; - } + }, + enableGlobalKeys: function (b) { + globalEnabled = b; + }, + checkNotGlobal: checkNotGlobal }; }]); diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.css b/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.css index fc08f68b..e0e9cf57 100644 --- a/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.css +++ b/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.css @@ -75,6 +75,15 @@ margin: 8px 0; } +#device-details-panel .editable { + cursor: pointer; + border-bottom: 1px dashed darkgreen; +} + +#device-details-panel h2 input { + font-size: 1.0em; +} + #device-details-panel .top div.left { float: left; padding: 0 18px 0 0; diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.html b/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.html index 5d51d1d4..63a04db8 100644 --- a/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.html +++ b/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.html @@ -35,13 +35,14 @@ <tr> <td colId="available" class="table-icon" sortable></td> <td colId="type" class="table-icon" sortable></td> + <td colId="name" sortable>Friendly Name </td> <td colId="id" sortable>Device ID </td> <td colId="masterid" sortable>Master Instance </td> - <td colId="num_ports" sortable>Ports </td> + <td colId="num_ports" col-width="60px" sortable>Ports </td> <td colId="mfr" sortable>Vendor </td> <td colId="hw" sortable>H/W Version </td> <td colId="sw" sortable>S/W Version </td> - <td colId="protocol" sortable>Protocol </td> + <td colId="protocol" col-width="80px" sortable>Protocol </td> </tr> </table> </div> @@ -64,6 +65,7 @@ <td class="table-icon"> <div icon icon-id="{{dev._iconid_type}}"></div> </td> + <td>{{dev.name}}</td> <td>{{dev.id}}</td> <td>{{dev.masterid}}</td> <td>{{dev.num_ports}}</td> diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.js b/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.js index 7a2dc4f9..5b7120fd 100644 --- a/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.js +++ b/framework/src/onos/web/gui/src/main/webapp/app/view/device/device.js @@ -22,13 +22,17 @@ 'use strict'; // injected refs - var $log, $scope, $location, fs, mast, ps, wss, is, ns; + var $log, $scope, $loc, fs, mast, ps, wss, is, ns, ks; // internal state var detailsPanel, - pStartY, pHeight, - top, bottom, iconDiv, - wSize; + pStartY, + pHeight, + top, + bottom, + iconDiv, + wSize, + editingName = false; // constants var topPdg = 13, @@ -39,13 +43,15 @@ pName = 'device-details-panel', detailsReq = 'deviceDetailsRequest', detailsResp = 'deviceDetailsResponse', + nameChangeReq = 'deviceNameChangeRequest', + nameChangeResp = 'deviceNameChangeResponse', propOrder = [ - 'type', 'masterid', 'chassisid', + 'id', 'type', 'masterid', 'chassisid', 'mfr', 'hw', 'sw', 'protocol', 'serial' ], friendlyProps = [ - 'Type', 'Master ID', 'Chassis ID', + 'URI', 'Type', 'Master ID', 'Chassis ID', 'Vendor', 'H/W Version', 'S/W Version', 'Protocol', 'Serial #' ], portCols = [ @@ -59,7 +65,9 @@ if (detailsPanel.isVisible()) { $scope.selId = null; detailsPanel.hide(); + return true; } + return false; } function addCloseBtn(div) { @@ -68,6 +76,59 @@ div.on('click', closePanel); } + function exitEditMode(nameH2, name) { + nameH2.html(name); + nameH2.classed('editable', true); + editingName = false; + ks.enableGlobalKeys(true); + } + + function editNameSave() { + var nameH2 = top.select('h2'), + id = $scope.panelData.id, + val, + newVal; + + if (editingName) { + val = nameH2.select('input').property('value').trim(); + newVal = val || id; + + exitEditMode(nameH2, newVal); + $scope.panelData.name = newVal; + wss.sendEvent(nameChangeReq, { id: id, name: val }); + } + } + + function editNameCancel() { + if (editingName) { + exitEditMode(top.select('h2'), $scope.panelData.name); + return true; + } + return false; + } + + function editName() { + var nameH2 = top.select('h2'), + tf, el; + + if (!editingName) { + nameH2.classed('editable', false); + nameH2.html(''); + tf = nameH2.append('input').classed('name-input', true) + .attr('type', 'text') + .attr('value', $scope.panelData.name); + el = tf[0][0]; + el.focus(); + el.select(); + editingName = true; + ks.enableGlobalKeys(false); + } + } + + function handleEscape() { + return editNameCancel() || closePanel(); + } + function setUpPanel() { var container, closeBtn, tblDiv; detailsPanel.empty(); @@ -78,7 +139,7 @@ closeBtn = top.append('div').classed('close-btn', true); addCloseBtn(closeBtn); iconDiv = top.append('div').classed('dev-icon', true); - top.append('h2'); + top.append('h2').classed('editable', true).on('click', editName); tblDiv = top.append('div').classed('top-tables', true); tblDiv.append('div').classed('left', true).append('table'); @@ -110,11 +171,11 @@ .append('tbody'); is.loadEmbeddedIcon(iconDiv, details._iconid_type, 40); - top.select('h2').html(details.id); + top.select('h2').html(details.name); propOrder.forEach(function (prop, i) { // properties are split into two tables - addProp(i < 3 ? leftTbl : rightTbl, i, details[prop]); + addProp(i < 4 ? leftTbl : rightTbl, i, details[prop]); }); } @@ -156,14 +217,23 @@ detailsPanel.width(tbWidth + ctnrPdg); } + function populateName(div, name) { + var lab = div.select('.label'), + val = div.select('.value'); + lab.html('Friendly Name:'); + val.html(name); + } + function populateDetails(details) { - var topTbs, btmTbl, ports; + var nameDiv, topTbs, btmTbl, ports; setUpPanel(); + nameDiv = top.select('.name-div'); topTbs = top.select('.top-tables'); btmTbl = bottom.select('table'); ports = details.ports; + populateName(nameDiv, details.name); populateTop(topTbs, details); populateBottom(btmTbl, ports); @@ -175,6 +245,13 @@ $scope.$apply(); } + function respNameCb(data) { + if (data.warn) { + $log.warn(data.warn, data.id); + top.select('h2').html(data.id); + } + } + function createDetailsPane() { detailsPanel = ps.createPanel(pName, { width: wSize.width, @@ -193,21 +270,26 @@ .controller('OvDeviceCtrl', ['$log', '$scope', '$location', 'TableBuilderService', 'FnService', 'MastService', 'PanelService', 'WebSocketService', 'IconService', - 'NavService', + 'NavService', 'KeyService', function (_$log_, _$scope_, _$location_, - tbs, _fs_, _mast_, _ps_, _wss_, _is_, _ns_) { + tbs, _fs_, _mast_, _ps_, _wss_, _is_, _ns_, _ks_) { + var params, + handlers = {}; + $log = _$log_; $scope = _$scope_; - $location = _$location_; + $loc = _$location_; fs = _fs_; mast = _mast_; ps = _ps_; wss = _wss_; is = _is_; ns = _ns_; - var params = $location.search(), - handlers = {}; + ks = _ks_; + + params = $loc.search(); + $scope.panelData = {}; $scope.flowTip = 'Show flow view for selected device'; $scope.portTip = 'Show port view for selected device'; @@ -215,6 +297,7 @@ // details panel handlers handlers[detailsResp] = respDetailsCb; + handlers[nameChangeResp] = respNameCb; wss.bindHandlers(handlers); // query for if a certain device needs to be highlighted @@ -278,7 +361,8 @@ } // create key bindings to handle panel ks.keyBindings({ - esc: [closePanel, 'Close the details panel'], + enter: editNameSave, + esc: [handleEscape, 'Close the details panel'], _helpFormat: ['esc'] }); ks.gestureNotes([ diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.css b/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.css new file mode 100644 index 00000000..12cf6377 --- /dev/null +++ b/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.css @@ -0,0 +1,49 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + ONOS GUI -- Processor View -- CSS file + */ + +#ov-processor h2 { + display: inline-block; +} + +#ov-processor div.ctrl-btns { + width: 40px; +} + +.light #ov-processor .current-view use { + fill: white; +} +.dark #ov-processor .current-view use { + fill: #304860; +} + +.light #ov-processor .current-view rect { + fill: deepskyblue; +} +.dark #ov-processor .current-view rect { + fill: #eee; +} + +#ov-processor td.number { + text-align: right; +} + +#ov-processor tr.no-data td { + text-align: center; +} diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.html b/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.html new file mode 100644 index 00000000..1c615041 --- /dev/null +++ b/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.html @@ -0,0 +1,63 @@ +<!-- processor partial HTML --> +<div id="ov-processor"> + <div class="tabular-header"> + <h2> + Packet Processors ({{tableData.length}} Processors total) + </h2> + <div class="ctrl-btns"> + <div class="refresh" ng-class="{active: autoRefresh}" + icon icon-size="36" icon-id="refresh" + tooltip tt-msg="autoRefreshTip" + ng-click="toggleRefresh()"></div> + <!-- + <div class="separator"></div> + + <div class="current-view" + icon icon-id="processorTable" icon-size="36"></div> + + <div class="active" + icon icon-id="requestTable" icon-size="36"git sta + tooltip tt-msg="requestTip" + ng-click="nav('request')"></div> + --> + </div> + </div> + + <div class="summary-list" onos-table-resize> + <div ng-show="loading" class="loading-wheel" + icon icon-id="loading" icon-size="75"></div> + + <div class="table-header" onos-sortable-header> + <table> + <tr> + <td class="number" colId="priority" sortable col-width="80px">Priority </td> + <td colId="type" sortable col-width="80px">Type </td> + <td colId="processor" sortable col-width="500px">Class </td> + <td class="number" colId="packets" sortable col-width="100px">Packets </td> + <td class="number" colId="avgMillis" sortable col-width="100px">Average (ms) </td> + </tr> + </table> + </div> + + <div class="table-body"> + <table onos-flash-changes id-prop="id"> + <tr ng-if="!tableData.length" class="no-data"> + <td colspan="5"> + No Processors found + </td> + </tr> + + <tr ng-repeat="processor in tableData track by $index" + ng-repeat-complete row-id="{{processor.id}}"> + <td class="number">{{processor.priority}}</td> + <td>{{processor.type}}</td> + <td>{{processor.processor}}</td> + <td class="number">{{processor.packets}}</td> + <td class="number">{{processor.avgMillis}}</td> + </tr> + </table> + </div> + + </div> + +</div> diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.js b/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.js new file mode 100644 index 00000000..89d717b6 --- /dev/null +++ b/framework/src/onos/web/gui/src/main/webapp/app/view/processor/processor.js @@ -0,0 +1,58 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + ONOS GUI -- Packet Processor View Module + */ + +(function () { + 'use strict'; + + // injected references + var $log, $scope, $location, fs, tbs, ns; + + angular.module('ovProcessor', []) + .controller('OvProcessorCtrl', + ['$log', '$scope', '$location', + 'FnService', 'TableBuilderService', 'NavService', + + function (_$log_, _$scope_, _$location_, _fs_, _tbs_, _ns_) { + var params; + $log = _$log_; + $scope = _$scope_; + $location = _$location_; + fs = _fs_; + tbs = _tbs_; + ns = _ns_; + $scope.requestTip = 'Show packet requests'; + + params = $location.search(); + + tbs.buildTable({ + scope: $scope, + tag: 'processor', + query: params + }); + + $scope.nav = function (path) { + if ($scope.devId) { + ns.navTo(path); + } + }; + + $log.log('OvProcessorCtrl has been created'); + }]); +}()); diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/settings/settings.html b/framework/src/onos/web/gui/src/main/webapp/app/view/settings/settings.html index ee069d37..61081017 100644 --- a/framework/src/onos/web/gui/src/main/webapp/app/view/settings/settings.html +++ b/framework/src/onos/web/gui/src/main/webapp/app/view/settings/settings.html @@ -17,7 +17,7 @@ <div class="table-header" onos-sortable-header> <table> <tr> - <td colId="component" sortable col-width="200px">Component </td> + <td colId="component" sortable col-width="300px">Component </td> <td colId="id" sortable>Property </td> <td colId="type" sortable col-width="70px">Type </td> <td colId="value" sortable>Value </td> diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topo.js b/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topo.js index 21894100..42b6f4bd 100644 --- a/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topo.js +++ b/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topo.js @@ -95,6 +95,9 @@ // and include them in the quick-help panel function mergeKeys(extra) { var _hf = actionMap._helpFormat[2]; + + ks.checkNotGlobal(extra); + extra._keyOrder.forEach(function (k) { var d = extra[k], cb = d && d.cb, diff --git a/framework/src/onos/web/gui/src/main/webapp/index.html b/framework/src/onos/web/gui/src/main/webapp/index.html index 154f9416..5df3c664 100644 --- a/framework/src/onos/web/gui/src/main/webapp/index.html +++ b/framework/src/onos/web/gui/src/main/webapp/index.html @@ -121,6 +121,7 @@ <script src="app/view/app/app.js"></script> <script src="app/view/settings/settings.js"></script> <script src="app/view/cluster/cluster.js"></script> + <script src="app/view/processor/processor.js"></script> <script src="app/view/tunnel/tunnel.js"></script> <!-- This is where contributed javascript will get injected --> @@ -139,6 +140,7 @@ <link rel="stylesheet" href="app/view/app/app.css"> <link rel="stylesheet" href="app/view/settings/settings.css"> <link rel="stylesheet" href="app/view/cluster/cluster.css"> + <link rel="stylesheet" href="app/view/processor/processor.css"> <link rel="stylesheet" href="app/view/tunnel/tunnel.css"> <!-- This is where contributed stylesheets will get injected --> diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_10_addDevice_s9_ids.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_10_addDevice_s9_ids.json new file mode 100644 index 00000000..232d4edb --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_10_addDevice_s9_ids.json @@ -0,0 +1,18 @@ +{ + "event": "addDevice", + "payload": { + "id": "of:0000000000000009", + "type": "ids", + "online": true, + "master": "ONOS", + "labels": [ + "", + "ids", + "of:0000000000000009" + ], + "metaUi": { + "x": 200, + "y": 400 + } + } +} diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_11_addDevice_s10_controller.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_11_addDevice_s10_controller.json new file mode 100644 index 00000000..abd24d66 --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_11_addDevice_s10_controller.json @@ -0,0 +1,18 @@ +{ + "event": "addDevice", + "payload": { + "id": "of:00000000000000010", + "type": "controller", + "online": true, + "master": "ONOS", + "labels": [ + "", + "controller", + "of:0000000000000010" + ], + "metaUi": { + "x": 350, + "y": 400 + } + } +} diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_12_addDevice_s11_virtual.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_12_addDevice_s11_virtual.json new file mode 100644 index 00000000..1fe1837b --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_12_addDevice_s11_virtual.json @@ -0,0 +1,18 @@ +{ + "event": "addDevice", + "payload": { + "id": "of:00000000000000011", + "type": "virtual", + "online": true, + "master": "ONOS", + "labels": [ + "", + "virtual", + "of:0000000000000011" + ], + "metaUi": { + "x": 500, + "y": 400 + } + } +} diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_13_addDevice_s12_fiber_switch.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_13_addDevice_s12_fiber_switch.json new file mode 100644 index 00000000..954376cd --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_13_addDevice_s12_fiber_switch.json @@ -0,0 +1,18 @@ +{ + "event": "addDevice", + "payload": { + "id": "of:00000000000000012", + "type": "fiber_switch", + "online": true, + "master": "ONOS", + "labels": [ + "", + "fiber_switch", + "of:0000000000000012" + ], + "metaUi": { + "x": 650, + "y": 400 + } + } +} diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_14_addDevice_s13_microwave.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_14_addDevice_s13_microwave.json new file mode 100644 index 00000000..3d40ceca --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_14_addDevice_s13_microwave.json @@ -0,0 +1,18 @@ +{ + "event": "addDevice", + "payload": { + "id": "of:00000000000000013", + "type": "microwave", + "online": true, + "master": "ONOS", + "labels": [ + "", + "microwave", + "of:0000000000000013" + ], + "metaUi": { + "x": 300, + "y": 500 + } + } +} diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_15_addDevice_s14_other.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_15_addDevice_s14_other.json new file mode 100644 index 00000000..e33532d7 --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_15_addDevice_s14_other.json @@ -0,0 +1,18 @@ +{ + "event": "addDevice", + "payload": { + "id": "of:00000000000000014", + "type": "other", + "online": true, + "master": "ONOS", + "labels": [ + "", + "other", + "of:0000000000000014" + ], + "metaUi": { + "x": 450, + "y": 500 + } + } +} diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_16_addDevice_s15_unmatched.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_16_addDevice_s15_unmatched.json new file mode 100644 index 00000000..1773c917 --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_16_addDevice_s15_unmatched.json @@ -0,0 +1,18 @@ +{ + "event": "addDevice", + "payload": { + "id": "of:00000000000000015", + "type": "-unmatched-", + "online": true, + "master": "ONOS-B", + "labels": [ + "", + "-unmatched-", + "of:0000000000000015" + ], + "metaUi": { + "x": 600, + "y": 500 + } + } +} diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_1_addInstance.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_1_addInstance.json new file mode 100644 index 00000000..20be9e2b --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_1_addInstance.json @@ -0,0 +1,14 @@ +{ + "event": "addInstance", + "payload": { + "id": "ONOS", + "ip": "192.168.56.101", + "online": true, + "uiAttached": true, + "switches": 4, + "labels": [ + "ONOS", + "192.168.56.101" + ] + } +} diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_2_addDevice_s1_switch.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_2_addDevice_s1_switch.json new file mode 100644 index 00000000..5f8ad66e --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_2_addDevice_s1_switch.json @@ -0,0 +1,18 @@ +{ + "event": "addDevice", + "payload": { + "id": "of:0000000000000001", + "type": "switch", + "online": true, + "master": "ONOS", + "labels": [ + "", + "switch", + "of:0000000000000001" + ], + "metaUi": { + "x": 200, + "y": 200 + } + } +} diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_3_addDevice_s2_router.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_3_addDevice_s2_router.json new file mode 100644 index 00000000..b6da44ce --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_3_addDevice_s2_router.json @@ -0,0 +1,18 @@ +{ + "event": "addDevice", + "payload": { + "id": "of:0000000000000002", + "type": "router", + "online": true, + "master": "ONOS", + "labels": [ + "", + "router", + "of:0000000000000002" + ], + "metaUi": { + "x": 350, + "y": 200 + } + } +} diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_4_addDevice_s3_roadm.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_4_addDevice_s3_roadm.json new file mode 100644 index 00000000..468749ef --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_4_addDevice_s3_roadm.json @@ -0,0 +1,18 @@ +{ + "event": "addDevice", + "payload": { + "id": "of:0000000000000003", + "type": "roadm", + "online": true, + "master": "ONOS", + "labels": [ + "", + "roadm", + "of:0000000000000003" + ], + "metaUi": { + "x": 500, + "y": 200 + } + } +} diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_5_addDevice_s4_otn.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_5_addDevice_s4_otn.json new file mode 100644 index 00000000..c8e234be --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_5_addDevice_s4_otn.json @@ -0,0 +1,18 @@ +{ + "event": "addDevice", + "payload": { + "id": "of:0000000000000004", + "type": "otn", + "online": true, + "master": "ONOS", + "labels": [ + "", + "otn", + "of:0000000000000004" + ], + "metaUi": { + "x": 650, + "y": 200 + } + } +} diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_6_addDevice_s5_roadm_otn.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_6_addDevice_s5_roadm_otn.json new file mode 100644 index 00000000..c54cc4ae --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_6_addDevice_s5_roadm_otn.json @@ -0,0 +1,18 @@ +{ + "event": "addDevice", + "payload": { + "id": "of:0000000000000005", + "type": "roadm_otn", + "online": true, + "master": "ONOS", + "labels": [ + "", + "roadm_otn", + "of:0000000000000005" + ], + "metaUi": { + "x": 300, + "y": 300 + } + } +} diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_7_addDevice_s6_firewall.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_7_addDevice_s6_firewall.json new file mode 100644 index 00000000..19d50d45 --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_7_addDevice_s6_firewall.json @@ -0,0 +1,18 @@ +{ + "event": "addDevice", + "payload": { + "id": "of:0000000000000006", + "type": "firewall", + "online": true, + "master": "ONOS", + "labels": [ + "", + "firewall", + "of:0000000000000006" + ], + "metaUi": { + "x": 450, + "y": 300 + } + } +} diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_8_addDevice_s7_balancer.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_8_addDevice_s7_balancer.json new file mode 100644 index 00000000..d9a3b795 --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_8_addDevice_s7_balancer.json @@ -0,0 +1,18 @@ +{ + "event": "addDevice", + "payload": { + "id": "of:0000000000000007", + "type": "balancer", + "online": true, + "master": "ONOS", + "labels": [ + "", + "balancer", + "of:0000000000000007" + ], + "metaUi": { + "x": 600, + "y": 300 + } + } +} diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_9_addDevice_s8_ips.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_9_addDevice_s8_ips.json new file mode 100644 index 00000000..c89f565e --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/ev_9_addDevice_s8_ips.json @@ -0,0 +1,18 @@ +{ + "event": "addDevice", + "payload": { + "id": "of:0000000000000008", + "type": "ips", + "online": true, + "master": "ONOS", + "labels": [ + "", + "ips", + "of:0000000000000008" + ], + "metaUi": { + "x": 750, + "y": 300 + } + } +} diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/devices/scenario.json b/framework/src/onos/web/gui/src/test/_karma/ev/devices/scenario.json new file mode 100644 index 00000000..ee3c1eaf --- /dev/null +++ b/framework/src/onos/web/gui/src/test/_karma/ev/devices/scenario.json @@ -0,0 +1,12 @@ +{ + "comments": [ + "Showing all device types" + ], + "title": "Show Device Types", + "params": { + "lastAuto": 16 + }, + "description": [ + "Show all device types." + ] +} |