alshabib

Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next

Conflicts:
	core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java
	core/store/hz/net/src/test/java/org/onlab/onos/store/device/impl/DistributedDeviceStoreTest.java
	features/features.xml
	tools/test/cells/office

Change-Id: I08e6d7c6a0bdaae072dd50ff7ac36d94f16d77e1
Showing 151 changed files with 9587 additions and 2351 deletions
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
5 + <modelVersion>4.0.0</modelVersion>
6 +
7 + <parent>
8 + <groupId>org.onlab.onos</groupId>
9 + <artifactId>onos-apps</artifactId>
10 + <version>1.0.0-SNAPSHOT</version>
11 + <relativePath>../pom.xml</relativePath>
12 + </parent>
13 +
14 + <artifactId>onos-app-calendar</artifactId>
15 + <packaging>bundle</packaging>
16 +
17 + <description>ONOS simple calendaring REST interface for intents</description>
18 +
19 + <properties>
20 + <web.context>/onos/calendar</web.context>
21 + </properties>
22 +
23 + <dependencies>
24 + <dependency>
25 + <groupId>org.onlab.onos</groupId>
26 + <artifactId>onlab-rest</artifactId>
27 + <version>${project.version}</version>
28 + </dependency>
29 +
30 + <dependency>
31 + <groupId>com.sun.jersey</groupId>
32 + <artifactId>jersey-servlet</artifactId>
33 + </dependency>
34 + <dependency>
35 + <groupId>com.sun.jersey.jersey-test-framework</groupId>
36 + <artifactId>jersey-test-framework-core</artifactId>
37 + <version>1.18.1</version>
38 + <scope>test</scope>
39 + </dependency>
40 + <dependency>
41 + <groupId>com.sun.jersey.jersey-test-framework</groupId>
42 + <artifactId>jersey-test-framework-grizzly2</artifactId>
43 + <version>1.18.1</version>
44 + <scope>test</scope>
45 + </dependency>
46 + <dependency>
47 + <groupId>org.osgi</groupId>
48 + <artifactId>org.osgi.core</artifactId>
49 + </dependency>
50 + </dependencies>
51 +
52 + <build>
53 + <plugins>
54 + <plugin>
55 + <groupId>org.apache.felix</groupId>
56 + <artifactId>maven-bundle-plugin</artifactId>
57 + <extensions>true</extensions>
58 + <configuration>
59 + <instructions>
60 + <_wab>src/main/webapp/</_wab>
61 + <Bundle-SymbolicName>
62 + ${project.groupId}.${project.artifactId}
63 + </Bundle-SymbolicName>
64 + <Import-Package>
65 + org.osgi.framework,
66 + javax.ws.rs,javax.ws.rs.core,
67 + com.sun.jersey.api.core,
68 + com.sun.jersey.spi.container.servlet,
69 + com.sun.jersey.server.impl.container.servlet,
70 + org.onlab.packet.*,
71 + org.onlab.rest.*,
72 + org.onlab.onos.*
73 + </Import-Package>
74 + <Web-ContextPath>${web.context}</Web-ContextPath>
75 + </instructions>
76 + </configuration>
77 + </plugin>
78 + </plugins>
79 + </build>
80 +
81 +</project>
1 +package org.onlab.onos.calendar;
2 +
3 +import org.onlab.onos.net.ConnectPoint;
4 +import org.onlab.onos.net.DeviceId;
5 +import org.onlab.onos.net.intent.IntentService;
6 +import org.onlab.rest.BaseResource;
7 +
8 +import javax.ws.rs.POST;
9 +import javax.ws.rs.Path;
10 +import javax.ws.rs.PathParam;
11 +import javax.ws.rs.core.Response;
12 +import java.net.URI;
13 +
14 +import static org.onlab.onos.net.PortNumber.portNumber;
15 +
16 +/**
17 + * Web resource for triggering calendared intents.
18 + */
19 +@Path("intent")
20 +public class BandwidthCalendarResource extends BaseResource {
21 +
22 + @POST
23 + @Path("{src}/{dst}/{srcPort}/{dstPort}/{bandwidth}")
24 + public Response createIntent(@PathParam("src") String src,
25 + @PathParam("dst") String dst,
26 + @PathParam("srcPort") String srcPort,
27 + @PathParam("dstPort") String dstPort,
28 + @PathParam("bandwidth") String bandwidth) {
29 + // TODO: implement calls to intent framework
30 + IntentService service = get(IntentService.class);
31 +
32 + ConnectPoint srcPoint = new ConnectPoint(deviceId(src), portNumber(srcPort));
33 + ConnectPoint dstPoint = new ConnectPoint(deviceId(dst), portNumber(dstPort));
34 +
35 + return Response.ok("Yo! We got src=" + srcPoint + "; dst=" + dstPoint +
36 + "; bw=" + bandwidth + "; intent service " + service).build();
37 + }
38 +
39 + private DeviceId deviceId(String dpid) {
40 + return DeviceId.deviceId(URI.create("of:" + dpid));
41 + }
42 +
43 +}
1 +/**
2 + * Application providing integration between OSCARS and ONOS intent
3 + * framework via REST API.
4 + */
5 +package org.onlab.onos.calendar;
...\ No newline at end of file ...\ No newline at end of file
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
3 + xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
4 + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
5 + id="ONOS" version="2.5">
6 + <display-name>ONOS GUI</display-name>
7 +
8 + <servlet>
9 + <servlet-name>JAX-RS Service</servlet-name>
10 + <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
11 + <init-param>
12 + <param-name>com.sun.jersey.config.property.packages</param-name>
13 + <param-value>org.onlab.onos.calendar</param-value>
14 + </init-param>
15 + <load-on-startup>10</load-on-startup>
16 + </servlet>
17 +
18 + <servlet-mapping>
19 + <servlet-name>JAX-RS Service</servlet-name>
20 + <url-pattern>/rs/*</url-pattern>
21 + </servlet-mapping>
22 +
23 +</web-app>
...\ No newline at end of file ...\ No newline at end of file
...@@ -16,4 +16,11 @@ ...@@ -16,4 +16,11 @@
16 16
17 <description>ONOS simple reactive forwarding app</description> 17 <description>ONOS simple reactive forwarding app</description>
18 18
19 + <dependencies>
20 + <dependency>
21 + <groupId>org.osgi</groupId>
22 + <artifactId>org.osgi.compendium</artifactId>
23 + </dependency>
24 + </dependencies>
25 +
19 </project> 26 </project>
......
1 package org.onlab.onos.fwd; 1 package org.onlab.onos.fwd;
2 2
3 -import static org.slf4j.LoggerFactory.getLogger;
4 -
5 -import java.util.Set;
6 -
7 import org.apache.felix.scr.annotations.Activate; 3 import org.apache.felix.scr.annotations.Activate;
8 import org.apache.felix.scr.annotations.Component; 4 import org.apache.felix.scr.annotations.Component;
9 import org.apache.felix.scr.annotations.Deactivate; 5 import org.apache.felix.scr.annotations.Deactivate;
6 +import org.apache.felix.scr.annotations.Modified;
7 +import org.apache.felix.scr.annotations.Property;
10 import org.apache.felix.scr.annotations.Reference; 8 import org.apache.felix.scr.annotations.Reference;
11 import org.apache.felix.scr.annotations.ReferenceCardinality; 9 import org.apache.felix.scr.annotations.ReferenceCardinality;
12 import org.onlab.onos.ApplicationId; 10 import org.onlab.onos.ApplicationId;
...@@ -29,8 +27,14 @@ import org.onlab.onos.net.packet.PacketProcessor; ...@@ -29,8 +27,14 @@ import org.onlab.onos.net.packet.PacketProcessor;
29 import org.onlab.onos.net.packet.PacketService; 27 import org.onlab.onos.net.packet.PacketService;
30 import org.onlab.onos.net.topology.TopologyService; 28 import org.onlab.onos.net.topology.TopologyService;
31 import org.onlab.packet.Ethernet; 29 import org.onlab.packet.Ethernet;
30 +import org.osgi.service.component.ComponentContext;
32 import org.slf4j.Logger; 31 import org.slf4j.Logger;
33 32
33 +import java.util.Dictionary;
34 +import java.util.Set;
35 +
36 +import static org.slf4j.LoggerFactory.getLogger;
37 +
34 /** 38 /**
35 * Sample reactive forwarding application. 39 * Sample reactive forwarding application.
36 */ 40 */
...@@ -61,6 +65,10 @@ public class ReactiveForwarding { ...@@ -61,6 +65,10 @@ public class ReactiveForwarding {
61 65
62 private ApplicationId appId; 66 private ApplicationId appId;
63 67
68 + @Property(name = "enabled", boolValue = true,
69 + label = "Enable forwarding; default is true")
70 + private boolean isEnabled = true;
71 +
64 @Activate 72 @Activate
65 public void activate() { 73 public void activate() {
66 appId = coreService.registerApplication("org.onlab.onos.fwd"); 74 appId = coreService.registerApplication("org.onlab.onos.fwd");
...@@ -76,6 +84,22 @@ public class ReactiveForwarding { ...@@ -76,6 +84,22 @@ public class ReactiveForwarding {
76 log.info("Stopped"); 84 log.info("Stopped");
77 } 85 }
78 86
87 + @Modified
88 + public void modified(ComponentContext context) {
89 + Dictionary properties = context.getProperties();
90 + String flag = (String) properties.get("enabled");
91 + if (flag != null) {
92 + boolean enabled = flag.equals("true");
93 + if (isEnabled != enabled) {
94 + isEnabled = enabled;
95 + if (!isEnabled) {
96 + flowRuleService.removeFlowRulesById(appId);
97 + }
98 + log.info("Reconfigured. Forwarding is {}",
99 + isEnabled ? "enabled" : "disabled");
100 + }
101 + }
102 + }
79 103
80 /** 104 /**
81 * Packet processor responsible for forwarding packets along their paths. 105 * Packet processor responsible for forwarding packets along their paths.
...@@ -86,7 +110,7 @@ public class ReactiveForwarding { ...@@ -86,7 +110,7 @@ public class ReactiveForwarding {
86 public void process(PacketContext context) { 110 public void process(PacketContext context) {
87 // Stop processing if the packet has been handled, since we 111 // Stop processing if the packet has been handled, since we
88 // can't do any more to it. 112 // can't do any more to it.
89 - if (context.isHandled()) { 113 + if (!isEnabled || context.isHandled()) {
90 return; 114 return;
91 } 115 }
92 116
...@@ -114,8 +138,8 @@ public class ReactiveForwarding { ...@@ -114,8 +138,8 @@ public class ReactiveForwarding {
114 // Otherwise, get a set of paths that lead from here to the 138 // Otherwise, get a set of paths that lead from here to the
115 // destination edge switch. 139 // destination edge switch.
116 Set<Path> paths = topologyService.getPaths(topologyService.currentTopology(), 140 Set<Path> paths = topologyService.getPaths(topologyService.currentTopology(),
117 - pkt.receivedFrom().deviceId(), 141 + pkt.receivedFrom().deviceId(),
118 - dst.location().deviceId()); 142 + dst.location().deviceId());
119 if (paths.isEmpty()) { 143 if (paths.isEmpty()) {
120 // If there are no paths, flood and bail. 144 // If there are no paths, flood and bail.
121 flood(context); 145 flood(context);
...@@ -127,8 +151,8 @@ public class ReactiveForwarding { ...@@ -127,8 +151,8 @@ public class ReactiveForwarding {
127 Path path = pickForwardPath(paths, pkt.receivedFrom().port()); 151 Path path = pickForwardPath(paths, pkt.receivedFrom().port());
128 if (path == null) { 152 if (path == null) {
129 log.warn("Doh... don't know where to go... {} -> {} received on {}", 153 log.warn("Doh... don't know where to go... {} -> {} received on {}",
130 - ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(), 154 + ethPkt.getSourceMAC(), ethPkt.getDestinationMAC(),
131 - pkt.receivedFrom()); 155 + pkt.receivedFrom());
132 flood(context); 156 flood(context);
133 return; 157 return;
134 } 158 }
...@@ -152,7 +176,7 @@ public class ReactiveForwarding { ...@@ -152,7 +176,7 @@ public class ReactiveForwarding {
152 // Floods the specified packet if permissible. 176 // Floods the specified packet if permissible.
153 private void flood(PacketContext context) { 177 private void flood(PacketContext context) {
154 if (topologyService.isBroadcastPoint(topologyService.currentTopology(), 178 if (topologyService.isBroadcastPoint(topologyService.currentTopology(),
155 - context.inPacket().receivedFrom())) { 179 + context.inPacket().receivedFrom())) {
156 packetOut(context, PortNumber.FLOOD); 180 packetOut(context, PortNumber.FLOOD);
157 } else { 181 } else {
158 context.block(); 182 context.block();
...@@ -174,18 +198,17 @@ public class ReactiveForwarding { ...@@ -174,18 +198,17 @@ public class ReactiveForwarding {
174 Ethernet inPkt = context.inPacket().parsed(); 198 Ethernet inPkt = context.inPacket().parsed();
175 TrafficSelector.Builder builder = DefaultTrafficSelector.builder(); 199 TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
176 builder.matchEthType(inPkt.getEtherType()) 200 builder.matchEthType(inPkt.getEtherType())
177 - .matchEthSrc(inPkt.getSourceMAC()) 201 + .matchEthSrc(inPkt.getSourceMAC())
178 - .matchEthDst(inPkt.getDestinationMAC()) 202 + .matchEthDst(inPkt.getDestinationMAC())
179 - .matchInport(context.inPacket().receivedFrom().port()); 203 + .matchInport(context.inPacket().receivedFrom().port());
180 204
181 TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder(); 205 TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
182 treat.setOutput(portNumber); 206 treat.setOutput(portNumber);
183 207
184 FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(), 208 FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
185 - builder.build(), treat.build(), PRIORITY, appId, TIMEOUT); 209 + builder.build(), treat.build(), PRIORITY, appId, TIMEOUT);
186 210
187 flowRuleService.applyFlowRules(f); 211 flowRuleService.applyFlowRules(f);
188 -
189 } 212 }
190 213
191 } 214 }
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
25 <module>proxyarp</module> 25 <module>proxyarp</module>
26 <module>config</module> 26 <module>config</module>
27 <module>sdnip</module> 27 <module>sdnip</module>
28 + <module>calendar</module>
28 </modules> 29 </modules>
29 30
30 <properties> 31 <properties>
......
...@@ -36,6 +36,36 @@ ...@@ -36,6 +36,36 @@
36 <groupId>com.google.guava</groupId> 36 <groupId>com.google.guava</groupId>
37 <artifactId>guava</artifactId> 37 <artifactId>guava</artifactId>
38 </dependency> 38 </dependency>
39 +
40 + <dependency>
41 + <groupId>org.onlab.onos</groupId>
42 + <artifactId>onlab-thirdparty</artifactId>
43 + </dependency>
44 +
45 + <dependency>
46 + <groupId>org.onlab.onos</groupId>
47 + <artifactId>onlab-misc</artifactId>
48 + </dependency>
49 +
50 + <dependency>
51 + <groupId>org.onlab.onos</groupId>
52 + <artifactId>onos-cli</artifactId>
53 + <version>${project.version}</version>
54 + </dependency>
55 + <dependency>
56 + <groupId>org.apache.karaf.shell</groupId>
57 + <artifactId>org.apache.karaf.shell.console</artifactId>
58 + </dependency>
59 + <dependency>
60 + <groupId>org.osgi</groupId>
61 + <artifactId>org.osgi.core</artifactId>
62 + </dependency>
63 +
64 + <dependency>
65 + <groupId>org.easymock</groupId>
66 + <artifactId>easymock</artifactId>
67 + <scope>test</scope>
68 + </dependency>
39 </dependencies> 69 </dependencies>
40 70
41 </project> 71 </project>
......
...@@ -126,8 +126,8 @@ public class PeerConnectivity { ...@@ -126,8 +126,8 @@ public class PeerConnectivity {
126 TrafficSelector selector = DefaultTrafficSelector.builder() 126 TrafficSelector selector = DefaultTrafficSelector.builder()
127 .matchEthType(Ethernet.TYPE_IPV4) 127 .matchEthType(Ethernet.TYPE_IPV4)
128 .matchIPProtocol(IPv4.PROTOCOL_TCP) 128 .matchIPProtocol(IPv4.PROTOCOL_TCP)
129 - .matchIPSrc(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH)) 129 + .matchIPSrc(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
130 - .matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH)) 130 + .matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
131 .matchTcpDst(BGP_PORT) 131 .matchTcpDst(BGP_PORT)
132 .build(); 132 .build();
133 133
...@@ -147,8 +147,8 @@ public class PeerConnectivity { ...@@ -147,8 +147,8 @@ public class PeerConnectivity {
147 selector = DefaultTrafficSelector.builder() 147 selector = DefaultTrafficSelector.builder()
148 .matchEthType(Ethernet.TYPE_IPV4) 148 .matchEthType(Ethernet.TYPE_IPV4)
149 .matchIPProtocol(IPv4.PROTOCOL_TCP) 149 .matchIPProtocol(IPv4.PROTOCOL_TCP)
150 - .matchIPSrc(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH)) 150 + .matchIPSrc(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
151 - .matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH)) 151 + .matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
152 .matchTcpSrc(BGP_PORT) 152 .matchTcpSrc(BGP_PORT)
153 .build(); 153 .build();
154 154
...@@ -165,8 +165,8 @@ public class PeerConnectivity { ...@@ -165,8 +165,8 @@ public class PeerConnectivity {
165 selector = DefaultTrafficSelector.builder() 165 selector = DefaultTrafficSelector.builder()
166 .matchEthType(Ethernet.TYPE_IPV4) 166 .matchEthType(Ethernet.TYPE_IPV4)
167 .matchIPProtocol(IPv4.PROTOCOL_TCP) 167 .matchIPProtocol(IPv4.PROTOCOL_TCP)
168 - .matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH)) 168 + .matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
169 - .matchIPDst(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH)) 169 + .matchIPDst(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
170 .matchTcpDst(BGP_PORT) 170 .matchTcpDst(BGP_PORT)
171 .build(); 171 .build();
172 172
...@@ -183,8 +183,8 @@ public class PeerConnectivity { ...@@ -183,8 +183,8 @@ public class PeerConnectivity {
183 selector = DefaultTrafficSelector.builder() 183 selector = DefaultTrafficSelector.builder()
184 .matchEthType(Ethernet.TYPE_IPV4) 184 .matchEthType(Ethernet.TYPE_IPV4)
185 .matchIPProtocol(IPv4.PROTOCOL_TCP) 185 .matchIPProtocol(IPv4.PROTOCOL_TCP)
186 - .matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH)) 186 + .matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
187 - .matchIPDst(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH)) 187 + .matchIPDst(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
188 .matchTcpSrc(BGP_PORT) 188 .matchTcpSrc(BGP_PORT)
189 .build(); 189 .build();
190 190
...@@ -251,8 +251,8 @@ public class PeerConnectivity { ...@@ -251,8 +251,8 @@ public class PeerConnectivity {
251 TrafficSelector selector = DefaultTrafficSelector.builder() 251 TrafficSelector selector = DefaultTrafficSelector.builder()
252 .matchEthType(Ethernet.TYPE_IPV4) 252 .matchEthType(Ethernet.TYPE_IPV4)
253 .matchIPProtocol(IPv4.PROTOCOL_ICMP) 253 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
254 - .matchIPSrc(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH)) 254 + .matchIPSrc(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
255 - .matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH)) 255 + .matchIPDst(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
256 .build(); 256 .build();
257 257
258 TrafficTreatment treatment = DefaultTrafficTreatment.builder() 258 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
...@@ -269,8 +269,8 @@ public class PeerConnectivity { ...@@ -269,8 +269,8 @@ public class PeerConnectivity {
269 selector = DefaultTrafficSelector.builder() 269 selector = DefaultTrafficSelector.builder()
270 .matchEthType(Ethernet.TYPE_IPV4) 270 .matchEthType(Ethernet.TYPE_IPV4)
271 .matchIPProtocol(IPv4.PROTOCOL_ICMP) 271 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
272 - .matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toRealInt(), IPV4_BIT_LENGTH)) 272 + .matchIPSrc(IpPrefix.valueOf(bgpdPeerAddress.toInt(), IPV4_BIT_LENGTH))
273 - .matchIPDst(IpPrefix.valueOf(bgpdAddress.toRealInt(), IPV4_BIT_LENGTH)) 273 + .matchIPDst(IpPrefix.valueOf(bgpdAddress.toInt(), IPV4_BIT_LENGTH))
274 .build(); 274 .build();
275 275
276 PointToPointIntent reversedIntent = new PointToPointIntent( 276 PointToPointIntent reversedIntent = new PointToPointIntent(
......
1 +package org.onlab.onos.sdnip;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.util.Objects;
6 +
7 +import org.onlab.packet.IpAddress;
8 +import org.onlab.packet.IpPrefix;
9 +
10 +import com.google.common.base.MoreObjects;
11 +
12 +/**
13 + * Represents a route entry for an IP prefix.
14 + */
15 +public class RouteEntry {
16 + private final IpPrefix prefix; // The IP prefix
17 + private final IpAddress nextHop; // Next-hop IP address
18 +
19 + /**
20 + * Class constructor.
21 + *
22 + * @param prefix the IP prefix of the route
23 + * @param nextHop the next hop IP address for the route
24 + */
25 + public RouteEntry(IpPrefix prefix, IpAddress nextHop) {
26 + this.prefix = checkNotNull(prefix);
27 + this.nextHop = checkNotNull(nextHop);
28 + }
29 +
30 + /**
31 + * Returns the IP prefix of the route.
32 + *
33 + * @return the IP prefix of the route
34 + */
35 + public IpPrefix prefix() {
36 + return prefix;
37 + }
38 +
39 + /**
40 + * Returns the next hop IP address for the route.
41 + *
42 + * @return the next hop IP address for the route
43 + */
44 + public IpAddress nextHop() {
45 + return nextHop;
46 + }
47 +
48 + /**
49 + * Creates the binary string representation of an IPv4 prefix.
50 + * The string length is equal to the prefix length.
51 + *
52 + * @param ip4Prefix the IPv4 prefix to use
53 + * @return the binary string representation
54 + */
55 + static String createBinaryString(IpPrefix ip4Prefix) {
56 + if (ip4Prefix.prefixLength() == 0) {
57 + return "";
58 + }
59 +
60 + StringBuilder result = new StringBuilder(ip4Prefix.prefixLength());
61 + long value = ip4Prefix.toInt();
62 + for (int i = 0; i < ip4Prefix.prefixLength(); i++) {
63 + long mask = 1 << (IpAddress.MAX_INET_MASK - 1 - i);
64 + result.append(((value & mask) == 0) ? "0" : "1");
65 + }
66 + return result.toString();
67 + }
68 +
69 + @Override
70 + public boolean equals(Object other) {
71 + if (this == other) {
72 + return true;
73 + }
74 +
75 + //
76 + // NOTE: Subclasses are considered as change of identity, hence
77 + // equals() will return false if the class type doesn't match.
78 + //
79 + if (other == null || getClass() != other.getClass()) {
80 + return false;
81 + }
82 +
83 + RouteEntry otherRoute = (RouteEntry) other;
84 + return Objects.equals(this.prefix, otherRoute.prefix) &&
85 + Objects.equals(this.nextHop, otherRoute.nextHop);
86 + }
87 +
88 + @Override
89 + public int hashCode() {
90 + return Objects.hash(prefix, nextHop);
91 + }
92 +
93 + @Override
94 + public String toString() {
95 + return MoreObjects.toStringHelper(getClass())
96 + .add("prefix", prefix)
97 + .add("nextHop", nextHop)
98 + .toString();
99 + }
100 +}
1 +package org.onlab.onos.sdnip;
2 +
3 +/**
4 + * An interface to receive route updates from route providers.
5 + */
6 +public interface RouteListener {
7 + /**
8 + * Receives a route update from a route provider.
9 + *
10 + * @param routeUpdate the updated route information
11 + */
12 + public void update(RouteUpdate routeUpdate);
13 +}
1 +package org.onlab.onos.sdnip;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.util.Objects;
6 +
7 +import com.google.common.base.MoreObjects;
8 +
9 +/**
10 + * Represents a change in routing information.
11 + */
12 +public class RouteUpdate {
13 + private final Type type; // The route update type
14 + private final RouteEntry routeEntry; // The updated route entry
15 +
16 + /**
17 + * Specifies the type of a route update.
18 + * <p/>
19 + * Route updates can either provide updated information for a route, or
20 + * withdraw a previously updated route.
21 + */
22 + public enum Type {
23 + /**
24 + * The update contains updated route information for a route.
25 + */
26 + UPDATE,
27 + /**
28 + * The update withdraws the route, meaning any previous information is
29 + * no longer valid.
30 + */
31 + DELETE
32 + }
33 +
34 + /**
35 + * Class constructor.
36 + *
37 + * @param type the type of the route update
38 + * @param routeEntry the route entry with the update
39 + */
40 + public RouteUpdate(Type type, RouteEntry routeEntry) {
41 + this.type = type;
42 + this.routeEntry = checkNotNull(routeEntry);
43 + }
44 +
45 + /**
46 + * Returns the type of the route update.
47 + *
48 + * @return the type of the update
49 + */
50 + public Type type() {
51 + return type;
52 + }
53 +
54 + /**
55 + * Returns the route entry the route update is for.
56 + *
57 + * @return the route entry the route update is for
58 + */
59 + public RouteEntry routeEntry() {
60 + return routeEntry;
61 + }
62 +
63 + @Override
64 + public boolean equals(Object other) {
65 + if (other == this) {
66 + return true;
67 + }
68 +
69 + if (!(other instanceof RouteUpdate)) {
70 + return false;
71 + }
72 +
73 + RouteUpdate otherUpdate = (RouteUpdate) other;
74 +
75 + return Objects.equals(this.type, otherUpdate.type) &&
76 + Objects.equals(this.routeEntry, otherUpdate.routeEntry);
77 + }
78 +
79 + @Override
80 + public int hashCode() {
81 + return Objects.hash(type, routeEntry);
82 + }
83 +
84 + @Override
85 + public String toString() {
86 + return MoreObjects.toStringHelper(getClass())
87 + .add("type", type)
88 + .add("routeEntry", routeEntry)
89 + .toString();
90 + }
91 +}
1 +package org.onlab.onos.sdnip;
2 +
3 +import java.util.Collection;
4 +import java.util.HashMap;
5 +import java.util.HashSet;
6 +import java.util.Iterator;
7 +import java.util.LinkedList;
8 +import java.util.List;
9 +import java.util.Map;
10 +import java.util.Set;
11 +import java.util.concurrent.BlockingQueue;
12 +import java.util.concurrent.ConcurrentHashMap;
13 +import java.util.concurrent.ExecutorService;
14 +import java.util.concurrent.Executors;
15 +import java.util.concurrent.LinkedBlockingQueue;
16 +import java.util.concurrent.Semaphore;
17 +
18 +import org.apache.commons.lang3.tuple.Pair;
19 +import org.onlab.onos.net.ConnectPoint;
20 +import org.onlab.onos.net.Host;
21 +import org.onlab.onos.net.flow.DefaultTrafficSelector;
22 +import org.onlab.onos.net.flow.DefaultTrafficTreatment;
23 +import org.onlab.onos.net.flow.TrafficSelector;
24 +import org.onlab.onos.net.flow.TrafficTreatment;
25 +import org.onlab.onos.net.flow.criteria.Criteria.IPCriterion;
26 +import org.onlab.onos.net.flow.criteria.Criterion;
27 +import org.onlab.onos.net.flow.criteria.Criterion.Type;
28 +import org.onlab.onos.net.host.HostEvent;
29 +import org.onlab.onos.net.host.HostListener;
30 +import org.onlab.onos.net.host.HostService;
31 +import org.onlab.onos.net.intent.Intent;
32 +import org.onlab.onos.net.intent.IntentId;
33 +import org.onlab.onos.net.intent.IntentService;
34 +import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
35 +import org.onlab.onos.sdnip.config.BgpPeer;
36 +import org.onlab.onos.sdnip.config.Interface;
37 +import org.onlab.onos.sdnip.config.SdnIpConfigService;
38 +import org.onlab.packet.Ethernet;
39 +import org.onlab.packet.IpAddress;
40 +import org.onlab.packet.IpPrefix;
41 +import org.onlab.packet.MacAddress;
42 +import org.slf4j.Logger;
43 +import org.slf4j.LoggerFactory;
44 +
45 +import com.google.common.base.Objects;
46 +import com.google.common.collect.HashMultimap;
47 +import com.google.common.collect.Multimaps;
48 +import com.google.common.collect.SetMultimap;
49 +import com.google.common.util.concurrent.ThreadFactoryBuilder;
50 +import com.googlecode.concurrenttrees.common.KeyValuePair;
51 +import com.googlecode.concurrenttrees.radix.node.concrete.DefaultByteArrayNodeFactory;
52 +import com.googlecode.concurrenttrees.radixinverted.ConcurrentInvertedRadixTree;
53 +import com.googlecode.concurrenttrees.radixinverted.InvertedRadixTree;
54 +
55 +/**
56 + * This class processes BGP route update, translates each update into a intent
57 + * and submits the intent.
58 + *
59 + * TODO: Make it thread-safe.
60 + */
61 +public class Router implements RouteListener {
62 +
63 + private static final Logger log = LoggerFactory.getLogger(Router.class);
64 +
65 + // Store all route updates in a radix tree.
66 + // The key in this tree is the binary string of prefix of the route.
67 + private InvertedRadixTree<RouteEntry> bgpRoutes;
68 +
69 + // Stores all incoming route updates in a queue.
70 + private BlockingQueue<RouteUpdate> routeUpdates;
71 +
72 + // The Ip4Address is the next hop address of each route update.
73 + private SetMultimap<IpAddress, RouteEntry> routesWaitingOnArp;
74 + private ConcurrentHashMap<IpPrefix, MultiPointToSinglePointIntent> pushedRouteIntents;
75 +
76 + private IntentService intentService;
77 + //private IProxyArpService proxyArp;
78 + private HostService hostService;
79 + private SdnIpConfigService configInfoService;
80 + private InterfaceService interfaceService;
81 +
82 + private ExecutorService bgpUpdatesExecutor;
83 + private ExecutorService bgpIntentsSynchronizerExecutor;
84 +
85 + // TODO temporary
86 + private int intentId = Integer.MAX_VALUE / 2;
87 +
88 + //
89 + // State to deal with SDN-IP Leader election and pushing Intents
90 + //
91 + private Semaphore intentsSynchronizerSemaphore = new Semaphore(0);
92 + private volatile boolean isElectedLeader = false;
93 + private volatile boolean isActivatedLeader = false;
94 +
95 + // For routes announced by local BGP daemon in SDN network,
96 + // the next hop will be 0.0.0.0.
97 + public static final IpAddress LOCAL_NEXT_HOP = IpAddress.valueOf("0.0.0.0");
98 +
99 + /**
100 + * Class constructor.
101 + *
102 + * @param intentService the intent service
103 + * @param hostService the host service
104 + * @param configInfoService the configuration service
105 + * @param interfaceService the interface service
106 + */
107 + public Router(IntentService intentService, HostService hostService,
108 + SdnIpConfigService configInfoService, InterfaceService interfaceService) {
109 +
110 + this.intentService = intentService;
111 + this.hostService = hostService;
112 + this.configInfoService = configInfoService;
113 + this.interfaceService = interfaceService;
114 +
115 + bgpRoutes = new ConcurrentInvertedRadixTree<>(
116 + new DefaultByteArrayNodeFactory());
117 + routeUpdates = new LinkedBlockingQueue<>();
118 + routesWaitingOnArp = Multimaps.synchronizedSetMultimap(
119 + HashMultimap.<IpAddress, RouteEntry>create());
120 + pushedRouteIntents = new ConcurrentHashMap<>();
121 +
122 + bgpUpdatesExecutor = Executors.newSingleThreadExecutor(
123 + new ThreadFactoryBuilder().setNameFormat("bgp-updates-%d").build());
124 + bgpIntentsSynchronizerExecutor = Executors.newSingleThreadExecutor(
125 + new ThreadFactoryBuilder()
126 + .setNameFormat("bgp-intents-synchronizer-%d").build());
127 +
128 + this.hostService.addListener(new InternalHostListener());
129 + }
130 +
131 + /**
132 + * Starts the Router.
133 + */
134 + public void start() {
135 +
136 + bgpUpdatesExecutor.execute(new Runnable() {
137 + @Override
138 + public void run() {
139 + doUpdatesThread();
140 + }
141 + });
142 +
143 + bgpIntentsSynchronizerExecutor.execute(new Runnable() {
144 + @Override
145 + public void run() {
146 + doIntentSynchronizationThread();
147 + }
148 + });
149 + }
150 +
151 + //@Override TODO hook this up to something
152 + public void leaderChanged(boolean isLeader) {
153 + log.debug("Leader changed: {}", isLeader);
154 +
155 + if (!isLeader) {
156 + this.isElectedLeader = false;
157 + this.isActivatedLeader = false;
158 + return; // Nothing to do
159 + }
160 + this.isActivatedLeader = false;
161 + this.isElectedLeader = true;
162 +
163 + //
164 + // Tell the Intents Synchronizer thread to start the synchronization
165 + //
166 + intentsSynchronizerSemaphore.release();
167 + }
168 +
169 + @Override
170 + public void update(RouteUpdate routeUpdate) {
171 + log.debug("Received new route Update: {}", routeUpdate);
172 +
173 + try {
174 + routeUpdates.put(routeUpdate);
175 + } catch (InterruptedException e) {
176 + log.debug("Interrupted while putting on routeUpdates queue", e);
177 + Thread.currentThread().interrupt();
178 + }
179 + }
180 +
181 + /**
182 + * Thread for Intent Synchronization.
183 + */
184 + private void doIntentSynchronizationThread() {
185 + boolean interrupted = false;
186 + try {
187 + while (!interrupted) {
188 + try {
189 + intentsSynchronizerSemaphore.acquire();
190 + //
191 + // Drain all permits, because a single synchronization is
192 + // sufficient.
193 + //
194 + intentsSynchronizerSemaphore.drainPermits();
195 + } catch (InterruptedException e) {
196 + log.debug("Interrupted while waiting to become " +
197 + "Intent Synchronization leader");
198 + interrupted = true;
199 + break;
200 + }
201 + syncIntents();
202 + }
203 + } finally {
204 + if (interrupted) {
205 + Thread.currentThread().interrupt();
206 + }
207 + }
208 + }
209 +
210 + /**
211 + * Thread for handling route updates.
212 + */
213 + private void doUpdatesThread() {
214 + boolean interrupted = false;
215 + try {
216 + while (!interrupted) {
217 + try {
218 + RouteUpdate update = routeUpdates.take();
219 + switch (update.type()) {
220 + case UPDATE:
221 + processRouteAdd(update.routeEntry());
222 + break;
223 + case DELETE:
224 + processRouteDelete(update.routeEntry());
225 + break;
226 + default:
227 + log.error("Unknown update Type: {}", update.type());
228 + break;
229 + }
230 + } catch (InterruptedException e) {
231 + log.debug("Interrupted while taking from updates queue", e);
232 + interrupted = true;
233 + } catch (Exception e) {
234 + log.debug("exception", e);
235 + }
236 + }
237 + } finally {
238 + if (interrupted) {
239 + Thread.currentThread().interrupt();
240 + }
241 + }
242 + }
243 +
244 + /**
245 + * Performs Intents Synchronization between the internally stored Route
246 + * Intents and the installed Route Intents.
247 + */
248 + private void syncIntents() {
249 + synchronized (this) {
250 + if (!isElectedLeader) {
251 + return; // Nothing to do: not the leader anymore
252 + }
253 + log.debug("Syncing SDN-IP Route Intents...");
254 +
255 + Map<IpPrefix, MultiPointToSinglePointIntent> fetchedIntents =
256 + new HashMap<>();
257 +
258 + //
259 + // Fetch all intents, and classify the Multi-Point-to-Point Intents
260 + // based on the matching prefix.
261 + //
262 + for (Intent intent : intentService.getIntents()) {
263 + //
264 + // TODO: Ignore all intents that are not installed by
265 + // the SDN-IP application.
266 + //
267 + if (!(intent instanceof MultiPointToSinglePointIntent)) {
268 + continue;
269 + }
270 + MultiPointToSinglePointIntent mp2pIntent =
271 + (MultiPointToSinglePointIntent) intent;
272 + /*Match match = mp2pIntent.getMatch();
273 + if (!(match instanceof PacketMatch)) {
274 + continue;
275 + }
276 + PacketMatch packetMatch = (PacketMatch) match;
277 + Ip4Prefix prefix = packetMatch.getDstIpAddress();
278 + if (prefix == null) {
279 + continue;
280 + }
281 + fetchedIntents.put(prefix, mp2pIntent);*/
282 + for (Criterion criterion : mp2pIntent.selector().criteria()) {
283 + if (criterion.type() == Type.IPV4_DST) {
284 + IPCriterion ipCriterion = (IPCriterion) criterion;
285 + fetchedIntents.put(ipCriterion.ip(), mp2pIntent);
286 + }
287 + }
288 +
289 + }
290 +
291 + //
292 + // Compare for each prefix the local IN-MEMORY Intents with the
293 + // FETCHED Intents:
294 + // - If the IN-MEMORY Intent is same as the FETCHED Intent, store
295 + // the FETCHED Intent in the local memory (i.e., override the
296 + // IN-MEMORY Intent) to preserve the original Intent ID
297 + // - if the IN-MEMORY Intent is not same as the FETCHED Intent,
298 + // delete the FETCHED Intent, and push/install the IN-MEMORY
299 + // Intent.
300 + // - If there is an IN-MEMORY Intent for a prefix, but no FETCHED
301 + // Intent for same prefix, then push/install the IN-MEMORY
302 + // Intent.
303 + // - If there is a FETCHED Intent for a prefix, but no IN-MEMORY
304 + // Intent for same prefix, then delete/withdraw the FETCHED
305 + // Intent.
306 + //
307 + Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>>
308 + storeInMemoryIntents = new LinkedList<>();
309 + Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>>
310 + addIntents = new LinkedList<>();
311 + Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>>
312 + deleteIntents = new LinkedList<>();
313 + for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry :
314 + pushedRouteIntents.entrySet()) {
315 + IpPrefix prefix = entry.getKey();
316 + MultiPointToSinglePointIntent inMemoryIntent =
317 + entry.getValue();
318 + MultiPointToSinglePointIntent fetchedIntent =
319 + fetchedIntents.get(prefix);
320 +
321 + if (fetchedIntent == null) {
322 + //
323 + // No FETCHED Intent for same prefix: push the IN-MEMORY
324 + // Intent.
325 + //
326 + addIntents.add(Pair.of(prefix, inMemoryIntent));
327 + continue;
328 + }
329 +
330 + //
331 + // If IN-MEMORY Intent is same as the FETCHED Intent,
332 + // store the FETCHED Intent in the local memory.
333 + //
334 + if (compareMultiPointToSinglePointIntents(inMemoryIntent,
335 + fetchedIntent)) {
336 + storeInMemoryIntents.add(Pair.of(prefix, fetchedIntent));
337 + } else {
338 + //
339 + // The IN-MEMORY Intent is not same as the FETCHED Intent,
340 + // hence delete the FETCHED Intent, and install the
341 + // IN-MEMORY Intent.
342 + //
343 + deleteIntents.add(Pair.of(prefix, fetchedIntent));
344 + addIntents.add(Pair.of(prefix, inMemoryIntent));
345 + }
346 + fetchedIntents.remove(prefix);
347 + }
348 +
349 + //
350 + // Any remaining FETCHED Intents have to be deleted/withdrawn
351 + //
352 + for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry :
353 + fetchedIntents.entrySet()) {
354 + IpPrefix prefix = entry.getKey();
355 + MultiPointToSinglePointIntent fetchedIntent = entry.getValue();
356 + deleteIntents.add(Pair.of(prefix, fetchedIntent));
357 + }
358 +
359 + //
360 + // Perform the actions:
361 + // 1. Store in memory fetched intents that are same. Can be done
362 + // even if we are not the leader anymore
363 + // 2. Delete intents: check if the leader before each operation
364 + // 3. Add intents: check if the leader before each operation
365 + //
366 + for (Pair<IpPrefix, MultiPointToSinglePointIntent> pair :
367 + storeInMemoryIntents) {
368 + IpPrefix prefix = pair.getLeft();
369 + MultiPointToSinglePointIntent intent = pair.getRight();
370 + log.debug("Intent synchronization: updating in-memory " +
371 + "Intent for prefix: {}", prefix);
372 + pushedRouteIntents.put(prefix, intent);
373 + }
374 + //
375 + isActivatedLeader = true; // Allow push of Intents
376 + for (Pair<IpPrefix, MultiPointToSinglePointIntent> pair :
377 + deleteIntents) {
378 + IpPrefix prefix = pair.getLeft();
379 + MultiPointToSinglePointIntent intent = pair.getRight();
380 + if (!isElectedLeader) {
381 + isActivatedLeader = false;
382 + return;
383 + }
384 + log.debug("Intent synchronization: deleting Intent for " +
385 + "prefix: {}", prefix);
386 + intentService.withdraw(intent);
387 + }
388 + //
389 + for (Pair<IpPrefix, MultiPointToSinglePointIntent> pair :
390 + addIntents) {
391 + IpPrefix prefix = pair.getLeft();
392 + MultiPointToSinglePointIntent intent = pair.getRight();
393 + if (!isElectedLeader) {
394 + isActivatedLeader = false;
395 + return;
396 + }
397 + log.debug("Intent synchronization: adding Intent for " +
398 + "prefix: {}", prefix);
399 + intentService.submit(intent);
400 + }
401 + if (!isElectedLeader) {
402 + isActivatedLeader = false;
403 + }
404 + log.debug("Syncing SDN-IP routes completed.");
405 + }
406 + }
407 +
408 + /**
409 + * Compares two Multi-point to Single Point Intents whether they represent
410 + * same logical intention.
411 + *
412 + * @param intent1 the first Intent to compare
413 + * @param intent2 the second Intent to compare
414 + * @return true if both Intents represent same logical intention, otherwise
415 + * false
416 + */
417 + private boolean compareMultiPointToSinglePointIntents(
418 + MultiPointToSinglePointIntent intent1,
419 + MultiPointToSinglePointIntent intent2) {
420 + /*Match match1 = intent1.getMatch();
421 + Match match2 = intent2.getMatch();
422 + Action action1 = intent1.getAction();
423 + Action action2 = intent2.getAction();
424 + Set<SwitchPort> ingressPorts1 = intent1.getIngressPorts();
425 + Set<SwitchPort> ingressPorts2 = intent2.getIngressPorts();
426 + SwitchPort egressPort1 = intent1.getEgressPort();
427 + SwitchPort egressPort2 = intent2.getEgressPort();
428 +
429 + return Objects.equal(match1, match2) &&
430 + Objects.equal(action1, action2) &&
431 + Objects.equal(egressPort1, egressPort2) &&
432 + Objects.equal(ingressPorts1, ingressPorts2);*/
433 + return Objects.equal(intent1.selector(), intent2.selector()) &&
434 + Objects.equal(intent1.treatment(), intent2.treatment()) &&
435 + Objects.equal(intent1.ingressPoints(), intent2.ingressPoints()) &&
436 + Objects.equal(intent1.egressPoint(), intent2.egressPoint());
437 + }
438 +
439 + /**
440 + * Processes adding a route entry.
441 + * <p/>
442 + * Put new route entry into the radix tree. If there was an existing
443 + * next hop for this prefix, but the next hop was different, then execute
444 + * deleting old route entry. If the next hop is the SDN domain, we do not
445 + * handle it at the moment. Otherwise, execute adding a route.
446 + *
447 + * @param routeEntry the route entry to add
448 + */
449 + protected void processRouteAdd(RouteEntry routeEntry) {
450 + synchronized (this) {
451 + log.debug("Processing route add: {}", routeEntry);
452 +
453 + IpPrefix prefix = routeEntry.prefix();
454 + IpAddress nextHop = null;
455 + RouteEntry foundRouteEntry =
456 + bgpRoutes.put(RouteEntry.createBinaryString(prefix),
457 + routeEntry);
458 + if (foundRouteEntry != null) {
459 + nextHop = foundRouteEntry.nextHop();
460 + }
461 +
462 + if (nextHop != null && !nextHop.equals(routeEntry.nextHop())) {
463 + // There was an existing nexthop for this prefix. This update
464 + // supersedes that, so we need to remove the old flows for this
465 + // prefix from the switches
466 + executeRouteDelete(routeEntry);
467 + }
468 + if (nextHop != null && nextHop.equals(routeEntry.nextHop())) {
469 + return;
470 + }
471 +
472 + if (routeEntry.nextHop().equals(LOCAL_NEXT_HOP)) {
473 + // Route originated by SDN domain
474 + // We don't handle these at the moment
475 + log.debug("Own route {} to {}",
476 + routeEntry.prefix(), routeEntry.nextHop());
477 + return;
478 + }
479 +
480 + executeRouteAdd(routeEntry);
481 + }
482 + }
483 +
484 + /**
485 + * Executes adding a route entry.
486 + * <p/>
487 + * Find out the egress Interface and MAC address of next hop router for
488 + * this route entry. If the MAC address can not be found in ARP cache,
489 + * then this prefix will be put in routesWaitingOnArp queue. Otherwise,
490 + * new route intent will be created and installed.
491 + *
492 + * @param routeEntry the route entry to add
493 + */
494 + private void executeRouteAdd(RouteEntry routeEntry) {
495 + log.debug("Executing route add: {}", routeEntry);
496 +
497 + // See if we know the MAC address of the next hop
498 + //MacAddress nextHopMacAddress =
499 + //proxyArp.getMacAddress(routeEntry.getNextHop());
500 + MacAddress nextHopMacAddress = null;
501 + Set<Host> hosts = hostService.getHostsByIp(
502 + routeEntry.nextHop().toPrefix());
503 + if (!hosts.isEmpty()) {
504 + // TODO how to handle if multiple hosts are returned?
505 + nextHopMacAddress = hosts.iterator().next().mac();
506 + }
507 +
508 + if (nextHopMacAddress == null) {
509 + routesWaitingOnArp.put(routeEntry.nextHop(), routeEntry);
510 + //proxyArp.sendArpRequest(routeEntry.getNextHop(), this, true);
511 + // TODO maybe just do this for every prefix anyway
512 + hostService.startMonitoringIp(routeEntry.nextHop());
513 + return;
514 + }
515 +
516 + addRouteIntentToNextHop(routeEntry.prefix(),
517 + routeEntry.nextHop(),
518 + nextHopMacAddress);
519 + }
520 +
521 + /**
522 + * Adds a route intent given a prefix and a next hop IP address. This
523 + * method will find the egress interface for the intent.
524 + *
525 + * @param prefix IP prefix of the route to add
526 + * @param nextHopIpAddress IP address of the next hop
527 + * @param nextHopMacAddress MAC address of the next hop
528 + */
529 + private void addRouteIntentToNextHop(IpPrefix prefix,
530 + IpAddress nextHopIpAddress,
531 + MacAddress nextHopMacAddress) {
532 +
533 + // Find the attachment point (egress interface) of the next hop
534 + Interface egressInterface;
535 + if (configInfoService.getBgpPeers().containsKey(nextHopIpAddress)) {
536 + // Route to a peer
537 + log.debug("Route to peer {}", nextHopIpAddress);
538 + BgpPeer peer =
539 + configInfoService.getBgpPeers().get(nextHopIpAddress);
540 + egressInterface =
541 + interfaceService.getInterface(peer.connectPoint());
542 + } else {
543 + // Route to non-peer
544 + log.debug("Route to non-peer {}", nextHopIpAddress);
545 + egressInterface =
546 + interfaceService.getMatchingInterface(nextHopIpAddress);
547 + if (egressInterface == null) {
548 + log.warn("No outgoing interface found for {}",
549 + nextHopIpAddress);
550 + return;
551 + }
552 + }
553 +
554 + doAddRouteIntent(prefix, egressInterface, nextHopMacAddress);
555 + }
556 +
557 + /**
558 + * Installs a route intent for a prefix.
559 + * <p/>
560 + * Intent will match dst IP prefix and rewrite dst MAC address at all other
561 + * border switches, then forward packets according to dst MAC address.
562 + *
563 + * @param prefix IP prefix from route
564 + * @param egressInterface egress Interface connected to next hop router
565 + * @param nextHopMacAddress MAC address of next hop router
566 + */
567 + private void doAddRouteIntent(IpPrefix prefix, Interface egressInterface,
568 + MacAddress nextHopMacAddress) {
569 + log.debug("Adding intent for prefix {}, next hop mac {}",
570 + prefix, nextHopMacAddress);
571 +
572 + MultiPointToSinglePointIntent pushedIntent =
573 + pushedRouteIntents.get(prefix);
574 +
575 + // Just for testing.
576 + if (pushedIntent != null) {
577 + log.error("There should not be a pushed intent: {}", pushedIntent);
578 + }
579 +
580 + ConnectPoint egressPort = egressInterface.connectPoint();
581 +
582 + Set<ConnectPoint> ingressPorts = new HashSet<>();
583 +
584 + for (Interface intf : interfaceService.getInterfaces()) {
585 + if (!intf.equals(egressInterface)) {
586 + ConnectPoint srcPort = intf.connectPoint();
587 + ingressPorts.add(srcPort);
588 + }
589 + }
590 +
591 + // Match the destination IP prefix at the first hop
592 + //PacketMatchBuilder builder = new PacketMatchBuilder();
593 + //builder.setEtherType(Ethernet.TYPE_IPV4).setDstIpNet(prefix);
594 + //PacketMatch packetMatch = builder.build();
595 + TrafficSelector selector = DefaultTrafficSelector.builder()
596 + .matchEthType(Ethernet.TYPE_IPV4)
597 + .matchIPDst(prefix)
598 + .build();
599 +
600 + // Rewrite the destination MAC address
601 + //ModifyDstMacAction modifyDstMacAction =
602 + //new ModifyDstMacAction(nextHopMacAddress);
603 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
604 + .setEthDst(nextHopMacAddress)
605 + .build();
606 +
607 + MultiPointToSinglePointIntent intent =
608 + new MultiPointToSinglePointIntent(nextIntentId(),
609 + selector, treatment, ingressPorts, egressPort);
610 +
611 + if (isElectedLeader && isActivatedLeader) {
612 + log.debug("Intent installation: adding Intent for prefix: {}",
613 + prefix);
614 + intentService.submit(intent);
615 + }
616 +
617 + // Maintain the Intent
618 + pushedRouteIntents.put(prefix, intent);
619 + }
620 +
621 + /**
622 + * Executes deleting a route entry.
623 + * <p/>
624 + * Removes prefix from radix tree, and if successful, then try to delete
625 + * the related intent.
626 + *
627 + * @param routeEntry the route entry to delete
628 + */
629 + protected void processRouteDelete(RouteEntry routeEntry) {
630 + synchronized (this) {
631 + log.debug("Processing route delete: {}", routeEntry);
632 + IpPrefix prefix = routeEntry.prefix();
633 +
634 + // TODO check the change of logic here - remove doesn't check that
635 + // the route entry was what we expected (and we can't do this
636 + // concurrently)
637 +
638 + if (bgpRoutes.remove(RouteEntry.createBinaryString(prefix))) {
639 + //
640 + // Only delete flows if an entry was actually removed from the
641 + // tree. If no entry was removed, the <prefix, nexthop> wasn't
642 + // there so it's probably already been removed and we don't
643 + // need to do anything.
644 + //
645 + executeRouteDelete(routeEntry);
646 + }
647 +
648 + routesWaitingOnArp.remove(routeEntry.nextHop(), routeEntry);
649 + // TODO cancel the request in the ARP manager as well
650 + }
651 + }
652 +
653 + /**
654 + * Executed deleting a route entry.
655 + *
656 + * @param routeEntry the route entry to delete
657 + */
658 + private void executeRouteDelete(RouteEntry routeEntry) {
659 + log.debug("Executing route delete: {}", routeEntry);
660 +
661 + IpPrefix prefix = routeEntry.prefix();
662 +
663 + MultiPointToSinglePointIntent intent =
664 + pushedRouteIntents.remove(prefix);
665 +
666 + if (intent == null) {
667 + log.debug("There is no intent in pushedRouteIntents to delete " +
668 + "for prefix: {}", prefix);
669 + } else {
670 + if (isElectedLeader && isActivatedLeader) {
671 + log.debug("Intent installation: deleting Intent for prefix: {}",
672 + prefix);
673 + intentService.withdraw(intent);
674 + }
675 + }
676 + }
677 +
678 + /**
679 + * This method handles the prefixes which are waiting for ARP replies for
680 + * MAC addresses of next hops.
681 + *
682 + * @param ipAddress next hop router IP address, for which we sent ARP
683 + * request out
684 + * @param macAddress MAC address which is relative to the ipAddress
685 + */
686 + //@Override
687 + // TODO change name
688 + public void arpResponse(IpAddress ipAddress, MacAddress macAddress) {
689 + log.debug("Received ARP response: {} => {}", ipAddress, macAddress);
690 +
691 + // We synchronize on this to prevent changes to the radix tree
692 + // while we're pushing intents. If the tree changes, the
693 + // tree and intents could get out of sync.
694 + synchronized (this) {
695 +
696 + Set<RouteEntry> routesToPush =
697 + routesWaitingOnArp.removeAll(ipAddress);
698 +
699 + for (RouteEntry routeEntry : routesToPush) {
700 + // These will always be adds
701 + IpPrefix prefix = routeEntry.prefix();
702 + String binaryString = RouteEntry.createBinaryString(prefix);
703 + RouteEntry foundRouteEntry =
704 + bgpRoutes.getValueForExactKey(binaryString);
705 + if (foundRouteEntry != null &&
706 + foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
707 + log.debug("Pushing prefix {} next hop {}",
708 + routeEntry.prefix(), routeEntry.nextHop());
709 + // We only push prefix flows if the prefix is still in the
710 + // radix tree and the next hop is the same as our
711 + // update.
712 + // The prefix could have been removed while we were waiting
713 + // for the ARP, or the next hop could have changed.
714 + addRouteIntentToNextHop(prefix, ipAddress, macAddress);
715 + } else {
716 + log.debug("Received ARP response, but {}/{} is no longer in"
717 + + " the radix tree", routeEntry.prefix(),
718 + routeEntry.nextHop());
719 + }
720 + }
721 + }
722 + }
723 +
724 + /**
725 + * Gets the SDN-IP routes.
726 + *
727 + * @return the SDN-IP routes
728 + */
729 + public Collection<RouteEntry> getRoutes() {
730 + Iterator<KeyValuePair<RouteEntry>> it =
731 + bgpRoutes.getKeyValuePairsForKeysStartingWith("").iterator();
732 +
733 + List<RouteEntry> routes = new LinkedList<>();
734 +
735 + while (it.hasNext()) {
736 + KeyValuePair<RouteEntry> entry = it.next();
737 + routes.add(entry.getValue());
738 + }
739 +
740 + return routes;
741 + }
742 +
743 + /**
744 + * Generates a new unique intent ID.
745 + *
746 + * @return the new intent ID.
747 + */
748 + private IntentId nextIntentId() {
749 + return new IntentId(intentId++);
750 + }
751 +
752 + /**
753 + * Listener for host events.
754 + */
755 + class InternalHostListener implements HostListener {
756 + @Override
757 + public void event(HostEvent event) {
758 + if (event.type() == HostEvent.Type.HOST_ADDED ||
759 + event.type() == HostEvent.Type.HOST_UPDATED) {
760 + Host host = event.subject();
761 + for (IpPrefix ip : host.ipAddresses()) {
762 + arpResponse(ip.toIpAddress(), host.mac());
763 + }
764 + }
765 + }
766 + }
767 +}
...@@ -2,21 +2,30 @@ package org.onlab.onos.sdnip; ...@@ -2,21 +2,30 @@ package org.onlab.onos.sdnip;
2 2
3 import static org.slf4j.LoggerFactory.getLogger; 3 import static org.slf4j.LoggerFactory.getLogger;
4 4
5 +import java.util.Collection;
6 +
5 import org.apache.felix.scr.annotations.Activate; 7 import org.apache.felix.scr.annotations.Activate;
6 import org.apache.felix.scr.annotations.Component; 8 import org.apache.felix.scr.annotations.Component;
7 import org.apache.felix.scr.annotations.Deactivate; 9 import org.apache.felix.scr.annotations.Deactivate;
8 import org.apache.felix.scr.annotations.Reference; 10 import org.apache.felix.scr.annotations.Reference;
9 import org.apache.felix.scr.annotations.ReferenceCardinality; 11 import org.apache.felix.scr.annotations.ReferenceCardinality;
12 +import org.apache.felix.scr.annotations.Service;
10 import org.onlab.onos.net.host.HostService; 13 import org.onlab.onos.net.host.HostService;
11 import org.onlab.onos.net.intent.IntentService; 14 import org.onlab.onos.net.intent.IntentService;
15 +import org.onlab.onos.sdnip.RouteUpdate.Type;
16 +import org.onlab.onos.sdnip.bgp.BgpRouteEntry;
17 +import org.onlab.onos.sdnip.bgp.BgpSessionManager;
12 import org.onlab.onos.sdnip.config.SdnIpConfigReader; 18 import org.onlab.onos.sdnip.config.SdnIpConfigReader;
19 +import org.onlab.packet.IpAddress;
20 +import org.onlab.packet.IpPrefix;
13 import org.slf4j.Logger; 21 import org.slf4j.Logger;
14 22
15 /** 23 /**
16 - * Placeholder SDN-IP component. 24 + * Component for the SDN-IP peering application.
17 */ 25 */
18 @Component(immediate = true) 26 @Component(immediate = true)
19 -public class SdnIp { 27 +@Service
28 +public class SdnIp implements SdnIpService {
20 29
21 private final Logger log = getLogger(getClass()); 30 private final Logger log = getLogger(getClass());
22 31
...@@ -28,6 +37,8 @@ public class SdnIp { ...@@ -28,6 +37,8 @@ public class SdnIp {
28 37
29 private SdnIpConfigReader config; 38 private SdnIpConfigReader config;
30 private PeerConnectivity peerConnectivity; 39 private PeerConnectivity peerConnectivity;
40 + private Router router;
41 + private BgpSessionManager bgpSessionManager;
31 42
32 @Activate 43 @Activate
33 protected void activate() { 44 protected void activate() {
...@@ -41,10 +52,31 @@ public class SdnIp { ...@@ -41,10 +52,31 @@ public class SdnIp {
41 peerConnectivity = new PeerConnectivity(config, interfaceService, intentService); 52 peerConnectivity = new PeerConnectivity(config, interfaceService, intentService);
42 peerConnectivity.start(); 53 peerConnectivity.start();
43 54
55 + router = new Router(intentService, hostService, config, interfaceService);
56 + router.start();
57 +
58 + bgpSessionManager = new BgpSessionManager(router);
59 + bgpSessionManager.startUp(2000); // TODO
60 +
61 + // TODO need to disable link discovery on external ports
62 +
63 + router.update(new RouteUpdate(Type.UPDATE, new RouteEntry(
64 + IpPrefix.valueOf("172.16.20.0/24"),
65 + IpAddress.valueOf("192.168.10.1"))));
44 } 66 }
45 67
46 @Deactivate 68 @Deactivate
47 protected void deactivate() { 69 protected void deactivate() {
48 log.info("Stopped"); 70 log.info("Stopped");
49 } 71 }
72 +
73 + @Override
74 + public Collection<BgpRouteEntry> getBgpRoutes() {
75 + return bgpSessionManager.getBgpRoutes();
76 + }
77 +
78 + @Override
79 + public Collection<RouteEntry> getRoutes() {
80 + return router.getRoutes();
81 + }
50 } 82 }
......
1 +package org.onlab.onos.sdnip;
2 +
3 +import java.util.Collection;
4 +
5 +import org.onlab.onos.sdnip.bgp.BgpRouteEntry;
6 +
7 +/**
8 + * Service interface exported by SDN-IP.
9 + */
10 +public interface SdnIpService {
11 + /**
12 + * Gets the BGP routes.
13 + *
14 + * @return the BGP routes
15 + */
16 + public Collection<BgpRouteEntry> getBgpRoutes();
17 +
18 + /**
19 + * Gets all the routes known to SDN-IP.
20 + *
21 + * @return the SDN-IP routes
22 + */
23 + public Collection<RouteEntry> getRoutes();
24 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +/**
4 + * BGP related constants.
5 + */
6 +public final class BgpConstants {
7 + /**
8 + * Default constructor.
9 + * <p>
10 + * The constructor is private to prevent creating an instance of
11 + * this utility class.
12 + */
13 + private BgpConstants() {
14 + }
15 +
16 + /** BGP port number (RFC 4271). */
17 + public static final int BGP_PORT = 179;
18 +
19 + /** BGP version. */
20 + public static final int BGP_VERSION = 4;
21 +
22 + /** BGP OPEN message type. */
23 + public static final int BGP_TYPE_OPEN = 1;
24 +
25 + /** BGP UPDATE message type. */
26 + public static final int BGP_TYPE_UPDATE = 2;
27 +
28 + /** BGP NOTIFICATION message type. */
29 + public static final int BGP_TYPE_NOTIFICATION = 3;
30 +
31 + /** BGP KEEPALIVE message type. */
32 + public static final int BGP_TYPE_KEEPALIVE = 4;
33 +
34 + /** BGP Header Marker field length. */
35 + public static final int BGP_HEADER_MARKER_LENGTH = 16;
36 +
37 + /** BGP Header length. */
38 + public static final int BGP_HEADER_LENGTH = 19;
39 +
40 + /** BGP message maximum length. */
41 + public static final int BGP_MESSAGE_MAX_LENGTH = 4096;
42 +
43 + /** BGP OPEN message minimum length (BGP Header included). */
44 + public static final int BGP_OPEN_MIN_LENGTH = 29;
45 +
46 + /** BGP UPDATE message minimum length (BGP Header included). */
47 + public static final int BGP_UPDATE_MIN_LENGTH = 23;
48 +
49 + /** BGP NOTIFICATION message minimum length (BGP Header included). */
50 + public static final int BGP_NOTIFICATION_MIN_LENGTH = 21;
51 +
52 + /** BGP KEEPALIVE message expected length (BGP Header included). */
53 + public static final int BGP_KEEPALIVE_EXPECTED_LENGTH = 19;
54 +
55 + /** BGP KEEPALIVE messages transmitted per Hold interval. */
56 + public static final int BGP_KEEPALIVE_PER_HOLD_INTERVAL = 3;
57 +
58 + /** BGP KEEPALIVE messages minimum Holdtime (in seconds). */
59 + public static final int BGP_KEEPALIVE_MIN_HOLDTIME = 3;
60 +
61 + /** BGP KEEPALIVE messages minimum transmission interval (in seconds). */
62 + public static final int BGP_KEEPALIVE_MIN_INTERVAL = 1;
63 +
64 + /** BGP AS 0 (zero) value. See draft-ietf-idr-as0-06.txt Internet Draft. */
65 + public static final long BGP_AS_0 = 0;
66 +
67 + /**
68 + * BGP UPDATE related constants.
69 + */
70 + public static final class Update {
71 + /**
72 + * Default constructor.
73 + * <p>
74 + * The constructor is private to prevent creating an instance of
75 + * this utility class.
76 + */
77 + private Update() {
78 + }
79 +
80 + /**
81 + * BGP UPDATE: ORIGIN related constants.
82 + */
83 + public static final class Origin {
84 + /**
85 + * Default constructor.
86 + * <p>
87 + * The constructor is private to prevent creating an instance of
88 + * this utility class.
89 + */
90 + private Origin() {
91 + }
92 +
93 + /** BGP UPDATE Attributes Type Code ORIGIN. */
94 + public static final int TYPE = 1;
95 +
96 + /** BGP UPDATE Attributes Type Code ORIGIN length. */
97 + public static final int LENGTH = 1;
98 +
99 + /** BGP UPDATE ORIGIN: IGP. */
100 + public static final int IGP = 0;
101 +
102 + /** BGP UPDATE ORIGIN: EGP. */
103 + public static final int EGP = 1;
104 +
105 + /** BGP UPDATE ORIGIN: INCOMPLETE. */
106 + public static final int INCOMPLETE = 2;
107 + }
108 +
109 + /**
110 + * BGP UPDATE: AS_PATH related constants.
111 + */
112 + public static final class AsPath {
113 + /**
114 + * Default constructor.
115 + * <p>
116 + * The constructor is private to prevent creating an instance of
117 + * this utility class.
118 + */
119 + private AsPath() {
120 + }
121 +
122 + /** BGP UPDATE Attributes Type Code AS_PATH. */
123 + public static final int TYPE = 2;
124 +
125 + /** BGP UPDATE AS_PATH Type: AS_SET. */
126 + public static final int AS_SET = 1;
127 +
128 + /** BGP UPDATE AS_PATH Type: AS_SEQUENCE. */
129 + public static final int AS_SEQUENCE = 2;
130 + }
131 +
132 + /**
133 + * BGP UPDATE: NEXT_HOP related constants.
134 + */
135 + public static final class NextHop {
136 + /**
137 + * Default constructor.
138 + * <p>
139 + * The constructor is private to prevent creating an instance of
140 + * this utility class.
141 + */
142 + private NextHop() {
143 + }
144 +
145 + /** BGP UPDATE Attributes Type Code NEXT_HOP. */
146 + public static final int TYPE = 3;
147 +
148 + /** BGP UPDATE Attributes Type Code NEXT_HOP length. */
149 + public static final int LENGTH = 4;
150 + }
151 +
152 + /**
153 + * BGP UPDATE: MULTI_EXIT_DISC related constants.
154 + */
155 + public static final class MultiExitDisc {
156 + /**
157 + * Default constructor.
158 + * <p>
159 + * The constructor is private to prevent creating an instance of
160 + * this utility class.
161 + */
162 + private MultiExitDisc() {
163 + }
164 +
165 + /** BGP UPDATE Attributes Type Code MULTI_EXIT_DISC. */
166 + public static final int TYPE = 4;
167 +
168 + /** BGP UPDATE Attributes Type Code MULTI_EXIT_DISC length. */
169 + public static final int LENGTH = 4;
170 +
171 + /** BGP UPDATE Attributes lowest MULTI_EXIT_DISC value. */
172 + public static final int LOWEST_MULTI_EXIT_DISC = 0;
173 + }
174 +
175 + /**
176 + * BGP UPDATE: LOCAL_PREF related constants.
177 + */
178 + public static final class LocalPref {
179 + /**
180 + * Default constructor.
181 + * <p>
182 + * The constructor is private to prevent creating an instance of
183 + * this utility class.
184 + */
185 + private LocalPref() {
186 + }
187 +
188 + /** BGP UPDATE Attributes Type Code LOCAL_PREF. */
189 + public static final int TYPE = 5;
190 +
191 + /** BGP UPDATE Attributes Type Code LOCAL_PREF length. */
192 + public static final int LENGTH = 4;
193 + }
194 +
195 + /**
196 + * BGP UPDATE: ATOMIC_AGGREGATE related constants.
197 + */
198 + public static final class AtomicAggregate {
199 + /**
200 + * Default constructor.
201 + * <p>
202 + * The constructor is private to prevent creating an instance of
203 + * this utility class.
204 + */
205 + private AtomicAggregate() {
206 + }
207 +
208 + /** BGP UPDATE Attributes Type Code ATOMIC_AGGREGATE. */
209 + public static final int TYPE = 6;
210 +
211 + /** BGP UPDATE Attributes Type Code ATOMIC_AGGREGATE length. */
212 + public static final int LENGTH = 0;
213 + }
214 +
215 + /**
216 + * BGP UPDATE: AGGREGATOR related constants.
217 + */
218 + public static final class Aggregator {
219 + /**
220 + * Default constructor.
221 + * <p>
222 + * The constructor is private to prevent creating an instance of
223 + * this utility class.
224 + */
225 + private Aggregator() {
226 + }
227 +
228 + /** BGP UPDATE Attributes Type Code AGGREGATOR. */
229 + public static final int TYPE = 7;
230 +
231 + /** BGP UPDATE Attributes Type Code AGGREGATOR length. */
232 + public static final int LENGTH = 6;
233 + }
234 + }
235 +
236 + /**
237 + * BGP NOTIFICATION related constants.
238 + */
239 + public static final class Notifications {
240 + /**
241 + * Default constructor.
242 + * <p>
243 + * The constructor is private to prevent creating an instance of
244 + * this utility class.
245 + */
246 + private Notifications() {
247 + }
248 +
249 + /**
250 + * BGP NOTIFICATION: Message Header Error constants.
251 + */
252 + public static final class MessageHeaderError {
253 + /**
254 + * Default constructor.
255 + * <p>
256 + * The constructor is private to prevent creating an instance of
257 + * this utility class.
258 + */
259 + private MessageHeaderError() {
260 + }
261 +
262 + /** Message Header Error code. */
263 + public static final int ERROR_CODE = 1;
264 +
265 + /** Message Header Error subcode: Connection Not Synchronized. */
266 + public static final int CONNECTION_NOT_SYNCHRONIZED = 1;
267 +
268 + /** Message Header Error subcode: Bad Message Length. */
269 + public static final int BAD_MESSAGE_LENGTH = 2;
270 +
271 + /** Message Header Error subcode: Bad Message Type. */
272 + public static final int BAD_MESSAGE_TYPE = 3;
273 + }
274 +
275 + /**
276 + * BGP NOTIFICATION: OPEN Message Error constants.
277 + */
278 + public static final class OpenMessageError {
279 + /**
280 + * Default constructor.
281 + * <p>
282 + * The constructor is private to prevent creating an instance of
283 + * this utility class.
284 + */
285 + private OpenMessageError() {
286 + }
287 +
288 + /** OPEN Message Error code. */
289 + public static final int ERROR_CODE = 2;
290 +
291 + /** OPEN Message Error subcode: Unsupported Version Number. */
292 + public static final int UNSUPPORTED_VERSION_NUMBER = 1;
293 +
294 + /** OPEN Message Error subcode: Bad PEER AS. */
295 + public static final int BAD_PEER_AS = 2;
296 +
297 + /** OPEN Message Error subcode: Unacceptable Hold Time. */
298 + public static final int UNACCEPTABLE_HOLD_TIME = 6;
299 + }
300 +
301 + /**
302 + * BGP NOTIFICATION: UPDATE Message Error constants.
303 + */
304 + public static final class UpdateMessageError {
305 + /**
306 + * Default constructor.
307 + * <p>
308 + * The constructor is private to prevent creating an instance of
309 + * this utility class.
310 + */
311 + private UpdateMessageError() {
312 + }
313 +
314 + /** UPDATE Message Error code. */
315 + public static final int ERROR_CODE = 3;
316 +
317 + /** UPDATE Message Error subcode: Malformed Attribute List. */
318 + public static final int MALFORMED_ATTRIBUTE_LIST = 1;
319 +
320 + /** UPDATE Message Error subcode: Unrecognized Well-known Attribute. */
321 + public static final int UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE = 2;
322 +
323 + /** UPDATE Message Error subcode: Missing Well-known Attribute. */
324 + public static final int MISSING_WELL_KNOWN_ATTRIBUTE = 3;
325 +
326 + /** UPDATE Message Error subcode: Attribute Flags Error. */
327 + public static final int ATTRIBUTE_FLAGS_ERROR = 4;
328 +
329 + /** UPDATE Message Error subcode: Attribute Length Error. */
330 + public static final int ATTRIBUTE_LENGTH_ERROR = 5;
331 +
332 + /** UPDATE Message Error subcode: Invalid ORIGIN Attribute. */
333 + public static final int INVALID_ORIGIN_ATTRIBUTE = 6;
334 +
335 + /** UPDATE Message Error subcode: Invalid NEXT_HOP Attribute. */
336 + public static final int INVALID_NEXT_HOP_ATTRIBUTE = 8;
337 +
338 + /** UPDATE Message Error subcode: Optional Attribute Error. Unused. */
339 + public static final int OPTIONAL_ATTRIBUTE_ERROR = 9;
340 +
341 + /** UPDATE Message Error subcode: Invalid Network Field. */
342 + public static final int INVALID_NETWORK_FIELD = 10;
343 +
344 + /** UPDATE Message Error subcode: Malformed AS_PATH. */
345 + public static final int MALFORMED_AS_PATH = 11;
346 + }
347 +
348 + /**
349 + * BGP NOTIFICATION: Hold Timer Expired constants.
350 + */
351 + public static final class HoldTimerExpired {
352 + /**
353 + * Default constructor.
354 + * <p>
355 + * The constructor is private to prevent creating an instance of
356 + * this utility class.
357 + */
358 + private HoldTimerExpired() {
359 + }
360 +
361 + /** Hold Timer Expired code. */
362 + public static final int ERROR_CODE = 4;
363 + }
364 +
365 + /** BGP NOTIFICATION message Error subcode: Unspecific. */
366 + public static final int ERROR_SUBCODE_UNSPECIFIC = 0;
367 + }
368 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import org.jboss.netty.buffer.ChannelBuffer;
4 +import org.jboss.netty.buffer.ChannelBuffers;
5 +import org.jboss.netty.channel.Channel;
6 +import org.jboss.netty.channel.ChannelHandlerContext;
7 +import org.jboss.netty.handler.codec.frame.FrameDecoder;
8 +import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.MessageHeaderError;
9 +import org.slf4j.Logger;
10 +import org.slf4j.LoggerFactory;
11 +
12 +/**
13 + * Class for handling the decoding of the BGP messages.
14 + */
15 +class BgpFrameDecoder extends FrameDecoder {
16 + private static final Logger log =
17 + LoggerFactory.getLogger(BgpFrameDecoder.class);
18 +
19 + private final BgpSession bgpSession;
20 +
21 + /**
22 + * Constructor for a given BGP Session.
23 + *
24 + * @param bgpSession the BGP session state to use.
25 + */
26 + BgpFrameDecoder(BgpSession bgpSession) {
27 + this.bgpSession = bgpSession;
28 + }
29 +
30 + @Override
31 + protected Object decode(ChannelHandlerContext ctx,
32 + Channel channel,
33 + ChannelBuffer buf) throws Exception {
34 + //
35 + // NOTE: If we close the channel during the decoding, we might still
36 + // see some incoming messages while the channel closing is completed.
37 + //
38 + if (bgpSession.isClosed()) {
39 + return null;
40 + }
41 +
42 + log.trace("BGP Peer: decode(): remoteAddr = {} localAddr = {} " +
43 + "messageSize = {}",
44 + ctx.getChannel().getRemoteAddress(),
45 + ctx.getChannel().getLocalAddress(),
46 + buf.readableBytes());
47 +
48 + // Test for minimum length of the BGP message
49 + if (buf.readableBytes() < BgpConstants.BGP_HEADER_LENGTH) {
50 + // No enough data received
51 + return null;
52 + }
53 +
54 + //
55 + // Mark the current buffer position in case we haven't received
56 + // the whole message.
57 + //
58 + buf.markReaderIndex();
59 +
60 + //
61 + // Read and check the BGP message Marker field: it must be all ones
62 + // (See RFC 4271, Section 4.1)
63 + //
64 + byte[] marker = new byte[BgpConstants.BGP_HEADER_MARKER_LENGTH];
65 + buf.readBytes(marker);
66 + for (int i = 0; i < marker.length; i++) {
67 + if (marker[i] != (byte) 0xff) {
68 + log.debug("BGP RX Error: invalid marker {} at position {}",
69 + marker[i], i);
70 + //
71 + // ERROR: Connection Not Synchronized
72 + //
73 + // Send NOTIFICATION and close the connection
74 + int errorCode = MessageHeaderError.ERROR_CODE;
75 + int errorSubcode =
76 + MessageHeaderError.CONNECTION_NOT_SYNCHRONIZED;
77 + ChannelBuffer txMessage =
78 + bgpSession.prepareBgpNotification(errorCode, errorSubcode,
79 + null);
80 + ctx.getChannel().write(txMessage);
81 + bgpSession.closeChannel(ctx);
82 + return null;
83 + }
84 + }
85 +
86 + //
87 + // Read and check the BGP message Length field
88 + //
89 + int length = buf.readUnsignedShort();
90 + if ((length < BgpConstants.BGP_HEADER_LENGTH) ||
91 + (length > BgpConstants.BGP_MESSAGE_MAX_LENGTH)) {
92 + log.debug("BGP RX Error: invalid Length field {}. " +
93 + "Must be between {} and {}",
94 + length,
95 + BgpConstants.BGP_HEADER_LENGTH,
96 + BgpConstants.BGP_MESSAGE_MAX_LENGTH);
97 + //
98 + // ERROR: Bad Message Length
99 + //
100 + // Send NOTIFICATION and close the connection
101 + ChannelBuffer txMessage =
102 + bgpSession.prepareBgpNotificationBadMessageLength(length);
103 + ctx.getChannel().write(txMessage);
104 + bgpSession.closeChannel(ctx);
105 + return null;
106 + }
107 +
108 + //
109 + // Test whether the rest of the message is received:
110 + // So far we have read the Marker (16 octets) and the
111 + // Length (2 octets) fields.
112 + //
113 + int remainingMessageLen =
114 + length - BgpConstants.BGP_HEADER_MARKER_LENGTH - 2;
115 + if (buf.readableBytes() < remainingMessageLen) {
116 + // No enough data received
117 + buf.resetReaderIndex();
118 + return null;
119 + }
120 +
121 + //
122 + // Read the BGP message Type field, and process based on that type
123 + //
124 + int type = buf.readUnsignedByte();
125 + remainingMessageLen--; // Adjust after reading the type
126 + ChannelBuffer message = buf.readBytes(remainingMessageLen);
127 +
128 + //
129 + // Process the remaining of the message based on the message type
130 + //
131 + switch (type) {
132 + case BgpConstants.BGP_TYPE_OPEN:
133 + bgpSession.processBgpOpen(ctx, message);
134 + break;
135 + case BgpConstants.BGP_TYPE_UPDATE:
136 + bgpSession.processBgpUpdate(ctx, message);
137 + break;
138 + case BgpConstants.BGP_TYPE_NOTIFICATION:
139 + bgpSession.processBgpNotification(ctx, message);
140 + break;
141 + case BgpConstants.BGP_TYPE_KEEPALIVE:
142 + bgpSession.processBgpKeepalive(ctx, message);
143 + break;
144 + default:
145 + //
146 + // ERROR: Bad Message Type
147 + //
148 + // Send NOTIFICATION and close the connection
149 + int errorCode = MessageHeaderError.ERROR_CODE;
150 + int errorSubcode = MessageHeaderError.BAD_MESSAGE_TYPE;
151 + ChannelBuffer data = ChannelBuffers.buffer(1);
152 + data.writeByte(type);
153 + ChannelBuffer txMessage =
154 + bgpSession.prepareBgpNotification(errorCode, errorSubcode,
155 + data);
156 + ctx.getChannel().write(txMessage);
157 + bgpSession.closeChannel(ctx);
158 + return null;
159 + }
160 + return null;
161 + }
162 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.util.ArrayList;
6 +import java.util.Objects;
7 +
8 +import org.onlab.onos.sdnip.RouteEntry;
9 +import org.onlab.packet.IpAddress;
10 +import org.onlab.packet.IpPrefix;
11 +
12 +import com.google.common.base.MoreObjects;
13 +
14 +/**
15 + * Represents a route in BGP.
16 + */
17 +public class BgpRouteEntry extends RouteEntry {
18 + private final BgpSession bgpSession; // The BGP Session the route was
19 + // received on
20 + private final byte origin; // Route ORIGIN: IGP, EGP, INCOMPLETE
21 + private final AsPath asPath; // The AS Path
22 + private final long localPref; // The local preference for the route
23 + private long multiExitDisc =
24 + BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
25 +
26 + /**
27 + * Class constructor.
28 + *
29 + * @param bgpSession the BGP Session the route was received on
30 + * @param prefix the prefix of the route
31 + * @param nextHop the next hop of the route
32 + * @param origin the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE
33 + * @param asPath the AS path
34 + * @param localPref the route local preference
35 + */
36 + public BgpRouteEntry(BgpSession bgpSession, IpPrefix prefix,
37 + IpAddress nextHop, byte origin,
38 + BgpRouteEntry.AsPath asPath, long localPref) {
39 + super(prefix, nextHop);
40 + this.bgpSession = checkNotNull(bgpSession);
41 + this.origin = origin;
42 + this.asPath = checkNotNull(asPath);
43 + this.localPref = localPref;
44 + }
45 +
46 + /**
47 + * Gets the BGP Session the route was received on.
48 + *
49 + * @return the BGP Session the route was received on
50 + */
51 + public BgpSession getBgpSession() {
52 + return bgpSession;
53 + }
54 +
55 + /**
56 + * Gets the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE.
57 + *
58 + * @return the route origin: 0=IGP, 1=EGP, 2=INCOMPLETE
59 + */
60 + public byte getOrigin() {
61 + return origin;
62 + }
63 +
64 + /**
65 + * Gets the route AS path.
66 + *
67 + * @return the route AS path
68 + */
69 + public BgpRouteEntry.AsPath getAsPath() {
70 + return asPath;
71 + }
72 +
73 + /**
74 + * Gets the route local preference.
75 + *
76 + * @return the route local preference
77 + */
78 + public long getLocalPref() {
79 + return localPref;
80 + }
81 +
82 + /**
83 + * Gets the route MED (Multi-Exit Discriminator).
84 + *
85 + * @return the route MED (Multi-Exit Discriminator)
86 + */
87 + public long getMultiExitDisc() {
88 + return multiExitDisc;
89 + }
90 +
91 + /**
92 + * Sets the route MED (Multi-Exit Discriminator).
93 + *
94 + * @param multiExitDisc the route MED (Multi-Exit Discriminator) to set
95 + */
96 + void setMultiExitDisc(long multiExitDisc) {
97 + this.multiExitDisc = multiExitDisc;
98 + }
99 +
100 + /**
101 + * Tests whether the route is originated from the local AS.
102 + * <p/>
103 + * The route is considered originated from the local AS if the AS Path
104 + * is empty or if it begins with an AS_SET.
105 + *
106 + * @return true if the route is originated from the local AS, otherwise
107 + * false
108 + */
109 + boolean isLocalRoute() {
110 + if (asPath.getPathSegments().isEmpty()) {
111 + return true;
112 + }
113 + PathSegment firstPathSegment = asPath.getPathSegments().get(0);
114 + if (firstPathSegment.getType() == BgpConstants.Update.AsPath.AS_SET) {
115 + return true;
116 + }
117 + return false;
118 + }
119 +
120 + /**
121 + * Gets the BGP Neighbor AS number the route was received from.
122 + * <p/>
123 + * If the router is originated from the local AS, the return value is
124 + * zero (BGP_AS_0).
125 + *
126 + * @return the BGP Neighbor AS number the route was received from.
127 + */
128 + long getNeighborAs() {
129 + if (isLocalRoute()) {
130 + return BgpConstants.BGP_AS_0;
131 + }
132 + PathSegment firstPathSegment = asPath.getPathSegments().get(0);
133 + if (firstPathSegment.getSegmentAsNumbers().isEmpty()) {
134 + // TODO: Shouldn't happen. Should check during the parsing.
135 + return BgpConstants.BGP_AS_0;
136 + }
137 + return firstPathSegment.getSegmentAsNumbers().get(0);
138 + }
139 +
140 + /**
141 + * Tests whether the AS Path contains a loop.
142 + * <p/>
143 + * The test is done by comparing whether the AS Path contains the
144 + * local AS number.
145 + *
146 + * @param localAsNumber the local AS number to compare against
147 + * @return true if the AS Path contains a loop, otherwise false
148 + */
149 + boolean hasAsPathLoop(long localAsNumber) {
150 + for (PathSegment pathSegment : asPath.getPathSegments()) {
151 + for (Long asNumber : pathSegment.getSegmentAsNumbers()) {
152 + if (asNumber.equals(localAsNumber)) {
153 + return true;
154 + }
155 + }
156 + }
157 + return false;
158 + }
159 +
160 + /**
161 + * Compares this BGP route against another BGP route by using the
162 + * BGP Decision Process.
163 + * <p/>
164 + * NOTE: The comparison needs to be performed only on routes that have
165 + * same IP Prefix.
166 + *
167 + * @param other the BGP route to compare against
168 + * @return true if this BGP route is better than the other BGP route
169 + * or same, otherwise false
170 + */
171 + boolean isBetterThan(BgpRouteEntry other) {
172 + if (this == other) {
173 + return true; // Return true if same route
174 + }
175 +
176 + // Compare the LOCAL_PREF values: larger is better
177 + if (getLocalPref() != other.getLocalPref()) {
178 + return (getLocalPref() > other.getLocalPref());
179 + }
180 +
181 + // Compare the AS number in the path: smaller is better
182 + if (getAsPath().getAsPathLength() !=
183 + other.getAsPath().getAsPathLength()) {
184 + return getAsPath().getAsPathLength() <
185 + other.getAsPath().getAsPathLength();
186 + }
187 +
188 + // Compare the Origin number: lower is better
189 + if (getOrigin() != other.getOrigin()) {
190 + return (getOrigin() < other.getOrigin());
191 + }
192 +
193 + // Compare the MED if the neighbor AS is same: larger is better
194 + medLabel: {
195 + boolean thisIsLocalRoute = isLocalRoute();
196 + if (thisIsLocalRoute != other.isLocalRoute()) {
197 + break medLabel; // AS number is different
198 + }
199 + if (!thisIsLocalRoute) {
200 + long thisNeighborAs = getNeighborAs();
201 + if (thisNeighborAs != other.getNeighborAs()) {
202 + break medLabel; // AS number is different
203 + }
204 + if (thisNeighborAs == BgpConstants.BGP_AS_0) {
205 + break medLabel; // Invalid AS number
206 + }
207 + }
208 +
209 + // Compare the MED
210 + if (getMultiExitDisc() != other.getMultiExitDisc()) {
211 + return (getMultiExitDisc() > other.getMultiExitDisc());
212 + }
213 + }
214 +
215 + // Compare the peer BGP ID: lower is better
216 + IpAddress peerBgpId = getBgpSession().getRemoteBgpId();
217 + IpAddress otherPeerBgpId = other.getBgpSession().getRemoteBgpId();
218 + if (!peerBgpId.equals(otherPeerBgpId)) {
219 + return (peerBgpId.compareTo(otherPeerBgpId) < 0);
220 + }
221 +
222 + // Compare the peer BGP address: lower is better
223 + IpAddress peerAddress = getBgpSession().getRemoteIp4Address();
224 + IpAddress otherPeerAddress =
225 + other.getBgpSession().getRemoteIp4Address();
226 + if (!peerAddress.equals(otherPeerAddress)) {
227 + return (peerAddress.compareTo(otherPeerAddress) < 0);
228 + }
229 +
230 + return true; // Routes are same. Shouldn't happen?
231 + }
232 +
233 + /**
234 + * A class to represent AS Path Segment.
235 + */
236 + public static class PathSegment {
237 + private final byte type; // Segment type: AS_SET, AS_SEQUENCE
238 + private final ArrayList<Long> segmentAsNumbers; // Segment AS numbers
239 +
240 + /**
241 + * Constructor.
242 + *
243 + * @param type the Path Segment Type: 1=AS_SET, 2=AS_SEQUENCE
244 + * @param segmentAsNumbers the Segment AS numbers
245 + */
246 + PathSegment(byte type, ArrayList<Long> segmentAsNumbers) {
247 + this.type = type;
248 + this.segmentAsNumbers = checkNotNull(segmentAsNumbers);
249 + }
250 +
251 + /**
252 + * Gets the Path Segment Type: AS_SET, AS_SEQUENCE.
253 + *
254 + * @return the Path Segment Type: AS_SET, AS_SEQUENCE
255 + */
256 + public byte getType() {
257 + return type;
258 + }
259 +
260 + /**
261 + * Gets the Path Segment AS Numbers.
262 + *
263 + * @return the Path Segment AS Numbers
264 + */
265 + public ArrayList<Long> getSegmentAsNumbers() {
266 + return segmentAsNumbers;
267 + }
268 +
269 + @Override
270 + public boolean equals(Object other) {
271 + if (this == other) {
272 + return true;
273 + }
274 +
275 + if (!(other instanceof PathSegment)) {
276 + return false;
277 + }
278 +
279 + PathSegment otherPathSegment = (PathSegment) other;
280 + return Objects.equals(this.type, otherPathSegment.type) &&
281 + Objects.equals(this.segmentAsNumbers,
282 + otherPathSegment.segmentAsNumbers);
283 + }
284 +
285 + @Override
286 + public int hashCode() {
287 + return Objects.hash(type, segmentAsNumbers);
288 + }
289 +
290 + @Override
291 + public String toString() {
292 + return MoreObjects.toStringHelper(getClass())
293 + .add("type", this.type)
294 + .add("segmentAsNumbers", this.segmentAsNumbers)
295 + .toString();
296 + }
297 + }
298 +
299 + /**
300 + * A class to represent AS Path.
301 + */
302 + public static class AsPath {
303 + private final ArrayList<PathSegment> pathSegments;
304 + private final int asPathLength; // Precomputed AS Path Length
305 +
306 + /**
307 + * Constructor.
308 + *
309 + * @param pathSegments the Path Segments of the Path
310 + */
311 + AsPath(ArrayList<PathSegment> pathSegments) {
312 + this.pathSegments = checkNotNull(pathSegments);
313 +
314 + //
315 + // Precompute the AS Path Length:
316 + // - AS_SET counts as 1
317 + //
318 + int pl = 0;
319 + for (PathSegment pathSegment : pathSegments) {
320 + if (pathSegment.getType() ==
321 + BgpConstants.Update.AsPath.AS_SET) {
322 + pl++;
323 + continue;
324 + }
325 + pl += pathSegment.getSegmentAsNumbers().size();
326 + }
327 + asPathLength = pl;
328 + }
329 +
330 + /**
331 + * Gets the AS Path Segments.
332 + *
333 + * @return the AS Path Segments
334 + */
335 + public ArrayList<PathSegment> getPathSegments() {
336 + return pathSegments;
337 + }
338 +
339 + /**
340 + * Gets the AS Path Length as considered by the BGP Decision Process.
341 + *
342 + * @return the AS Path Length as considered by the BGP Decision Process
343 + */
344 + int getAsPathLength() {
345 + return asPathLength;
346 + }
347 +
348 + @Override
349 + public boolean equals(Object other) {
350 + if (this == other) {
351 + return true;
352 + }
353 +
354 + if (!(other instanceof AsPath)) {
355 + return false;
356 + }
357 +
358 + AsPath otherAsPath = (AsPath) other;
359 + return Objects.equals(this.pathSegments, otherAsPath.pathSegments);
360 + }
361 +
362 + @Override
363 + public int hashCode() {
364 + return Objects.hash(pathSegments);
365 + }
366 +
367 + @Override
368 + public String toString() {
369 + return MoreObjects.toStringHelper(getClass())
370 + .add("pathSegments", this.pathSegments)
371 + .toString();
372 + }
373 + }
374 +
375 + /**
376 + * Compares whether two objects are equal.
377 + * <p/>
378 + * NOTE: The bgpSession field is excluded from the comparison.
379 + *
380 + * @return true if the two objects are equal, otherwise false.
381 + */
382 + @Override
383 + public boolean equals(Object other) {
384 + if (this == other) {
385 + return true;
386 + }
387 +
388 + //
389 + // NOTE: Subclasses are considered as change of identity, hence
390 + // equals() will return false if the class type doesn't match.
391 + //
392 + if (other == null || getClass() != other.getClass()) {
393 + return false;
394 + }
395 +
396 + if (!super.equals(other)) {
397 + return false;
398 + }
399 +
400 + // NOTE: The bgpSession field is excluded from the comparison
401 + BgpRouteEntry otherRoute = (BgpRouteEntry) other;
402 + return (this.origin == otherRoute.origin) &&
403 + Objects.equals(this.asPath, otherRoute.asPath) &&
404 + (this.localPref == otherRoute.localPref) &&
405 + (this.multiExitDisc == otherRoute.multiExitDisc);
406 + }
407 +
408 + /**
409 + * Computes the hash code.
410 + * <p/>
411 + * NOTE: We return the base class hash code to avoid expensive computation
412 + *
413 + * @return the object hash code
414 + */
415 + @Override
416 + public int hashCode() {
417 + return super.hashCode();
418 + }
419 +
420 + @Override
421 + public String toString() {
422 + return MoreObjects.toStringHelper(getClass())
423 + .add("prefix", prefix())
424 + .add("nextHop", nextHop())
425 + .add("bgpId", bgpSession.getRemoteBgpId())
426 + .add("origin", origin)
427 + .add("asPath", asPath)
428 + .add("localPref", localPref)
429 + .add("multiExitDisc", multiExitDisc)
430 + .toString();
431 + }
432 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import java.net.InetAddress;
4 +import java.net.InetSocketAddress;
5 +import java.net.SocketAddress;
6 +import java.util.ArrayList;
7 +import java.util.Collection;
8 +import java.util.Collections;
9 +import java.util.HashMap;
10 +import java.util.Map;
11 +import java.util.concurrent.ConcurrentHashMap;
12 +import java.util.concurrent.ConcurrentMap;
13 +import java.util.concurrent.TimeUnit;
14 +
15 +import org.apache.commons.lang3.tuple.Pair;
16 +import org.jboss.netty.buffer.ChannelBuffer;
17 +import org.jboss.netty.buffer.ChannelBuffers;
18 +import org.jboss.netty.channel.ChannelHandlerContext;
19 +import org.jboss.netty.channel.ChannelStateEvent;
20 +import org.jboss.netty.channel.SimpleChannelHandler;
21 +import org.jboss.netty.util.HashedWheelTimer;
22 +import org.jboss.netty.util.Timeout;
23 +import org.jboss.netty.util.Timer;
24 +import org.jboss.netty.util.TimerTask;
25 +import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications;
26 +import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.HoldTimerExpired;
27 +import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.MessageHeaderError;
28 +import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.OpenMessageError;
29 +import org.onlab.onos.sdnip.bgp.BgpConstants.Notifications.UpdateMessageError;
30 +import org.onlab.packet.IpAddress;
31 +import org.onlab.packet.IpPrefix;
32 +import org.slf4j.Logger;
33 +import org.slf4j.LoggerFactory;
34 +
35 +/**
36 + * Class for handling the BGP peer sessions.
37 + * There is one instance per each BGP peer session.
38 + */
39 +public class BgpSession extends SimpleChannelHandler {
40 + private static final Logger log =
41 + LoggerFactory.getLogger(BgpSession.class);
42 +
43 + private final BgpSessionManager bgpSessionManager;
44 +
45 + // Local flag to indicate the session is closed.
46 + // It is used to avoid the Netty's asynchronous closing of a channel.
47 + private boolean isClosed = false;
48 +
49 + private SocketAddress remoteAddress; // Peer IP addr/port
50 + private IpAddress remoteIp4Address; // Peer IPv4 address
51 + private int remoteBgpVersion; // 1 octet
52 + private long remoteAs; // 2 octets
53 + private long remoteHoldtime; // 2 octets
54 + private IpAddress remoteBgpId; // 4 octets -> IPv4 address
55 + //
56 + private SocketAddress localAddress; // Local IP addr/port
57 + private IpAddress localIp4Address; // Local IPv4 address
58 + private int localBgpVersion; // 1 octet
59 + private long localAs; // 2 octets
60 + private long localHoldtime; // 2 octets
61 + private IpAddress localBgpId; // 4 octets -> IPv4 address
62 + //
63 + private long localKeepaliveInterval; // Keepalive interval
64 +
65 + // Timers state
66 + private Timer timer = new HashedWheelTimer();
67 + private volatile Timeout keepaliveTimeout; // Periodic KEEPALIVE
68 + private volatile Timeout sessionTimeout; // Session timeout
69 +
70 + // BGP RIB-IN routing entries from this peer
71 + private ConcurrentMap<IpPrefix, BgpRouteEntry> bgpRibIn =
72 + new ConcurrentHashMap<>();
73 +
74 + /**
75 + * Constructor for a given BGP Session Manager.
76 + *
77 + * @param bgpSessionManager the BGP Session Manager to use
78 + */
79 + BgpSession(BgpSessionManager bgpSessionManager) {
80 + this.bgpSessionManager = bgpSessionManager;
81 + }
82 +
83 + /**
84 + * Gets the BGP RIB-IN routing entries.
85 + *
86 + * @return the BGP RIB-IN routing entries
87 + */
88 + public Collection<BgpRouteEntry> getBgpRibIn() {
89 + return bgpRibIn.values();
90 + }
91 +
92 + /**
93 + * Finds a BGP routing entry in the BGP RIB-IN.
94 + *
95 + * @param prefix the prefix of the route to search for
96 + * @return the BGP routing entry if found, otherwise null
97 + */
98 + public BgpRouteEntry findBgpRouteEntry(IpPrefix prefix) {
99 + return bgpRibIn.get(prefix);
100 + }
101 +
102 + /**
103 + * Gets the BGP session remote address.
104 + *
105 + * @return the BGP session remote address
106 + */
107 + public SocketAddress getRemoteAddress() {
108 + return remoteAddress;
109 + }
110 +
111 + /**
112 + * Gets the BGP session remote IPv4 address.
113 + *
114 + * @return the BGP session remote IPv4 address
115 + */
116 + public IpAddress getRemoteIp4Address() {
117 + return remoteIp4Address;
118 + }
119 +
120 + /**
121 + * Gets the BGP session remote BGP version.
122 + *
123 + * @return the BGP session remote BGP version
124 + */
125 + public int getRemoteBgpVersion() {
126 + return remoteBgpVersion;
127 + }
128 +
129 + /**
130 + * Gets the BGP session remote AS number.
131 + *
132 + * @return the BGP session remote AS number
133 + */
134 + public long getRemoteAs() {
135 + return remoteAs;
136 + }
137 +
138 + /**
139 + * Gets the BGP session remote Holdtime.
140 + *
141 + * @return the BGP session remote Holdtime
142 + */
143 + public long getRemoteHoldtime() {
144 + return remoteHoldtime;
145 + }
146 +
147 + /**
148 + * Gets the BGP session remote BGP Identifier as an IPv4 address.
149 + *
150 + * @return the BGP session remote BGP Identifier as an IPv4 address
151 + */
152 + public IpAddress getRemoteBgpId() {
153 + return remoteBgpId;
154 + }
155 +
156 + /**
157 + * Gets the BGP session local address.
158 + *
159 + * @return the BGP session local address
160 + */
161 + public SocketAddress getLocalAddress() {
162 + return localAddress;
163 + }
164 +
165 + /**
166 + * Gets the BGP session local BGP version.
167 + *
168 + * @return the BGP session local BGP version
169 + */
170 + public int getLocalBgpVersion() {
171 + return localBgpVersion;
172 + }
173 +
174 + /**
175 + * Gets the BGP session local AS number.
176 + *
177 + * @return the BGP session local AS number
178 + */
179 + public long getLocalAs() {
180 + return localAs;
181 + }
182 +
183 + /**
184 + * Gets the BGP session local Holdtime.
185 + *
186 + * @return the BGP session local Holdtime
187 + */
188 + public long getLocalHoldtime() {
189 + return localHoldtime;
190 + }
191 +
192 + /**
193 + * Gets the BGP session local BGP Identifier as an IPv4 address.
194 + *
195 + * @return the BGP session local BGP Identifier as an IPv4 address
196 + */
197 + public IpAddress getLocalBgpId() {
198 + return localBgpId;
199 + }
200 +
201 + /**
202 + * Tests whether the session is closed.
203 + * <p/>
204 + * NOTE: We use this method to avoid the Netty's asynchronous closing
205 + * of a channel.
206 + *
207 + * @param return true if the session is closed
208 + */
209 + boolean isClosed() {
210 + return isClosed;
211 + }
212 +
213 + /**
214 + * Closes the channel.
215 + *
216 + * @param ctx the Channel Handler Context
217 + */
218 + void closeChannel(ChannelHandlerContext ctx) {
219 + isClosed = true;
220 + timer.stop();
221 + ctx.getChannel().close();
222 + }
223 +
224 + @Override
225 + public void channelConnected(ChannelHandlerContext ctx,
226 + ChannelStateEvent channelEvent) {
227 + localAddress = ctx.getChannel().getLocalAddress();
228 + remoteAddress = ctx.getChannel().getRemoteAddress();
229 +
230 + // Assign the local and remote IPv4 addresses
231 + InetAddress inetAddr;
232 + if (localAddress instanceof InetSocketAddress) {
233 + inetAddr = ((InetSocketAddress) localAddress).getAddress();
234 + localIp4Address = IpAddress.valueOf(inetAddr.getAddress());
235 + }
236 + if (remoteAddress instanceof InetSocketAddress) {
237 + inetAddr = ((InetSocketAddress) remoteAddress).getAddress();
238 + remoteIp4Address = IpAddress.valueOf(inetAddr.getAddress());
239 + }
240 +
241 + log.debug("BGP Session Connected from {} on {}",
242 + remoteAddress, localAddress);
243 + if (!bgpSessionManager.peerConnected(this)) {
244 + log.debug("Cannot setup BGP Session Connection from {}. Closing...",
245 + remoteAddress);
246 + ctx.getChannel().close();
247 + }
248 + }
249 +
250 + @Override
251 + public void channelDisconnected(ChannelHandlerContext ctx,
252 + ChannelStateEvent channelEvent) {
253 + log.debug("BGP Session Disconnected from {} on {}",
254 + ctx.getChannel().getRemoteAddress(),
255 + ctx.getChannel().getLocalAddress());
256 +
257 + //
258 + // Withdraw the routes advertised by this BGP peer
259 + //
260 + // NOTE: We must initialize the RIB-IN before propagating the withdraws
261 + // for further processing. Otherwise, the BGP Decision Process
262 + // will use those routes again.
263 + //
264 + Collection<BgpRouteEntry> deletedRoutes = bgpRibIn.values();
265 + bgpRibIn = new ConcurrentHashMap<>();
266 +
267 + // Push the updates to the BGP Merged RIB
268 + BgpSessionManager.BgpRouteSelector bgpRouteSelector =
269 + bgpSessionManager.getBgpRouteSelector();
270 + Collection<BgpRouteEntry> addedRoutes = Collections.emptyList();
271 + bgpRouteSelector.routeUpdates(this, addedRoutes, deletedRoutes);
272 +
273 + bgpSessionManager.peerDisconnected(this);
274 + }
275 +
276 + /**
277 + * Processes BGP OPEN message.
278 + *
279 + * @param ctx the Channel Handler Context
280 + * @param message the message to process
281 + */
282 + void processBgpOpen(ChannelHandlerContext ctx, ChannelBuffer message) {
283 + int minLength =
284 + BgpConstants.BGP_OPEN_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH;
285 + if (message.readableBytes() < minLength) {
286 + log.debug("BGP RX OPEN Error from {}: " +
287 + "Message length {} too short. Must be at least {}",
288 + remoteAddress, message.readableBytes(), minLength);
289 + //
290 + // ERROR: Bad Message Length
291 + //
292 + // Send NOTIFICATION and close the connection
293 + ChannelBuffer txMessage = prepareBgpNotificationBadMessageLength(
294 + message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH);
295 + ctx.getChannel().write(txMessage);
296 + closeChannel(ctx);
297 + return;
298 + }
299 +
300 + //
301 + // Parse the OPEN message
302 + //
303 + // Remote BGP version
304 + remoteBgpVersion = message.readUnsignedByte();
305 + if (remoteBgpVersion != BgpConstants.BGP_VERSION) {
306 + log.debug("BGP RX OPEN Error from {}: " +
307 + "Unsupported BGP version {}. Should be {}",
308 + remoteAddress, remoteBgpVersion,
309 + BgpConstants.BGP_VERSION);
310 + //
311 + // ERROR: Unsupported Version Number
312 + //
313 + // Send NOTIFICATION and close the connection
314 + int errorCode = OpenMessageError.ERROR_CODE;
315 + int errorSubcode = OpenMessageError.UNSUPPORTED_VERSION_NUMBER;
316 + ChannelBuffer data = ChannelBuffers.buffer(2);
317 + data.writeShort(BgpConstants.BGP_VERSION);
318 + ChannelBuffer txMessage =
319 + prepareBgpNotification(errorCode, errorSubcode, data);
320 + ctx.getChannel().write(txMessage);
321 + closeChannel(ctx);
322 + return;
323 + }
324 +
325 + // Remote AS number
326 + remoteAs = message.readUnsignedShort();
327 + //
328 + // Verify that the AS number is same for all other BGP Sessions
329 + // NOTE: This check applies only for our use-case where all BGP
330 + // sessions are iBGP.
331 + //
332 + for (BgpSession bgpSession : bgpSessionManager.getBgpSessions()) {
333 + if (remoteAs != bgpSession.getRemoteAs()) {
334 + log.debug("BGP RX OPEN Error from {}: Bad Peer AS {}. " +
335 + "Expected {}",
336 + remoteAddress, remoteAs, bgpSession.getRemoteAs());
337 + //
338 + // ERROR: Bad Peer AS
339 + //
340 + // Send NOTIFICATION and close the connection
341 + int errorCode = OpenMessageError.ERROR_CODE;
342 + int errorSubcode = OpenMessageError.BAD_PEER_AS;
343 + ChannelBuffer txMessage =
344 + prepareBgpNotification(errorCode, errorSubcode, null);
345 + ctx.getChannel().write(txMessage);
346 + closeChannel(ctx);
347 + return;
348 + }
349 + }
350 +
351 + // Remote Hold Time
352 + remoteHoldtime = message.readUnsignedShort();
353 + if ((remoteHoldtime != 0) &&
354 + (remoteHoldtime < BgpConstants.BGP_KEEPALIVE_MIN_HOLDTIME)) {
355 + log.debug("BGP RX OPEN Error from {}: " +
356 + "Unacceptable Hold Time field {}. " +
357 + "Should be 0 or at least {}",
358 + remoteAddress, remoteHoldtime,
359 + BgpConstants.BGP_KEEPALIVE_MIN_HOLDTIME);
360 + //
361 + // ERROR: Unacceptable Hold Time
362 + //
363 + // Send NOTIFICATION and close the connection
364 + int errorCode = OpenMessageError.ERROR_CODE;
365 + int errorSubcode = OpenMessageError.UNACCEPTABLE_HOLD_TIME;
366 + ChannelBuffer txMessage =
367 + prepareBgpNotification(errorCode, errorSubcode, null);
368 + ctx.getChannel().write(txMessage);
369 + closeChannel(ctx);
370 + return;
371 + }
372 +
373 + // Remote BGP Identifier
374 + remoteBgpId = IpAddress.valueOf((int) message.readUnsignedInt());
375 +
376 + // Optional Parameters
377 + int optParamLen = message.readUnsignedByte();
378 + if (message.readableBytes() < optParamLen) {
379 + log.debug("BGP RX OPEN Error from {}: " +
380 + "Invalid Optional Parameter Length field {}. " +
381 + "Remaining Optional Parameters {}",
382 + remoteAddress, optParamLen, message.readableBytes());
383 + //
384 + // ERROR: Invalid Optional Parameter Length field: Unspecific
385 + //
386 + // Send NOTIFICATION and close the connection
387 + int errorCode = OpenMessageError.ERROR_CODE;
388 + int errorSubcode = Notifications.ERROR_SUBCODE_UNSPECIFIC;
389 + ChannelBuffer txMessage =
390 + prepareBgpNotification(errorCode, errorSubcode, null);
391 + ctx.getChannel().write(txMessage);
392 + closeChannel(ctx);
393 + return;
394 + }
395 + // TODO: Parse the optional parameters (if needed)
396 + message.readBytes(optParamLen); // NOTE: data ignored
397 +
398 + //
399 + // Copy some of the remote peer's state/setup to the local setup:
400 + // - BGP version
401 + // - AS number (NOTE: the peer setup is always iBGP)
402 + // - Holdtime
403 + // Also, assign the local BGP ID based on the local setup
404 + //
405 + localBgpVersion = remoteBgpVersion;
406 + localAs = remoteAs;
407 + localHoldtime = remoteHoldtime;
408 + localBgpId = bgpSessionManager.getMyBgpId();
409 +
410 + // Set the Keepalive interval
411 + if (localHoldtime == 0) {
412 + localKeepaliveInterval = 0;
413 + } else {
414 + localKeepaliveInterval = Math.max(localHoldtime /
415 + BgpConstants.BGP_KEEPALIVE_PER_HOLD_INTERVAL,
416 + BgpConstants.BGP_KEEPALIVE_MIN_INTERVAL);
417 + }
418 +
419 + log.debug("BGP RX OPEN message from {}: " +
420 + "BGPv{} AS {} BGP-ID {} Holdtime {}",
421 + remoteAddress, remoteBgpVersion, remoteAs,
422 + remoteBgpId, remoteHoldtime);
423 +
424 + // Send my OPEN followed by KEEPALIVE
425 + ChannelBuffer txMessage = prepareBgpOpen();
426 + ctx.getChannel().write(txMessage);
427 + //
428 + txMessage = prepareBgpKeepalive();
429 + ctx.getChannel().write(txMessage);
430 +
431 + // Start the KEEPALIVE timer
432 + restartKeepaliveTimer(ctx);
433 +
434 + // Start the Session Timeout timer
435 + restartSessionTimeoutTimer(ctx);
436 + }
437 +
438 + /**
439 + * Processes BGP UPDATE message.
440 + *
441 + * @param ctx the Channel Handler Context
442 + * @param message the message to process
443 + */
444 + void processBgpUpdate(ChannelHandlerContext ctx, ChannelBuffer message) {
445 + Collection<BgpRouteEntry> addedRoutes = null;
446 + Map<IpPrefix, BgpRouteEntry> deletedRoutes = new HashMap<>();
447 +
448 + int minLength =
449 + BgpConstants.BGP_UPDATE_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH;
450 + if (message.readableBytes() < minLength) {
451 + log.debug("BGP RX UPDATE Error from {}: " +
452 + "Message length {} too short. Must be at least {}",
453 + remoteAddress, message.readableBytes(), minLength);
454 + //
455 + // ERROR: Bad Message Length
456 + //
457 + // Send NOTIFICATION and close the connection
458 + ChannelBuffer txMessage = prepareBgpNotificationBadMessageLength(
459 + message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH);
460 + ctx.getChannel().write(txMessage);
461 + closeChannel(ctx);
462 + return;
463 + }
464 +
465 + log.debug("BGP RX UPDATE message from {}", remoteAddress);
466 +
467 + //
468 + // Parse the UPDATE message
469 + //
470 +
471 + //
472 + // Parse the Withdrawn Routes
473 + //
474 + int withdrawnRoutesLength = message.readUnsignedShort();
475 + if (withdrawnRoutesLength > message.readableBytes()) {
476 + // ERROR: Malformed Attribute List
477 + actionsBgpUpdateMalformedAttributeList(ctx);
478 + return;
479 + }
480 + Collection<IpPrefix> withdrawnPrefixes = null;
481 + try {
482 + withdrawnPrefixes = parsePackedPrefixes(withdrawnRoutesLength,
483 + message);
484 + } catch (BgpParseException e) {
485 + // ERROR: Invalid Network Field
486 + log.debug("Exception parsing Withdrawn Prefixes from BGP peer {}: ",
487 + remoteBgpId, e);
488 + actionsBgpUpdateInvalidNetworkField(ctx);
489 + return;
490 + }
491 + for (IpPrefix prefix : withdrawnPrefixes) {
492 + log.debug("BGP RX UPDATE message WITHDRAWN from {}: {}",
493 + remoteAddress, prefix);
494 + BgpRouteEntry bgpRouteEntry = bgpRibIn.get(prefix);
495 + if (bgpRouteEntry != null) {
496 + deletedRoutes.put(prefix, bgpRouteEntry);
497 + }
498 + }
499 +
500 + //
501 + // Parse the Path Attributes
502 + //
503 + try {
504 + addedRoutes = parsePathAttributes(ctx, message);
505 + } catch (BgpParseException e) {
506 + log.debug("Exception parsing Path Attributes from BGP peer {}: ",
507 + remoteBgpId, e);
508 + // NOTE: The session was already closed, so nothing else to do
509 + return;
510 + }
511 + // Ignore WITHDRAWN routes that are ADDED
512 + for (BgpRouteEntry bgpRouteEntry : addedRoutes) {
513 + deletedRoutes.remove(bgpRouteEntry.prefix());
514 + }
515 +
516 + // Update the BGP RIB-IN
517 + for (BgpRouteEntry bgpRouteEntry : deletedRoutes.values()) {
518 + bgpRibIn.remove(bgpRouteEntry.prefix());
519 + }
520 + for (BgpRouteEntry bgpRouteEntry : addedRoutes) {
521 + bgpRibIn.put(bgpRouteEntry.prefix(), bgpRouteEntry);
522 + }
523 +
524 + // Push the updates to the BGP Merged RIB
525 + BgpSessionManager.BgpRouteSelector bgpRouteSelector =
526 + bgpSessionManager.getBgpRouteSelector();
527 + bgpRouteSelector.routeUpdates(this, addedRoutes,
528 + deletedRoutes.values());
529 +
530 + // Start the Session Timeout timer
531 + restartSessionTimeoutTimer(ctx);
532 + }
533 +
534 + /**
535 + * Parse BGP Path Attributes from the BGP UPDATE message.
536 + *
537 + * @param ctx the Channel Handler Context
538 + * @param message the message to parse
539 + * @return a collection of the result BGP Route Entries
540 + * @throws BgpParseException
541 + */
542 + private Collection<BgpRouteEntry> parsePathAttributes(
543 + ChannelHandlerContext ctx,
544 + ChannelBuffer message)
545 + throws BgpParseException {
546 + Map<IpPrefix, BgpRouteEntry> addedRoutes = new HashMap<>();
547 +
548 + //
549 + // Parsed values
550 + //
551 + Short origin = -1; // Mandatory
552 + BgpRouteEntry.AsPath asPath = null; // Mandatory
553 + IpAddress nextHop = null; // Mandatory
554 + long multiExitDisc = // Optional
555 + BgpConstants.Update.MultiExitDisc.LOWEST_MULTI_EXIT_DISC;
556 + Long localPref = null; // Mandatory
557 + Long aggregatorAsNumber = null; // Optional: unused
558 + IpAddress aggregatorIpAddress = null; // Optional: unused
559 +
560 + //
561 + // Get and verify the Path Attributes Length
562 + //
563 + int pathAttributeLength = message.readUnsignedShort();
564 + if (pathAttributeLength > message.readableBytes()) {
565 + // ERROR: Malformed Attribute List
566 + actionsBgpUpdateMalformedAttributeList(ctx);
567 + String errorMsg = "Malformed Attribute List";
568 + throw new BgpParseException(errorMsg);
569 + }
570 + if (pathAttributeLength == 0) {
571 + return addedRoutes.values();
572 + }
573 +
574 + //
575 + // Parse the Path Attributes
576 + //
577 + int pathAttributeEnd = message.readerIndex() + pathAttributeLength;
578 + while (message.readerIndex() < pathAttributeEnd) {
579 + int attrFlags = message.readUnsignedByte();
580 + if (message.readerIndex() >= pathAttributeEnd) {
581 + // ERROR: Malformed Attribute List
582 + actionsBgpUpdateMalformedAttributeList(ctx);
583 + String errorMsg = "Malformed Attribute List";
584 + throw new BgpParseException(errorMsg);
585 + }
586 + int attrTypeCode = message.readUnsignedByte();
587 +
588 + // The Attribute Flags
589 + boolean optionalBit = ((0x80 & attrFlags) != 0);
590 + boolean transitiveBit = ((0x40 & attrFlags) != 0);
591 + boolean partialBit = ((0x20 & attrFlags) != 0);
592 + boolean extendedLengthBit = ((0x10 & attrFlags) != 0);
593 +
594 + // The Attribute Length
595 + int attrLen = 0;
596 + int attrLenOctets = 1;
597 + if (extendedLengthBit) {
598 + attrLenOctets = 2;
599 + }
600 + if (message.readerIndex() + attrLenOctets > pathAttributeEnd) {
601 + // ERROR: Malformed Attribute List
602 + actionsBgpUpdateMalformedAttributeList(ctx);
603 + String errorMsg = "Malformed Attribute List";
604 + throw new BgpParseException(errorMsg);
605 + }
606 + if (extendedLengthBit) {
607 + attrLen = message.readUnsignedShort();
608 + } else {
609 + attrLen = message.readUnsignedByte();
610 + }
611 + if (message.readerIndex() + attrLen > pathAttributeEnd) {
612 + // ERROR: Malformed Attribute List
613 + actionsBgpUpdateMalformedAttributeList(ctx);
614 + String errorMsg = "Malformed Attribute List";
615 + throw new BgpParseException(errorMsg);
616 + }
617 +
618 + //
619 + // Verify the Attribute Flags
620 + //
621 + verifyBgpUpdateAttributeFlags(ctx, attrTypeCode, attrLen,
622 + attrFlags, message);
623 +
624 + //
625 + // Extract the Attribute Value based on the Attribute Type Code
626 + //
627 + switch (attrTypeCode) {
628 +
629 + case BgpConstants.Update.Origin.TYPE:
630 + // Attribute Type Code ORIGIN
631 + origin = parseAttributeTypeOrigin(ctx, attrTypeCode, attrLen,
632 + attrFlags, message);
633 + break;
634 +
635 + case BgpConstants.Update.AsPath.TYPE:
636 + // Attribute Type Code AS_PATH
637 + asPath = parseAttributeTypeAsPath(ctx, attrTypeCode, attrLen,
638 + attrFlags, message);
639 + break;
640 +
641 + case BgpConstants.Update.NextHop.TYPE:
642 + // Attribute Type Code NEXT_HOP
643 + nextHop = parseAttributeTypeNextHop(ctx, attrTypeCode, attrLen,
644 + attrFlags, message);
645 + break;
646 +
647 + case BgpConstants.Update.MultiExitDisc.TYPE:
648 + // Attribute Type Code MULTI_EXIT_DISC
649 + multiExitDisc =
650 + parseAttributeTypeMultiExitDisc(ctx, attrTypeCode, attrLen,
651 + attrFlags, message);
652 + break;
653 +
654 + case BgpConstants.Update.LocalPref.TYPE:
655 + // Attribute Type Code LOCAL_PREF
656 + localPref =
657 + parseAttributeTypeLocalPref(ctx, attrTypeCode, attrLen,
658 + attrFlags, message);
659 + break;
660 +
661 + case BgpConstants.Update.AtomicAggregate.TYPE:
662 + // Attribute Type Code ATOMIC_AGGREGATE
663 + parseAttributeTypeAtomicAggregate(ctx, attrTypeCode, attrLen,
664 + attrFlags, message);
665 + // Nothing to do: this attribute is primarily informational
666 + break;
667 +
668 + case BgpConstants.Update.Aggregator.TYPE:
669 + // Attribute Type Code AGGREGATOR
670 + Pair<Long, IpAddress> aggregator =
671 + parseAttributeTypeAggregator(ctx, attrTypeCode, attrLen,
672 + attrFlags, message);
673 + aggregatorAsNumber = aggregator.getLeft();
674 + aggregatorIpAddress = aggregator.getRight();
675 + break;
676 +
677 + default:
678 + // TODO: Parse any new Attribute Types if needed
679 + if (!optionalBit) {
680 + // ERROR: Unrecognized Well-known Attribute
681 + actionsBgpUpdateUnrecognizedWellKnownAttribute(
682 + ctx, attrTypeCode, attrLen, attrFlags, message);
683 + String errorMsg = "Unrecognized Well-known Attribute: " +
684 + attrTypeCode;
685 + throw new BgpParseException(errorMsg);
686 + }
687 +
688 + // Skip the data from the unrecognized attribute
689 + log.debug("BGP RX UPDATE message from {}: " +
690 + "Unrecognized Attribute Type {}",
691 + remoteAddress, attrTypeCode);
692 + message.skipBytes(attrLen);
693 + break;
694 + }
695 + }
696 +
697 + //
698 + // Verify the Well-known Attributes
699 + //
700 + verifyBgpUpdateWellKnownAttributes(ctx, origin, asPath, nextHop,
701 + localPref);
702 +
703 + //
704 + // Parse the NLRI (Network Layer Reachability Information)
705 + //
706 + Collection<IpPrefix> addedPrefixes = null;
707 + int nlriLength = message.readableBytes();
708 + try {
709 + addedPrefixes = parsePackedPrefixes(nlriLength, message);
710 + } catch (BgpParseException e) {
711 + // ERROR: Invalid Network Field
712 + log.debug("Exception parsing NLRI from BGP peer {}: ",
713 + remoteBgpId, e);
714 + actionsBgpUpdateInvalidNetworkField(ctx);
715 + // Rethrow the exception
716 + throw e;
717 + }
718 +
719 + // Generate the added routes
720 + for (IpPrefix prefix : addedPrefixes) {
721 + BgpRouteEntry bgpRouteEntry =
722 + new BgpRouteEntry(this, prefix, nextHop,
723 + origin.byteValue(), asPath, localPref);
724 + bgpRouteEntry.setMultiExitDisc(multiExitDisc);
725 + if (bgpRouteEntry.hasAsPathLoop(localAs)) {
726 + log.debug("BGP RX UPDATE message IGNORED from {}: {} " +
727 + "nextHop {}: contains AS Path loop",
728 + remoteAddress, prefix, nextHop);
729 + continue;
730 + } else {
731 + log.debug("BGP RX UPDATE message ADDED from {}: {} nextHop {}",
732 + remoteAddress, prefix, nextHop);
733 + }
734 + addedRoutes.put(prefix, bgpRouteEntry);
735 + }
736 +
737 + return addedRoutes.values();
738 + }
739 +
740 + /**
741 + * Verifies BGP UPDATE Well-known Attributes.
742 + *
743 + * @param ctx the Channel Handler Context
744 + * @param origin the ORIGIN well-known mandatory attribute
745 + * @param asPath the AS_PATH well-known mandatory attribute
746 + * @param nextHop the NEXT_HOP well-known mandatory attribute
747 + * @param localPref the LOCAL_PREF required attribute
748 + * @throws BgpParseException
749 + */
750 + private void verifyBgpUpdateWellKnownAttributes(
751 + ChannelHandlerContext ctx,
752 + Short origin,
753 + BgpRouteEntry.AsPath asPath,
754 + IpAddress nextHop,
755 + Long localPref)
756 + throws BgpParseException {
757 + //
758 + // Check for Missing Well-known Attributes
759 + //
760 + if ((origin == null) || (origin == -1)) {
761 + // Missing Attribute Type Code ORIGIN
762 + int type = BgpConstants.Update.Origin.TYPE;
763 + actionsBgpUpdateMissingWellKnownAttribute(ctx, type);
764 + String errorMsg = "Missing Well-known Attribute: ORIGIN";
765 + throw new BgpParseException(errorMsg);
766 + }
767 + if (asPath == null) {
768 + // Missing Attribute Type Code AS_PATH
769 + int type = BgpConstants.Update.AsPath.TYPE;
770 + actionsBgpUpdateMissingWellKnownAttribute(ctx, type);
771 + String errorMsg = "Missing Well-known Attribute: AS_PATH";
772 + throw new BgpParseException(errorMsg);
773 + }
774 + if (nextHop == null) {
775 + // Missing Attribute Type Code NEXT_HOP
776 + int type = BgpConstants.Update.NextHop.TYPE;
777 + actionsBgpUpdateMissingWellKnownAttribute(ctx, type);
778 + String errorMsg = "Missing Well-known Attribute: NEXT_HOP";
779 + throw new BgpParseException(errorMsg);
780 + }
781 + if (localPref == null) {
782 + // Missing Attribute Type Code LOCAL_PREF
783 + // NOTE: Required for iBGP
784 + int type = BgpConstants.Update.LocalPref.TYPE;
785 + actionsBgpUpdateMissingWellKnownAttribute(ctx, type);
786 + String errorMsg = "Missing Well-known Attribute: LOCAL_PREF";
787 + throw new BgpParseException(errorMsg);
788 + }
789 + }
790 +
791 + /**
792 + * Verifies the BGP UPDATE Attribute Flags.
793 + *
794 + * @param ctx the Channel Handler Context
795 + * @param attrTypeCode the attribute type code
796 + * @param attrLen the attribute length (in octets)
797 + * @param attrFlags the attribute flags
798 + * @param message the message to parse
799 + * @throws BgpParseException
800 + */
801 + private void verifyBgpUpdateAttributeFlags(
802 + ChannelHandlerContext ctx,
803 + int attrTypeCode,
804 + int attrLen,
805 + int attrFlags,
806 + ChannelBuffer message)
807 + throws BgpParseException {
808 +
809 + //
810 + // Assign the Attribute Type Name and the Well-known flag
811 + //
812 + String typeName = "UNKNOWN";
813 + boolean isWellKnown = false;
814 + switch (attrTypeCode) {
815 + case BgpConstants.Update.Origin.TYPE:
816 + isWellKnown = true;
817 + typeName = "ORIGIN";
818 + break;
819 + case BgpConstants.Update.AsPath.TYPE:
820 + isWellKnown = true;
821 + typeName = "AS_PATH";
822 + break;
823 + case BgpConstants.Update.NextHop.TYPE:
824 + isWellKnown = true;
825 + typeName = "NEXT_HOP";
826 + break;
827 + case BgpConstants.Update.MultiExitDisc.TYPE:
828 + isWellKnown = false;
829 + typeName = "MULTI_EXIT_DISC";
830 + break;
831 + case BgpConstants.Update.LocalPref.TYPE:
832 + isWellKnown = true;
833 + typeName = "LOCAL_PREF";
834 + break;
835 + case BgpConstants.Update.AtomicAggregate.TYPE:
836 + isWellKnown = true;
837 + typeName = "ATOMIC_AGGREGATE";
838 + break;
839 + case BgpConstants.Update.Aggregator.TYPE:
840 + isWellKnown = false;
841 + typeName = "AGGREGATOR";
842 + break;
843 + default:
844 + isWellKnown = false;
845 + typeName = "UNKNOWN(" + attrTypeCode + ")";
846 + break;
847 + }
848 +
849 + //
850 + // Verify the Attribute Flags
851 + //
852 + boolean optionalBit = ((0x80 & attrFlags) != 0);
853 + boolean transitiveBit = ((0x40 & attrFlags) != 0);
854 + boolean partialBit = ((0x20 & attrFlags) != 0);
855 + if ((isWellKnown && optionalBit) ||
856 + (isWellKnown && (!transitiveBit)) ||
857 + (isWellKnown && partialBit) ||
858 + (optionalBit && (!transitiveBit) && partialBit)) {
859 + //
860 + // ERROR: The Optional bit cannot be set for Well-known attributes
861 + // ERROR: The Transtive bit MUST be 1 for well-known attributes
862 + // ERROR: The Partial bit MUST be 0 for well-known attributes
863 + // ERROR: The Partial bit MUST be 0 for optional non-transitive
864 + // attributes
865 + //
866 + actionsBgpUpdateAttributeFlagsError(
867 + ctx, attrTypeCode, attrLen, attrFlags, message);
868 + String errorMsg = "Attribute Flags Error for " + typeName + ": " +
869 + attrFlags;
870 + throw new BgpParseException(errorMsg);
871 + }
872 + }
873 +
874 + /**
875 + * Parses BGP UPDATE Attribute Type ORIGIN.
876 + *
877 + * @param ctx the Channel Handler Context
878 + * @param attrTypeCode the attribute type code
879 + * @param attrLen the attribute length (in octets)
880 + * @param attrFlags the attribute flags
881 + * @param message the message to parse
882 + * @return the parsed ORIGIN value
883 + * @throws BgpParseException
884 + */
885 + private short parseAttributeTypeOrigin(
886 + ChannelHandlerContext ctx,
887 + int attrTypeCode,
888 + int attrLen,
889 + int attrFlags,
890 + ChannelBuffer message)
891 + throws BgpParseException {
892 +
893 + // Check the Attribute Length
894 + if (attrLen != BgpConstants.Update.Origin.LENGTH) {
895 + // ERROR: Attribute Length Error
896 + actionsBgpUpdateAttributeLengthError(
897 + ctx, attrTypeCode, attrLen, attrFlags, message);
898 + String errorMsg = "Attribute Length Error";
899 + throw new BgpParseException(errorMsg);
900 + }
901 +
902 + message.markReaderIndex();
903 + short origin = message.readUnsignedByte();
904 + switch (origin) {
905 + case BgpConstants.Update.Origin.IGP:
906 + // FALLTHROUGH
907 + case BgpConstants.Update.Origin.EGP:
908 + // FALLTHROUGH
909 + case BgpConstants.Update.Origin.INCOMPLETE:
910 + break;
911 + default:
912 + // ERROR: Invalid ORIGIN Attribute
913 + message.resetReaderIndex();
914 + actionsBgpUpdateInvalidOriginAttribute(
915 + ctx, attrTypeCode, attrLen, attrFlags, message, origin);
916 + String errorMsg = "Invalid ORIGIN Attribute: " + origin;
917 + throw new BgpParseException(errorMsg);
918 + }
919 +
920 + return origin;
921 + }
922 +
923 + /**
924 + * Parses BGP UPDATE Attribute AS Path.
925 + *
926 + * @param ctx the Channel Handler Context
927 + * @param attrTypeCode the attribute type code
928 + * @param attrLen the attribute length (in octets)
929 + * @param attrFlags the attribute flags
930 + * @param message the message to parse
931 + * @return the parsed AS Path
932 + * @throws BgpParseException
933 + */
934 + private BgpRouteEntry.AsPath parseAttributeTypeAsPath(
935 + ChannelHandlerContext ctx,
936 + int attrTypeCode,
937 + int attrLen,
938 + int attrFlags,
939 + ChannelBuffer message)
940 + throws BgpParseException {
941 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
942 +
943 + //
944 + // Parse the message
945 + //
946 + while (attrLen > 0) {
947 + if (attrLen < 2) {
948 + // ERROR: Malformed AS_PATH
949 + actionsBgpUpdateMalformedAsPath(ctx);
950 + String errorMsg = "Malformed AS Path";
951 + throw new BgpParseException(errorMsg);
952 + }
953 + // Get the Path Segment Type and Length (in number of ASes)
954 + short pathSegmentType = message.readUnsignedByte();
955 + short pathSegmentLength = message.readUnsignedByte();
956 + attrLen -= 2;
957 +
958 + // Verify the Path Segment Type
959 + switch (pathSegmentType) {
960 + case BgpConstants.Update.AsPath.AS_SET:
961 + // FALLTHROUGH
962 + case BgpConstants.Update.AsPath.AS_SEQUENCE:
963 + break;
964 + default:
965 + // ERROR: Invalid Path Segment Type
966 + //
967 + // NOTE: The BGP Spec (RFC 4271) doesn't contain Error Subcode
968 + // for "Invalid Path Segment Type", hence we return
969 + // the error as "Malformed AS_PATH".
970 + //
971 + actionsBgpUpdateMalformedAsPath(ctx);
972 + String errorMsg =
973 + "Invalid AS Path Segment Type: " + pathSegmentType;
974 + throw new BgpParseException(errorMsg);
975 + }
976 +
977 + // Parse the AS numbers
978 + if (2 * pathSegmentLength > attrLen) {
979 + // ERROR: Malformed AS_PATH
980 + actionsBgpUpdateMalformedAsPath(ctx);
981 + String errorMsg = "Malformed AS Path";
982 + throw new BgpParseException(errorMsg);
983 + }
984 + attrLen -= (2 * pathSegmentLength);
985 + ArrayList<Long> segmentAsNumbers = new ArrayList<>();
986 + while (pathSegmentLength-- > 0) {
987 + long asNumber = message.readUnsignedShort();
988 + segmentAsNumbers.add(asNumber);
989 + }
990 +
991 + BgpRouteEntry.PathSegment pathSegment =
992 + new BgpRouteEntry.PathSegment((byte) pathSegmentType,
993 + segmentAsNumbers);
994 + pathSegments.add(pathSegment);
995 + }
996 +
997 + return new BgpRouteEntry.AsPath(pathSegments);
998 + }
999 +
1000 + /**
1001 + * Parses BGP UPDATE Attribute Type NEXT_HOP.
1002 + *
1003 + * @param ctx the Channel Handler Context
1004 + * @param attrTypeCode the attribute type code
1005 + * @param attrLen the attribute length (in octets)
1006 + * @param attrFlags the attribute flags
1007 + * @param message the message to parse
1008 + * @return the parsed NEXT_HOP value
1009 + * @throws BgpParseException
1010 + */
1011 + private IpAddress parseAttributeTypeNextHop(
1012 + ChannelHandlerContext ctx,
1013 + int attrTypeCode,
1014 + int attrLen,
1015 + int attrFlags,
1016 + ChannelBuffer message)
1017 + throws BgpParseException {
1018 +
1019 + // Check the Attribute Length
1020 + if (attrLen != BgpConstants.Update.NextHop.LENGTH) {
1021 + // ERROR: Attribute Length Error
1022 + actionsBgpUpdateAttributeLengthError(
1023 + ctx, attrTypeCode, attrLen, attrFlags, message);
1024 + String errorMsg = "Attribute Length Error";
1025 + throw new BgpParseException(errorMsg);
1026 + }
1027 +
1028 + message.markReaderIndex();
1029 + long address = message.readUnsignedInt();
1030 + IpAddress nextHopAddress = IpAddress.valueOf((int) address);
1031 + //
1032 + // Check whether the NEXT_HOP IP address is semantically correct.
1033 + // As per RFC 4271, Section 6.3:
1034 + //
1035 + // a) It MUST NOT be the IP address of the receiving speaker
1036 + // b) In the case of an EBGP ....
1037 + //
1038 + // Here we check only (a), because (b) doesn't apply for us: all our
1039 + // peers are iBGP.
1040 + //
1041 + if (nextHopAddress.equals(localIp4Address)) {
1042 + // ERROR: Invalid NEXT_HOP Attribute
1043 + message.resetReaderIndex();
1044 + actionsBgpUpdateInvalidNextHopAttribute(
1045 + ctx, attrTypeCode, attrLen, attrFlags, message,
1046 + nextHopAddress);
1047 + String errorMsg = "Invalid NEXT_HOP Attribute: " + nextHopAddress;
1048 + throw new BgpParseException(errorMsg);
1049 + }
1050 +
1051 + return nextHopAddress;
1052 + }
1053 +
1054 + /**
1055 + * Parses BGP UPDATE Attribute Type MULTI_EXIT_DISC.
1056 + *
1057 + * @param ctx the Channel Handler Context
1058 + * @param attrTypeCode the attribute type code
1059 + * @param attrLen the attribute length (in octets)
1060 + * @param attrFlags the attribute flags
1061 + * @param message the message to parse
1062 + * @return the parsed MULTI_EXIT_DISC value
1063 + * @throws BgpParseException
1064 + */
1065 + private long parseAttributeTypeMultiExitDisc(
1066 + ChannelHandlerContext ctx,
1067 + int attrTypeCode,
1068 + int attrLen,
1069 + int attrFlags,
1070 + ChannelBuffer message)
1071 + throws BgpParseException {
1072 +
1073 + // Check the Attribute Length
1074 + if (attrLen != BgpConstants.Update.MultiExitDisc.LENGTH) {
1075 + // ERROR: Attribute Length Error
1076 + actionsBgpUpdateAttributeLengthError(
1077 + ctx, attrTypeCode, attrLen, attrFlags, message);
1078 + String errorMsg = "Attribute Length Error";
1079 + throw new BgpParseException(errorMsg);
1080 + }
1081 +
1082 + long multiExitDisc = message.readUnsignedInt();
1083 + return multiExitDisc;
1084 + }
1085 +
1086 + /**
1087 + * Parses BGP UPDATE Attribute Type LOCAL_PREF.
1088 + *
1089 + * @param ctx the Channel Handler Context
1090 + * @param attrTypeCode the attribute type code
1091 + * @param attrLen the attribute length (in octets)
1092 + * @param attrFlags the attribute flags
1093 + * @param message the message to parse
1094 + * @return the parsed LOCAL_PREF value
1095 + * @throws BgpParseException
1096 + */
1097 + private long parseAttributeTypeLocalPref(
1098 + ChannelHandlerContext ctx,
1099 + int attrTypeCode,
1100 + int attrLen,
1101 + int attrFlags,
1102 + ChannelBuffer message)
1103 + throws BgpParseException {
1104 +
1105 + // Check the Attribute Length
1106 + if (attrLen != BgpConstants.Update.LocalPref.LENGTH) {
1107 + // ERROR: Attribute Length Error
1108 + actionsBgpUpdateAttributeLengthError(
1109 + ctx, attrTypeCode, attrLen, attrFlags, message);
1110 + String errorMsg = "Attribute Length Error";
1111 + throw new BgpParseException(errorMsg);
1112 + }
1113 +
1114 + long localPref = message.readUnsignedInt();
1115 + return localPref;
1116 + }
1117 +
1118 + /**
1119 + * Parses BGP UPDATE Attribute Type ATOMIC_AGGREGATE.
1120 + *
1121 + * @param ctx the Channel Handler Context
1122 + * @param attrTypeCode the attribute type code
1123 + * @param attrLen the attribute length (in octets)
1124 + * @param attrFlags the attribute flags
1125 + * @param message the message to parse
1126 + * @throws BgpParseException
1127 + */
1128 + private void parseAttributeTypeAtomicAggregate(
1129 + ChannelHandlerContext ctx,
1130 + int attrTypeCode,
1131 + int attrLen,
1132 + int attrFlags,
1133 + ChannelBuffer message)
1134 + throws BgpParseException {
1135 +
1136 + // Check the Attribute Length
1137 + if (attrLen != BgpConstants.Update.AtomicAggregate.LENGTH) {
1138 + // ERROR: Attribute Length Error
1139 + actionsBgpUpdateAttributeLengthError(
1140 + ctx, attrTypeCode, attrLen, attrFlags, message);
1141 + String errorMsg = "Attribute Length Error";
1142 + throw new BgpParseException(errorMsg);
1143 + }
1144 +
1145 + // Nothing to do: this attribute is primarily informational
1146 + }
1147 +
1148 + /**
1149 + * Parses BGP UPDATE Attribute Type AGGREGATOR.
1150 + *
1151 + * @param ctx the Channel Handler Context
1152 + * @param attrTypeCode the attribute type code
1153 + * @param attrLen the attribute length (in octets)
1154 + * @param attrFlags the attribute flags
1155 + * @param message the message to parse
1156 + * @return the parsed AGGREGATOR value: a tuple of <AS-Number, IP-Address>
1157 + * @throws BgpParseException
1158 + */
1159 + private Pair<Long, IpAddress> parseAttributeTypeAggregator(
1160 + ChannelHandlerContext ctx,
1161 + int attrTypeCode,
1162 + int attrLen,
1163 + int attrFlags,
1164 + ChannelBuffer message)
1165 + throws BgpParseException {
1166 +
1167 + // Check the Attribute Length
1168 + if (attrLen != BgpConstants.Update.Aggregator.LENGTH) {
1169 + // ERROR: Attribute Length Error
1170 + actionsBgpUpdateAttributeLengthError(
1171 + ctx, attrTypeCode, attrLen, attrFlags, message);
1172 + String errorMsg = "Attribute Length Error";
1173 + throw new BgpParseException(errorMsg);
1174 + }
1175 +
1176 + // The AGGREGATOR AS number
1177 + long aggregatorAsNumber = message.readUnsignedShort();
1178 + // The AGGREGATOR IP address
1179 + long aggregatorAddress = message.readUnsignedInt();
1180 + IpAddress aggregatorIpAddress =
1181 + IpAddress.valueOf((int) aggregatorAddress);
1182 +
1183 + Pair<Long, IpAddress> aggregator = Pair.of(aggregatorAsNumber,
1184 + aggregatorIpAddress);
1185 + return aggregator;
1186 + }
1187 +
1188 + /**
1189 + * Parses a message that contains encoded IPv4 network prefixes.
1190 + * <p>
1191 + * The IPv4 prefixes are encoded in the form:
1192 + * <Length, Prefix> where Length is the length in bits of the IPv4 prefix,
1193 + * and Prefix is the IPv4 prefix (padded with trailing bits to the end
1194 + * of an octet).
1195 + *
1196 + * @param totalLength the total length of the data to parse
1197 + * @param message the message with data to parse
1198 + * @return a collection of parsed IPv4 network prefixes
1199 + * @throws BgpParseException
1200 + */
1201 + private Collection<IpPrefix> parsePackedPrefixes(int totalLength,
1202 + ChannelBuffer message)
1203 + throws BgpParseException {
1204 + Collection<IpPrefix> result = new ArrayList<>();
1205 +
1206 + if (totalLength == 0) {
1207 + return result;
1208 + }
1209 +
1210 + // Parse the data
1211 + int dataEnd = message.readerIndex() + totalLength;
1212 + while (message.readerIndex() < dataEnd) {
1213 + int prefixBitlen = message.readUnsignedByte();
1214 + int prefixBytelen = (prefixBitlen + 7) / 8; // Round-up
1215 + if (message.readerIndex() + prefixBytelen > dataEnd) {
1216 + String errorMsg = "Malformed Network Prefixes";
1217 + throw new BgpParseException(errorMsg);
1218 + }
1219 +
1220 + long address = 0;
1221 + long extraShift = (4 - prefixBytelen) * 8;
1222 + while (prefixBytelen > 0) {
1223 + address <<= 8;
1224 + address |= message.readUnsignedByte();
1225 + prefixBytelen--;
1226 + }
1227 + address <<= extraShift;
1228 + IpPrefix prefix =
1229 + IpPrefix.valueOf(IpAddress.valueOf((int) address).toInt(),
1230 + (short) prefixBitlen);
1231 + result.add(prefix);
1232 + }
1233 +
1234 + return result;
1235 + }
1236 +
1237 + /**
1238 + * Applies the appropriate actions after detecting BGP UPDATE
1239 + * Invalid Network Field Error: send NOTIFICATION and close the channel.
1240 + *
1241 + * @param ctx the Channel Handler Context
1242 + */
1243 + private void actionsBgpUpdateInvalidNetworkField(
1244 + ChannelHandlerContext ctx) {
1245 + log.debug("BGP RX UPDATE Error from {}: Invalid Network Field",
1246 + remoteAddress);
1247 +
1248 + //
1249 + // ERROR: Invalid Network Field
1250 + //
1251 + // Send NOTIFICATION and close the connection
1252 + int errorCode = UpdateMessageError.ERROR_CODE;
1253 + int errorSubcode = UpdateMessageError.INVALID_NETWORK_FIELD;
1254 + ChannelBuffer txMessage =
1255 + prepareBgpNotification(errorCode, errorSubcode, null);
1256 + ctx.getChannel().write(txMessage);
1257 + closeChannel(ctx);
1258 + }
1259 +
1260 + /**
1261 + * Applies the appropriate actions after detecting BGP UPDATE
1262 + * Malformed Attribute List Error: send NOTIFICATION and close the channel.
1263 + *
1264 + * @param ctx the Channel Handler Context
1265 + */
1266 + private void actionsBgpUpdateMalformedAttributeList(
1267 + ChannelHandlerContext ctx) {
1268 + log.debug("BGP RX UPDATE Error from {}: Malformed Attribute List",
1269 + remoteAddress);
1270 +
1271 + //
1272 + // ERROR: Malformed Attribute List
1273 + //
1274 + // Send NOTIFICATION and close the connection
1275 + int errorCode = UpdateMessageError.ERROR_CODE;
1276 + int errorSubcode = UpdateMessageError.MALFORMED_ATTRIBUTE_LIST;
1277 + ChannelBuffer txMessage =
1278 + prepareBgpNotification(errorCode, errorSubcode, null);
1279 + ctx.getChannel().write(txMessage);
1280 + closeChannel(ctx);
1281 + }
1282 +
1283 + /**
1284 + * Applies the appropriate actions after detecting BGP UPDATE
1285 + * Missing Well-known Attribute Error: send NOTIFICATION and close the
1286 + * channel.
1287 + *
1288 + * @param ctx the Channel Handler Context
1289 + * @param missingAttrTypeCode the missing attribute type code
1290 + */
1291 + private void actionsBgpUpdateMissingWellKnownAttribute(
1292 + ChannelHandlerContext ctx,
1293 + int missingAttrTypeCode) {
1294 + log.debug("BGP RX UPDATE Error from {}: Missing Well-known Attribute: {}",
1295 + remoteAddress, missingAttrTypeCode);
1296 +
1297 + //
1298 + // ERROR: Missing Well-known Attribute
1299 + //
1300 + // Send NOTIFICATION and close the connection
1301 + int errorCode = UpdateMessageError.ERROR_CODE;
1302 + int errorSubcode = UpdateMessageError.MISSING_WELL_KNOWN_ATTRIBUTE;
1303 + ChannelBuffer data = ChannelBuffers.buffer(1);
1304 + data.writeByte(missingAttrTypeCode);
1305 + ChannelBuffer txMessage =
1306 + prepareBgpNotification(errorCode, errorSubcode, data);
1307 + ctx.getChannel().write(txMessage);
1308 + closeChannel(ctx);
1309 + }
1310 +
1311 + /**
1312 + * Applies the appropriate actions after detecting BGP UPDATE
1313 + * Invalid ORIGIN Attribute Error: send NOTIFICATION and close the channel.
1314 + *
1315 + * @param ctx the Channel Handler Context
1316 + * @param attrTypeCode the attribute type code
1317 + * @param attrLen the attribute length (in octets)
1318 + * @param attrFlags the attribute flags
1319 + * @param message the message with the data
1320 + * @param origin the ORIGIN attribute value
1321 + */
1322 + private void actionsBgpUpdateInvalidOriginAttribute(
1323 + ChannelHandlerContext ctx,
1324 + int attrTypeCode,
1325 + int attrLen,
1326 + int attrFlags,
1327 + ChannelBuffer message,
1328 + short origin) {
1329 + log.debug("BGP RX UPDATE Error from {}: Invalid ORIGIN Attribute",
1330 + remoteAddress);
1331 +
1332 + //
1333 + // ERROR: Invalid ORIGIN Attribute
1334 + //
1335 + // Send NOTIFICATION and close the connection
1336 + int errorCode = UpdateMessageError.ERROR_CODE;
1337 + int errorSubcode = UpdateMessageError.INVALID_ORIGIN_ATTRIBUTE;
1338 + ChannelBuffer data =
1339 + prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1340 + attrFlags, message);
1341 + ChannelBuffer txMessage =
1342 + prepareBgpNotification(errorCode, errorSubcode, data);
1343 + ctx.getChannel().write(txMessage);
1344 + closeChannel(ctx);
1345 + }
1346 +
1347 + /**
1348 + * Applies the appropriate actions after detecting BGP UPDATE
1349 + * Attribute Flags Error: send NOTIFICATION and close the channel.
1350 + *
1351 + * @param ctx the Channel Handler Context
1352 + * @param attrTypeCode the attribute type code
1353 + * @param attrLen the attribute length (in octets)
1354 + * @param attrFlags the attribute flags
1355 + * @param message the message with the data
1356 + */
1357 + private void actionsBgpUpdateAttributeFlagsError(
1358 + ChannelHandlerContext ctx,
1359 + int attrTypeCode,
1360 + int attrLen,
1361 + int attrFlags,
1362 + ChannelBuffer message) {
1363 + log.debug("BGP RX UPDATE Error from {}: Attribute Flags Error",
1364 + remoteAddress);
1365 +
1366 + //
1367 + // ERROR: Attribute Flags Error
1368 + //
1369 + // Send NOTIFICATION and close the connection
1370 + int errorCode = UpdateMessageError.ERROR_CODE;
1371 + int errorSubcode = UpdateMessageError.ATTRIBUTE_FLAGS_ERROR;
1372 + ChannelBuffer data =
1373 + prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1374 + attrFlags, message);
1375 + ChannelBuffer txMessage =
1376 + prepareBgpNotification(errorCode, errorSubcode, data);
1377 + ctx.getChannel().write(txMessage);
1378 + closeChannel(ctx);
1379 + }
1380 +
1381 + /**
1382 + * Applies the appropriate actions after detecting BGP UPDATE
1383 + * Invalid NEXT_HOP Attribute Error: send NOTIFICATION and close the
1384 + * channel.
1385 + *
1386 + * @param ctx the Channel Handler Context
1387 + * @param attrTypeCode the attribute type code
1388 + * @param attrLen the attribute length (in octets)
1389 + * @param attrFlags the attribute flags
1390 + * @param message the message with the data
1391 + * @param nextHop the NEXT_HOP attribute value
1392 + */
1393 + private void actionsBgpUpdateInvalidNextHopAttribute(
1394 + ChannelHandlerContext ctx,
1395 + int attrTypeCode,
1396 + int attrLen,
1397 + int attrFlags,
1398 + ChannelBuffer message,
1399 + IpAddress nextHop) {
1400 + log.debug("BGP RX UPDATE Error from {}: Invalid NEXT_HOP Attribute {}",
1401 + remoteAddress, nextHop);
1402 +
1403 + //
1404 + // ERROR: Invalid ORIGIN Attribute
1405 + //
1406 + // Send NOTIFICATION and close the connection
1407 + int errorCode = UpdateMessageError.ERROR_CODE;
1408 + int errorSubcode = UpdateMessageError.INVALID_NEXT_HOP_ATTRIBUTE;
1409 + ChannelBuffer data =
1410 + prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1411 + attrFlags, message);
1412 + ChannelBuffer txMessage =
1413 + prepareBgpNotification(errorCode, errorSubcode, data);
1414 + ctx.getChannel().write(txMessage);
1415 + closeChannel(ctx);
1416 + }
1417 +
1418 + /**
1419 + * Applies the appropriate actions after detecting BGP UPDATE
1420 + * Unrecognized Well-known Attribute Error: send NOTIFICATION and close
1421 + * the channel.
1422 + *
1423 + * @param ctx the Channel Handler Context
1424 + * @param attrTypeCode the attribute type code
1425 + * @param attrLen the attribute length (in octets)
1426 + * @param attrFlags the attribute flags
1427 + * @param message the message with the data
1428 + */
1429 + private void actionsBgpUpdateUnrecognizedWellKnownAttribute(
1430 + ChannelHandlerContext ctx,
1431 + int attrTypeCode,
1432 + int attrLen,
1433 + int attrFlags,
1434 + ChannelBuffer message) {
1435 + log.debug("BGP RX UPDATE Error from {}: " +
1436 + "Unrecognized Well-known Attribute Error: {}",
1437 + remoteAddress, attrTypeCode);
1438 +
1439 + //
1440 + // ERROR: Unrecognized Well-known Attribute
1441 + //
1442 + // Send NOTIFICATION and close the connection
1443 + int errorCode = UpdateMessageError.ERROR_CODE;
1444 + int errorSubcode =
1445 + UpdateMessageError.UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE;
1446 + ChannelBuffer data =
1447 + prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1448 + attrFlags, message);
1449 + ChannelBuffer txMessage =
1450 + prepareBgpNotification(errorCode, errorSubcode, data);
1451 + ctx.getChannel().write(txMessage);
1452 + closeChannel(ctx);
1453 + }
1454 +
1455 + /**
1456 + * Applies the appropriate actions after detecting BGP UPDATE
1457 + * Attribute Length Error: send NOTIFICATION and close the channel.
1458 + *
1459 + * @param ctx the Channel Handler Context
1460 + * @param attrTypeCode the attribute type code
1461 + * @param attrLen the attribute length (in octets)
1462 + * @param attrFlags the attribute flags
1463 + * @param message the message with the data
1464 + */
1465 + private void actionsBgpUpdateAttributeLengthError(
1466 + ChannelHandlerContext ctx,
1467 + int attrTypeCode,
1468 + int attrLen,
1469 + int attrFlags,
1470 + ChannelBuffer message) {
1471 + log.debug("BGP RX UPDATE Error from {}: Attribute Length Error",
1472 + remoteAddress);
1473 +
1474 + //
1475 + // ERROR: Attribute Length Error
1476 + //
1477 + // Send NOTIFICATION and close the connection
1478 + int errorCode = UpdateMessageError.ERROR_CODE;
1479 + int errorSubcode = UpdateMessageError.ATTRIBUTE_LENGTH_ERROR;
1480 + ChannelBuffer data =
1481 + prepareBgpUpdateNotificationDataPayload(attrTypeCode, attrLen,
1482 + attrFlags, message);
1483 + ChannelBuffer txMessage =
1484 + prepareBgpNotification(errorCode, errorSubcode, data);
1485 + ctx.getChannel().write(txMessage);
1486 + closeChannel(ctx);
1487 + }
1488 +
1489 + /**
1490 + * Applies the appropriate actions after detecting BGP UPDATE
1491 + * Malformed AS_PATH Error: send NOTIFICATION and close the channel.
1492 + *
1493 + * @param ctx the Channel Handler Context
1494 + */
1495 + private void actionsBgpUpdateMalformedAsPath(
1496 + ChannelHandlerContext ctx) {
1497 + log.debug("BGP RX UPDATE Error from {}: Malformed AS Path",
1498 + remoteAddress);
1499 +
1500 + //
1501 + // ERROR: Malformed AS_PATH
1502 + //
1503 + // Send NOTIFICATION and close the connection
1504 + int errorCode = UpdateMessageError.ERROR_CODE;
1505 + int errorSubcode = UpdateMessageError.MALFORMED_AS_PATH;
1506 + ChannelBuffer txMessage =
1507 + prepareBgpNotification(errorCode, errorSubcode, null);
1508 + ctx.getChannel().write(txMessage);
1509 + closeChannel(ctx);
1510 + }
1511 +
1512 + /**
1513 + * Processes BGP NOTIFICATION message.
1514 + *
1515 + * @param ctx the Channel Handler Context
1516 + * @param message the message to process
1517 + */
1518 + void processBgpNotification(ChannelHandlerContext ctx,
1519 + ChannelBuffer message) {
1520 + int minLength =
1521 + BgpConstants.BGP_NOTIFICATION_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH;
1522 + if (message.readableBytes() < minLength) {
1523 + log.debug("BGP RX NOTIFICATION Error from {}: " +
1524 + "Message length {} too short. Must be at least {}",
1525 + remoteAddress, message.readableBytes(), minLength);
1526 + //
1527 + // ERROR: Bad Message Length
1528 + //
1529 + // NOTE: We do NOT send NOTIFICATION in response to a notification
1530 + return;
1531 + }
1532 +
1533 + //
1534 + // Parse the NOTIFICATION message
1535 + //
1536 + int errorCode = message.readUnsignedByte();
1537 + int errorSubcode = message.readUnsignedByte();
1538 + int dataLength = message.readableBytes();
1539 +
1540 + log.debug("BGP RX NOTIFICATION message from {}: Error Code {} " +
1541 + "Error Subcode {} Data Length {}",
1542 + remoteAddress, errorCode, errorSubcode, dataLength);
1543 +
1544 + //
1545 + // NOTE: If the peer sent a NOTIFICATION, we leave it to the peer to
1546 + // close the connection.
1547 + //
1548 +
1549 + // Start the Session Timeout timer
1550 + restartSessionTimeoutTimer(ctx);
1551 + }
1552 +
1553 + /**
1554 + * Processes BGP KEEPALIVE message.
1555 + *
1556 + * @param ctx the Channel Handler Context
1557 + * @param message the message to process
1558 + */
1559 + void processBgpKeepalive(ChannelHandlerContext ctx,
1560 + ChannelBuffer message) {
1561 + if (message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH !=
1562 + BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH) {
1563 + log.debug("BGP RX KEEPALIVE Error from {}: " +
1564 + "Invalid total message length {}. Expected {}",
1565 + remoteAddress,
1566 + message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH,
1567 + BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH);
1568 + //
1569 + // ERROR: Bad Message Length
1570 + //
1571 + // Send NOTIFICATION and close the connection
1572 + ChannelBuffer txMessage = prepareBgpNotificationBadMessageLength(
1573 + message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH);
1574 + ctx.getChannel().write(txMessage);
1575 + closeChannel(ctx);
1576 + return;
1577 + }
1578 +
1579 + //
1580 + // Parse the KEEPALIVE message: nothing to do
1581 + //
1582 + log.debug("BGP RX KEEPALIVE message from {}", remoteAddress);
1583 +
1584 + // Start the Session Timeout timer
1585 + restartSessionTimeoutTimer(ctx);
1586 + }
1587 +
1588 + /**
1589 + * Prepares BGP OPEN message.
1590 + *
1591 + * @return the message to transmit (BGP header included)
1592 + */
1593 + private ChannelBuffer prepareBgpOpen() {
1594 + ChannelBuffer message =
1595 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
1596 +
1597 + //
1598 + // Prepare the OPEN message payload
1599 + //
1600 + message.writeByte(localBgpVersion);
1601 + message.writeShort((int) localAs);
1602 + message.writeShort((int) localHoldtime);
1603 + message.writeInt(bgpSessionManager.getMyBgpId().toInt());
1604 + message.writeByte(0); // No Optional Parameters
1605 + return prepareBgpMessage(BgpConstants.BGP_TYPE_OPEN, message);
1606 + }
1607 +
1608 + /**
1609 + * Prepares BGP KEEPALIVE message.
1610 + *
1611 + * @return the message to transmit (BGP header included)
1612 + */
1613 + private ChannelBuffer prepareBgpKeepalive() {
1614 + ChannelBuffer message =
1615 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
1616 +
1617 + //
1618 + // Prepare the KEEPALIVE message payload: nothing to do
1619 + //
1620 + return prepareBgpMessage(BgpConstants.BGP_TYPE_KEEPALIVE, message);
1621 + }
1622 +
1623 + /**
1624 + * Prepares BGP NOTIFICATION message.
1625 + *
1626 + * @param errorCode the BGP NOTIFICATION Error Code
1627 + * @param errorSubcode the BGP NOTIFICATION Error Subcode if applicable,
1628 + * otherwise BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC
1629 + * @param payload the BGP NOTIFICATION Data if applicable, otherwise null
1630 + * @return the message to transmit (BGP header included)
1631 + */
1632 + ChannelBuffer prepareBgpNotification(int errorCode, int errorSubcode,
1633 + ChannelBuffer data) {
1634 + ChannelBuffer message =
1635 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
1636 +
1637 + //
1638 + // Prepare the NOTIFICATION message payload
1639 + //
1640 + message.writeByte(errorCode);
1641 + message.writeByte(errorSubcode);
1642 + if (data != null) {
1643 + message.writeBytes(data);
1644 + }
1645 + return prepareBgpMessage(BgpConstants.BGP_TYPE_NOTIFICATION, message);
1646 + }
1647 +
1648 + /**
1649 + * Prepares BGP NOTIFICATION message: Bad Message Length.
1650 + *
1651 + * @param length the erroneous Length field
1652 + * @return the message to transmit (BGP header included)
1653 + */
1654 + ChannelBuffer prepareBgpNotificationBadMessageLength(int length) {
1655 + int errorCode = MessageHeaderError.ERROR_CODE;
1656 + int errorSubcode = MessageHeaderError.BAD_MESSAGE_LENGTH;
1657 + ChannelBuffer data = ChannelBuffers.buffer(2);
1658 + data.writeShort(length);
1659 +
1660 + return prepareBgpNotification(errorCode, errorSubcode, data);
1661 + }
1662 +
1663 + /**
1664 + * Prepares BGP UPDATE Notification data payload.
1665 + *
1666 + * @param attrTypeCode the attribute type code
1667 + * @param attrLen the attribute length (in octets)
1668 + * @param attrFlags the attribute flags
1669 + * @param message the message with the data
1670 + * @return the buffer with the data payload for the BGP UPDATE Notification
1671 + */
1672 + private ChannelBuffer prepareBgpUpdateNotificationDataPayload(
1673 + int attrTypeCode,
1674 + int attrLen,
1675 + int attrFlags,
1676 + ChannelBuffer message) {
1677 + // Compute the attribute length field octets
1678 + boolean extendedLengthBit = ((0x10 & attrFlags) != 0);
1679 + int attrLenOctets = 1;
1680 + if (extendedLengthBit) {
1681 + attrLenOctets = 2;
1682 + }
1683 + ChannelBuffer data =
1684 + ChannelBuffers.buffer(attrLen + attrLenOctets + 1);
1685 + data.writeByte(attrTypeCode);
1686 + if (extendedLengthBit) {
1687 + data.writeShort(attrLen);
1688 + } else {
1689 + data.writeByte(attrLen);
1690 + }
1691 + data.writeBytes(message, attrLen);
1692 + return data;
1693 + }
1694 +
1695 + /**
1696 + * Prepares BGP message.
1697 + *
1698 + * @param type the BGP message type
1699 + * @param payload the message payload to transmit (BGP header excluded)
1700 + * @return the message to transmit (BGP header included)
1701 + */
1702 + private ChannelBuffer prepareBgpMessage(int type, ChannelBuffer payload) {
1703 + ChannelBuffer message =
1704 + ChannelBuffers.buffer(BgpConstants.BGP_HEADER_LENGTH +
1705 + payload.readableBytes());
1706 +
1707 + // Write the marker
1708 + for (int i = 0; i < BgpConstants.BGP_HEADER_MARKER_LENGTH; i++) {
1709 + message.writeByte(0xff);
1710 + }
1711 +
1712 + // Write the rest of the BGP header
1713 + message.writeShort(BgpConstants.BGP_HEADER_LENGTH +
1714 + payload.readableBytes());
1715 + message.writeByte(type);
1716 +
1717 + // Write the payload
1718 + message.writeBytes(payload);
1719 + return message;
1720 + }
1721 +
1722 + /**
1723 + * Restarts the BGP KeepaliveTimer.
1724 + */
1725 + private void restartKeepaliveTimer(ChannelHandlerContext ctx) {
1726 + if (localKeepaliveInterval == 0) {
1727 + return; // Nothing to do
1728 + }
1729 + keepaliveTimeout = timer.newTimeout(new TransmitKeepaliveTask(ctx),
1730 + localKeepaliveInterval,
1731 + TimeUnit.SECONDS);
1732 + }
1733 +
1734 + /**
1735 + * Task class for transmitting KEEPALIVE messages.
1736 + */
1737 + private final class TransmitKeepaliveTask implements TimerTask {
1738 + private final ChannelHandlerContext ctx;
1739 +
1740 + /**
1741 + * Constructor for given Channel Handler Context.
1742 + *
1743 + * @param ctx the Channel Handler Context to use
1744 + */
1745 + TransmitKeepaliveTask(ChannelHandlerContext ctx) {
1746 + this.ctx = ctx;
1747 + }
1748 +
1749 + @Override
1750 + public void run(Timeout timeout) throws Exception {
1751 + if (timeout.isCancelled()) {
1752 + return;
1753 + }
1754 + if (!ctx.getChannel().isOpen()) {
1755 + return;
1756 + }
1757 +
1758 + // Transmit the KEEPALIVE
1759 + ChannelBuffer txMessage = prepareBgpKeepalive();
1760 + ctx.getChannel().write(txMessage);
1761 +
1762 + // Restart the KEEPALIVE timer
1763 + restartKeepaliveTimer(ctx);
1764 + }
1765 + }
1766 +
1767 + /**
1768 + * Restarts the BGP Session Timeout Timer.
1769 + */
1770 + private void restartSessionTimeoutTimer(ChannelHandlerContext ctx) {
1771 + if (remoteHoldtime == 0) {
1772 + return; // Nothing to do
1773 + }
1774 + if (sessionTimeout != null) {
1775 + sessionTimeout.cancel();
1776 + }
1777 + sessionTimeout = timer.newTimeout(new SessionTimeoutTask(ctx),
1778 + remoteHoldtime,
1779 + TimeUnit.SECONDS);
1780 + }
1781 +
1782 + /**
1783 + * Task class for BGP Session timeout.
1784 + */
1785 + private final class SessionTimeoutTask implements TimerTask {
1786 + private final ChannelHandlerContext ctx;
1787 +
1788 + /**
1789 + * Constructor for given Channel Handler Context.
1790 + *
1791 + * @param ctx the Channel Handler Context to use
1792 + */
1793 + SessionTimeoutTask(ChannelHandlerContext ctx) {
1794 + this.ctx = ctx;
1795 + }
1796 +
1797 + @Override
1798 + public void run(Timeout timeout) throws Exception {
1799 + if (timeout.isCancelled()) {
1800 + return;
1801 + }
1802 + if (!ctx.getChannel().isOpen()) {
1803 + return;
1804 + }
1805 +
1806 + log.debug("BGP Session Timeout: peer {}", remoteAddress);
1807 + //
1808 + // ERROR: Invalid Optional Parameter Length field: Unspecific
1809 + //
1810 + // Send NOTIFICATION and close the connection
1811 + int errorCode = HoldTimerExpired.ERROR_CODE;
1812 + int errorSubcode = Notifications.ERROR_SUBCODE_UNSPECIFIC;
1813 + ChannelBuffer txMessage =
1814 + prepareBgpNotification(errorCode, errorSubcode, null);
1815 + ctx.getChannel().write(txMessage);
1816 + closeChannel(ctx);
1817 + }
1818 + }
1819 +
1820 + /**
1821 + * An exception indicating a parsing error of the BGP message.
1822 + */
1823 + private static class BgpParseException extends Exception {
1824 + /**
1825 + * Default constructor.
1826 + */
1827 + public BgpParseException() {
1828 + super();
1829 + }
1830 +
1831 + /**
1832 + * Constructor for a specific exception details message.
1833 + *
1834 + * @param message the message with the exception details
1835 + */
1836 + public BgpParseException(String message) {
1837 + super(message);
1838 + }
1839 + }
1840 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.net.InetAddress;
6 +import java.net.InetSocketAddress;
7 +import java.net.SocketAddress;
8 +import java.util.Collection;
9 +import java.util.concurrent.ConcurrentHashMap;
10 +import java.util.concurrent.ConcurrentMap;
11 +import java.util.concurrent.Executors;
12 +
13 +import org.jboss.netty.bootstrap.ServerBootstrap;
14 +import org.jboss.netty.channel.Channel;
15 +import org.jboss.netty.channel.ChannelException;
16 +import org.jboss.netty.channel.ChannelFactory;
17 +import org.jboss.netty.channel.ChannelPipeline;
18 +import org.jboss.netty.channel.ChannelPipelineFactory;
19 +import org.jboss.netty.channel.Channels;
20 +import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
21 +import org.onlab.onos.sdnip.RouteListener;
22 +import org.onlab.onos.sdnip.RouteUpdate;
23 +import org.onlab.packet.IpAddress;
24 +import org.onlab.packet.IpPrefix;
25 +import org.slf4j.Logger;
26 +import org.slf4j.LoggerFactory;
27 +
28 +/**
29 + * BGP Session Manager class.
30 + */
31 +public class BgpSessionManager {
32 + private static final Logger log =
33 + LoggerFactory.getLogger(BgpSessionManager.class);
34 + private Channel serverChannel; // Listener for incoming BGP connections
35 + private ConcurrentMap<SocketAddress, BgpSession> bgpSessions =
36 + new ConcurrentHashMap<>();
37 + private IpAddress myBgpId; // Same BGP ID for all peers
38 +
39 + private BgpRouteSelector bgpRouteSelector = new BgpRouteSelector();
40 + private ConcurrentMap<IpPrefix, BgpRouteEntry> bgpRoutes =
41 + new ConcurrentHashMap<>();
42 +
43 + private final RouteListener routeListener;
44 +
45 + /**
46 + * Constructor for given route listener.
47 + *
48 + * @param routeListener the route listener to use
49 + */
50 + public BgpSessionManager(RouteListener routeListener) {
51 + this.routeListener = checkNotNull(routeListener);
52 + }
53 +
54 + /**
55 + * Gets the BGP sessions.
56 + *
57 + * @return the BGP sessions
58 + */
59 + public Collection<BgpSession> getBgpSessions() {
60 + return bgpSessions.values();
61 + }
62 +
63 + /**
64 + * Gets the BGP routes.
65 + *
66 + * @return the BGP routes
67 + */
68 + public Collection<BgpRouteEntry> getBgpRoutes() {
69 + return bgpRoutes.values();
70 + }
71 +
72 + /**
73 + * Processes the connection from a BGP peer.
74 + *
75 + * @param bgpSession the BGP session for the peer
76 + * @return true if the connection can be established, otherwise false
77 + */
78 + boolean peerConnected(BgpSession bgpSession) {
79 +
80 + // Test whether there is already a session from the same remote
81 + if (bgpSessions.get(bgpSession.getRemoteAddress()) != null) {
82 + return false; // Duplicate BGP session
83 + }
84 + bgpSessions.put(bgpSession.getRemoteAddress(), bgpSession);
85 +
86 + //
87 + // If the first connection, set my BGP ID to the local address
88 + // of the socket.
89 + //
90 + if (bgpSession.getLocalAddress() instanceof InetSocketAddress) {
91 + InetAddress inetAddr =
92 + ((InetSocketAddress) bgpSession.getLocalAddress()).getAddress();
93 + IpAddress ip4Address = IpAddress.valueOf(inetAddr.getAddress());
94 + updateMyBgpId(ip4Address);
95 + }
96 + return true;
97 + }
98 +
99 + /**
100 + * Processes the disconnection from a BGP peer.
101 + *
102 + * @param bgpSession the BGP session for the peer
103 + */
104 + void peerDisconnected(BgpSession bgpSession) {
105 + bgpSessions.remove(bgpSession.getRemoteAddress());
106 + }
107 +
108 + /**
109 + * Conditionally updates the local BGP ID if it wasn't set already.
110 + * <p/>
111 + * NOTE: A BGP instance should use same BGP ID across all BGP sessions.
112 + *
113 + * @param ip4Address the IPv4 address to use as BGP ID
114 + */
115 + private synchronized void updateMyBgpId(IpAddress ip4Address) {
116 + if (myBgpId == null) {
117 + myBgpId = ip4Address;
118 + log.debug("BGP: My BGP ID is {}", myBgpId);
119 + }
120 + }
121 +
122 + /**
123 + * Gets the local BGP Identifier as an IPv4 address.
124 + *
125 + * @return the local BGP Identifier as an IPv4 address
126 + */
127 + IpAddress getMyBgpId() {
128 + return myBgpId;
129 + }
130 +
131 + /**
132 + * Gets the BGP Route Selector.
133 + *
134 + * @return the BGP Route Selector
135 + */
136 + BgpRouteSelector getBgpRouteSelector() {
137 + return bgpRouteSelector;
138 + }
139 +
140 + /**
141 + * Starts up BGP Session Manager operation.
142 + *
143 + * @param listenPortNumber the port number to listen on. By default
144 + * it should be BgpConstants.BGP_PORT (179)
145 + */
146 + public void startUp(int listenPortNumber) {
147 + log.debug("BGP Session Manager startUp()");
148 +
149 + ChannelFactory channelFactory =
150 + new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),
151 + Executors.newCachedThreadPool());
152 + ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() {
153 + @Override
154 + public ChannelPipeline getPipeline() throws Exception {
155 + // Allocate a new session per connection
156 + BgpSession bgpSessionHandler =
157 + new BgpSession(BgpSessionManager.this);
158 + BgpFrameDecoder bgpFrameDecoder =
159 + new BgpFrameDecoder(bgpSessionHandler);
160 +
161 + // Setup the processing pipeline
162 + ChannelPipeline pipeline = Channels.pipeline();
163 + pipeline.addLast("BgpFrameDecoder", bgpFrameDecoder);
164 + pipeline.addLast("BgpSession", bgpSessionHandler);
165 + return pipeline;
166 + }
167 + };
168 + InetSocketAddress listenAddress =
169 + new InetSocketAddress(listenPortNumber);
170 +
171 + ServerBootstrap serverBootstrap = new ServerBootstrap(channelFactory);
172 + // serverBootstrap.setOptions("reuseAddr", true);
173 + serverBootstrap.setOption("child.keepAlive", true);
174 + serverBootstrap.setOption("child.tcpNoDelay", true);
175 + serverBootstrap.setPipelineFactory(pipelineFactory);
176 + try {
177 + serverChannel = serverBootstrap.bind(listenAddress);
178 + } catch (ChannelException e) {
179 + log.debug("Exception binding to BGP port {}: ",
180 + listenAddress.getPort(), e);
181 + }
182 + }
183 +
184 + /**
185 + * Shuts down the BGP Session Manager operation.
186 + */
187 + public void shutDown() {
188 + // TODO: Complete the implementation: remove routes, etc.
189 + if (serverChannel != null) {
190 + serverChannel.close();
191 + }
192 + }
193 +
194 + /**
195 + * Class to receive and process the BGP routes from each BGP Session/Peer.
196 + */
197 + class BgpRouteSelector {
198 + /**
199 + * Processes route entry updates: added/updated and deleted route
200 + * entries.
201 + *
202 + * @param bgpSession the BGP session the route entry updates were
203 + * received on
204 + * @param addedBgpRouteEntries the added/updated route entries to
205 + * process
206 + * @param deletedBgpRouteEntries the deleted route entries to process
207 + */
208 + synchronized void routeUpdates(BgpSession bgpSession,
209 + Collection<BgpRouteEntry> addedBgpRouteEntries,
210 + Collection<BgpRouteEntry> deletedBgpRouteEntries) {
211 + //
212 + // TODO: Merge the updates from different BGP Peers,
213 + // by choosing the best route.
214 + //
215 +
216 + // Process the deleted route entries
217 + for (BgpRouteEntry bgpRouteEntry : deletedBgpRouteEntries) {
218 + processDeletedRoute(bgpSession, bgpRouteEntry);
219 + }
220 +
221 + // Process the added/updated route entries
222 + for (BgpRouteEntry bgpRouteEntry : addedBgpRouteEntries) {
223 + processAddedRoute(bgpSession, bgpRouteEntry);
224 + }
225 + }
226 +
227 + /**
228 + * Processes an added/updated route entry.
229 + *
230 + * @param bgpSession the BGP session the route entry update was
231 + * received on
232 + * @param bgpRouteEntry the added/updated route entry
233 + */
234 + private void processAddedRoute(BgpSession bgpSession,
235 + BgpRouteEntry bgpRouteEntry) {
236 + RouteUpdate routeUpdate;
237 + BgpRouteEntry bestBgpRouteEntry =
238 + bgpRoutes.get(bgpRouteEntry.prefix());
239 +
240 + //
241 + // Install the new route entry if it is better than the
242 + // current best route.
243 + //
244 + if ((bestBgpRouteEntry == null) ||
245 + bgpRouteEntry.isBetterThan(bestBgpRouteEntry)) {
246 + bgpRoutes.put(bgpRouteEntry.prefix(), bgpRouteEntry);
247 + routeUpdate =
248 + new RouteUpdate(RouteUpdate.Type.UPDATE, bgpRouteEntry);
249 + // Forward the result route updates to the Route Listener
250 + routeListener.update(routeUpdate);
251 + return;
252 + }
253 +
254 + //
255 + // If the route entry arrived on the same BGP Session as
256 + // the current best route, then elect the next best route
257 + // and install it.
258 + //
259 + if (bestBgpRouteEntry.getBgpSession() !=
260 + bgpRouteEntry.getBgpSession()) {
261 + return;
262 + }
263 +
264 + // Find the next best route
265 + bestBgpRouteEntry = findBestBgpRoute(bgpRouteEntry.prefix());
266 + if (bestBgpRouteEntry == null) {
267 + //
268 + // TODO: Shouldn't happen. Install the new route as a
269 + // pre-caution.
270 + //
271 + log.debug("BGP next best route for prefix {} is missing. " +
272 + "Adding the route that is currently processed.",
273 + bgpRouteEntry.prefix());
274 + bestBgpRouteEntry = bgpRouteEntry;
275 + }
276 + // Install the next best route
277 + bgpRoutes.put(bestBgpRouteEntry.prefix(), bestBgpRouteEntry);
278 + routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
279 + bestBgpRouteEntry);
280 + // Forward the result route updates to the Route Listener
281 + routeListener.update(routeUpdate);
282 + }
283 +
284 + /**
285 + * Processes a deleted route entry.
286 + *
287 + * @param bgpSession the BGP session the route entry update was
288 + * received on
289 + * @param bgpRouteEntry the deleted route entry
290 + */
291 + private void processDeletedRoute(BgpSession bgpSession,
292 + BgpRouteEntry bgpRouteEntry) {
293 + RouteUpdate routeUpdate;
294 + BgpRouteEntry bestBgpRouteEntry =
295 + bgpRoutes.get(bgpRouteEntry.prefix());
296 +
297 + //
298 + // Remove the route entry only if it was the best one.
299 + // Install the the next best route if it exists.
300 + //
301 + // NOTE: We intentionally use "==" instead of method equals(),
302 + // because we need to check whether this is same object.
303 + //
304 + if (bgpRouteEntry != bestBgpRouteEntry) {
305 + return; // Nothing to do
306 + }
307 +
308 + //
309 + // Find the next best route
310 + //
311 + bestBgpRouteEntry = findBestBgpRoute(bgpRouteEntry.prefix());
312 + if (bestBgpRouteEntry != null) {
313 + // Install the next best route
314 + bgpRoutes.put(bestBgpRouteEntry.prefix(),
315 + bestBgpRouteEntry);
316 + routeUpdate = new RouteUpdate(RouteUpdate.Type.UPDATE,
317 + bestBgpRouteEntry);
318 + // Forward the result route updates to the Route Listener
319 + routeListener.update(routeUpdate);
320 + return;
321 + }
322 +
323 + //
324 + // No route found. Remove the route entry
325 + //
326 + bgpRoutes.remove(bgpRouteEntry.prefix());
327 + routeUpdate = new RouteUpdate(RouteUpdate.Type.DELETE,
328 + bgpRouteEntry);
329 + // Forward the result route updates to the Route Listener
330 + routeListener.update(routeUpdate);
331 + }
332 +
333 + /**
334 + * Finds the best route entry among all BGP Sessions.
335 + *
336 + * @param prefix the prefix of the route
337 + * @return the best route if found, otherwise null
338 + */
339 + private BgpRouteEntry findBestBgpRoute(IpPrefix prefix) {
340 + BgpRouteEntry bestRoute = null;
341 +
342 + // Iterate across all BGP Sessions and select the best route
343 + for (BgpSession bgpSession : bgpSessions.values()) {
344 + BgpRouteEntry route = bgpSession.findBgpRouteEntry(prefix);
345 + if (route == null) {
346 + continue;
347 + }
348 + if ((bestRoute == null) || route.isBetterThan(bestRoute)) {
349 + bestRoute = route;
350 + }
351 + }
352 + return bestRoute;
353 + }
354 + }
355 +}
1 +/**
2 + * Implementation of the BGP protocol.
3 + */
4 +package org.onlab.onos.sdnip.bgp;
...\ No newline at end of file ...\ No newline at end of file
1 +package org.onlab.onos.sdnip.cli;
2 +
3 +import org.apache.karaf.shell.commands.Command;
4 +import org.onlab.onos.cli.AbstractShellCommand;
5 +import org.onlab.onos.sdnip.SdnIpService;
6 +import org.onlab.onos.sdnip.bgp.BgpConstants;
7 +import org.onlab.onos.sdnip.bgp.BgpRouteEntry;
8 +
9 +/**
10 + * Command to show the routes learned through BGP.
11 + */
12 +@Command(scope = "onos", name = "bgp-routes",
13 + description = "Lists all routes received from BGP")
14 +public class BgpRoutesListCommand extends AbstractShellCommand {
15 +
16 + private static final String FORMAT =
17 + "prefix=%s, nexthop=%s, origin=%s, localpref=%s, med=%s, aspath=%s, bgpid=%s";
18 +
19 + @Override
20 + protected void execute() {
21 + SdnIpService service = get(SdnIpService.class);
22 +
23 + for (BgpRouteEntry route : service.getBgpRoutes()) {
24 + printRoute(route);
25 + }
26 + }
27 +
28 + private void printRoute(BgpRouteEntry route) {
29 + if (route != null) {
30 + print(FORMAT, route.prefix(), route.nextHop(),
31 + originToString(route.getOrigin()), route.getLocalPref(),
32 + route.getMultiExitDisc(), route.getAsPath(),
33 + route.getBgpSession().getRemoteBgpId());
34 + }
35 + }
36 +
37 + private static String originToString(int origin) {
38 + String originString = "UNKNOWN";
39 +
40 + switch (origin) {
41 + case BgpConstants.Update.Origin.IGP:
42 + originString = "IGP";
43 + break;
44 + case BgpConstants.Update.Origin.EGP:
45 + originString = "EGP";
46 + break;
47 + case BgpConstants.Update.Origin.INCOMPLETE:
48 + originString = "INCOMPLETE";
49 + break;
50 + default:
51 + break;
52 + }
53 +
54 + return originString;
55 + }
56 +
57 +}
1 +package org.onlab.onos.sdnip.cli;
2 +
3 +import org.apache.karaf.shell.commands.Command;
4 +import org.onlab.onos.cli.AbstractShellCommand;
5 +import org.onlab.onos.sdnip.RouteEntry;
6 +import org.onlab.onos.sdnip.SdnIpService;
7 +
8 +/**
9 + * Command to show the list of routes in SDN-IP's routing table.
10 + */
11 +@Command(scope = "onos", name = "routes",
12 + description = "Lists all routes known to SDN-IP")
13 +public class RoutesListCommand extends AbstractShellCommand {
14 +
15 + private static final String FORMAT =
16 + "prefix=%s, nexthop=%s";
17 +
18 + @Override
19 + protected void execute() {
20 + SdnIpService service = get(SdnIpService.class);
21 +
22 + for (RouteEntry route : service.getRoutes()) {
23 + printRoute(route);
24 + }
25 + }
26 +
27 + private void printRoute(RouteEntry route) {
28 + if (route != null) {
29 + print(FORMAT, route.prefix(), route.nextHop());
30 + }
31 + }
32 +}
1 +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
2 +
3 + <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
4 + <command>
5 + <action class="org.onlab.onos.sdnip.cli.BgpRoutesListCommand"/>
6 + </command>
7 + <command>
8 + <action class="org.onlab.onos.sdnip.cli.RoutesListCommand"/>
9 + </command>
10 + </command-bundle>
11 +</blueprint>
1 +package org.onlab.onos.sdnip;
2 +
3 +import static org.hamcrest.Matchers.is;
4 +import static org.hamcrest.Matchers.not;
5 +import static org.junit.Assert.assertThat;
6 +
7 +import org.junit.Test;
8 +import org.onlab.packet.IpAddress;
9 +import org.onlab.packet.IpPrefix;
10 +
11 +/**
12 + * Unit tests for the RouteEntry class.
13 + */
14 +public class RouteEntryTest {
15 + /**
16 + * Tests valid class constructor.
17 + */
18 + @Test
19 + public void testConstructor() {
20 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
21 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
22 +
23 + RouteEntry routeEntry = new RouteEntry(prefix, nextHop);
24 + assertThat(routeEntry.toString(),
25 + is("RouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8}"));
26 + }
27 +
28 + /**
29 + * Tests invalid class constructor for null IPv4 prefix.
30 + */
31 + @Test(expected = NullPointerException.class)
32 + public void testInvalidConstructorNullPrefix() {
33 + IpPrefix prefix = null;
34 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
35 +
36 + new RouteEntry(prefix, nextHop);
37 + }
38 +
39 + /**
40 + * Tests invalid class constructor for null IPv4 next-hop.
41 + */
42 + @Test(expected = NullPointerException.class)
43 + public void testInvalidConstructorNullNextHop() {
44 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
45 + IpAddress nextHop = null;
46 +
47 + new RouteEntry(prefix, nextHop);
48 + }
49 +
50 + /**
51 + * Tests getting the fields of a route entry.
52 + */
53 + @Test
54 + public void testGetFields() {
55 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
56 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
57 +
58 + RouteEntry routeEntry = new RouteEntry(prefix, nextHop);
59 + assertThat(routeEntry.prefix(), is(prefix));
60 + assertThat(routeEntry.nextHop(), is(nextHop));
61 + }
62 +
63 + /**
64 + * Tests creating a binary string from IPv4 prefix.
65 + */
66 + @Test
67 + public void testCreateBinaryString() {
68 + IpPrefix prefix;
69 +
70 + prefix = IpPrefix.valueOf("0.0.0.0/0");
71 + assertThat(RouteEntry.createBinaryString(prefix), is(""));
72 +
73 + prefix = IpPrefix.valueOf("192.168.166.0/22");
74 + assertThat(RouteEntry.createBinaryString(prefix),
75 + is("1100000010101000101001"));
76 +
77 + prefix = IpPrefix.valueOf("192.168.166.0/23");
78 + assertThat(RouteEntry.createBinaryString(prefix),
79 + is("11000000101010001010011"));
80 +
81 + prefix = IpPrefix.valueOf("192.168.166.0/24");
82 + assertThat(RouteEntry.createBinaryString(prefix),
83 + is("110000001010100010100110"));
84 +
85 + prefix = IpPrefix.valueOf("130.162.10.1/25");
86 + assertThat(RouteEntry.createBinaryString(prefix),
87 + is("1000001010100010000010100"));
88 +
89 + prefix = IpPrefix.valueOf("255.255.255.255/32");
90 + assertThat(RouteEntry.createBinaryString(prefix),
91 + is("11111111111111111111111111111111"));
92 + }
93 +
94 + /**
95 + * Tests equality of {@link RouteEntry}.
96 + */
97 + @Test
98 + public void testEquality() {
99 + IpPrefix prefix1 = IpPrefix.valueOf("1.2.3.0/24");
100 + IpAddress nextHop1 = IpAddress.valueOf("5.6.7.8");
101 + RouteEntry routeEntry1 = new RouteEntry(prefix1, nextHop1);
102 +
103 + IpPrefix prefix2 = IpPrefix.valueOf("1.2.3.0/24");
104 + IpAddress nextHop2 = IpAddress.valueOf("5.6.7.8");
105 + RouteEntry routeEntry2 = new RouteEntry(prefix2, nextHop2);
106 +
107 + assertThat(routeEntry1, is(routeEntry2));
108 + }
109 +
110 + /**
111 + * Tests non-equality of {@link RouteEntry}.
112 + */
113 + @Test
114 + public void testNonEquality() {
115 + IpPrefix prefix1 = IpPrefix.valueOf("1.2.3.0/24");
116 + IpAddress nextHop1 = IpAddress.valueOf("5.6.7.8");
117 + RouteEntry routeEntry1 = new RouteEntry(prefix1, nextHop1);
118 +
119 + IpPrefix prefix2 = IpPrefix.valueOf("1.2.3.0/25"); // Different
120 + IpAddress nextHop2 = IpAddress.valueOf("5.6.7.8");
121 + RouteEntry routeEntry2 = new RouteEntry(prefix2, nextHop2);
122 +
123 + IpPrefix prefix3 = IpPrefix.valueOf("1.2.3.0/24");
124 + IpAddress nextHop3 = IpAddress.valueOf("5.6.7.9"); // Different
125 + RouteEntry routeEntry3 = new RouteEntry(prefix3, nextHop3);
126 +
127 + assertThat(routeEntry1, is(not(routeEntry2)));
128 + assertThat(routeEntry1, is(not(routeEntry3)));
129 + }
130 +
131 + /**
132 + * Tests object string representation.
133 + */
134 + @Test
135 + public void testToString() {
136 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
137 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
138 + RouteEntry routeEntry = new RouteEntry(prefix, nextHop);
139 +
140 + assertThat(routeEntry.toString(),
141 + is("RouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8}"));
142 + }
143 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import static org.hamcrest.Matchers.is;
4 +import static org.hamcrest.Matchers.not;
5 +import static org.junit.Assert.assertThat;
6 +
7 +import java.util.ArrayList;
8 +
9 +import org.junit.Test;
10 +
11 +/**
12 + * Unit tests for the BgpRouteEntry.AsPath class.
13 + */
14 +public class AsPathTest {
15 + /**
16 + * Generates an AS Path.
17 + *
18 + * @return a generated AS Path
19 + */
20 + private BgpRouteEntry.AsPath generateAsPath() {
21 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
22 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
23 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
24 + segmentAsNumbers1.add((long) 1);
25 + segmentAsNumbers1.add((long) 2);
26 + segmentAsNumbers1.add((long) 3);
27 + BgpRouteEntry.PathSegment pathSegment1 =
28 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
29 + pathSegments.add(pathSegment1);
30 + //
31 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
32 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
33 + segmentAsNumbers2.add((long) 4);
34 + segmentAsNumbers2.add((long) 5);
35 + segmentAsNumbers2.add((long) 6);
36 + BgpRouteEntry.PathSegment pathSegment2 =
37 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
38 + pathSegments.add(pathSegment2);
39 + //
40 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
41 +
42 + return asPath;
43 + }
44 +
45 + /**
46 + * Tests valid class constructor.
47 + */
48 + @Test
49 + public void testConstructor() {
50 + BgpRouteEntry.AsPath asPath = generateAsPath();
51 +
52 + String expectedString =
53 + "AsPath{pathSegments=" +
54 + "[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " +
55 + "PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}";
56 + assertThat(asPath.toString(), is(expectedString));
57 + }
58 +
59 + /**
60 + * Tests invalid class constructor for null Path Segments.
61 + */
62 + @Test(expected = NullPointerException.class)
63 + public void testInvalidConstructorNullPathSegments() {
64 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = null;
65 + new BgpRouteEntry.AsPath(pathSegments);
66 + }
67 +
68 + /**
69 + * Tests getting the fields of an AS Path.
70 + */
71 + @Test
72 + public void testGetFields() {
73 + // Create the fields to compare against
74 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
75 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
76 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
77 + segmentAsNumbers1.add((long) 1);
78 + segmentAsNumbers1.add((long) 2);
79 + segmentAsNumbers1.add((long) 3);
80 + BgpRouteEntry.PathSegment pathSegment1 =
81 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
82 + pathSegments.add(pathSegment1);
83 + //
84 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
85 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
86 + segmentAsNumbers2.add((long) 4);
87 + segmentAsNumbers2.add((long) 5);
88 + segmentAsNumbers2.add((long) 6);
89 + BgpRouteEntry.PathSegment pathSegment2 =
90 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
91 + pathSegments.add(pathSegment2);
92 +
93 + // Generate the entry to test
94 + BgpRouteEntry.AsPath asPath = generateAsPath();
95 +
96 + assertThat(asPath.getPathSegments(), is(pathSegments));
97 + }
98 +
99 + /**
100 + * Tests getting the AS Path Length.
101 + */
102 + @Test
103 + public void testGetAsPathLength() {
104 + BgpRouteEntry.AsPath asPath = generateAsPath();
105 + assertThat(asPath.getAsPathLength(), is(4));
106 +
107 + // Create an empty AS Path
108 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
109 + asPath = new BgpRouteEntry.AsPath(pathSegments);
110 + assertThat(asPath.getAsPathLength(), is(0));
111 + }
112 +
113 + /**
114 + * Tests equality of {@link BgpRouteEntry.AsPath}.
115 + */
116 + @Test
117 + public void testEquality() {
118 + BgpRouteEntry.AsPath asPath1 = generateAsPath();
119 + BgpRouteEntry.AsPath asPath2 = generateAsPath();
120 +
121 + assertThat(asPath1, is(asPath2));
122 + }
123 +
124 + /**
125 + * Tests non-equality of {@link BgpRouteEntry.AsPath}.
126 + */
127 + @Test
128 + public void testNonEquality() {
129 + BgpRouteEntry.AsPath asPath1 = generateAsPath();
130 +
131 + // Setup AS Path 2
132 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
133 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
134 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
135 + segmentAsNumbers1.add((long) 1);
136 + segmentAsNumbers1.add((long) 2);
137 + segmentAsNumbers1.add((long) 3);
138 + BgpRouteEntry.PathSegment pathSegment1 =
139 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
140 + pathSegments.add(pathSegment1);
141 + //
142 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
143 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
144 + segmentAsNumbers2.add((long) 4);
145 + segmentAsNumbers2.add((long) 55); // Different
146 + segmentAsNumbers2.add((long) 6);
147 + BgpRouteEntry.PathSegment pathSegment2 =
148 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
149 + pathSegments.add(pathSegment2);
150 + //
151 + BgpRouteEntry.AsPath asPath2 = new BgpRouteEntry.AsPath(pathSegments);
152 +
153 + assertThat(asPath1, is(not(asPath2)));
154 + }
155 +
156 + /**
157 + * Tests object string representation.
158 + */
159 + @Test
160 + public void testToString() {
161 + BgpRouteEntry.AsPath asPath = generateAsPath();
162 +
163 + String expectedString =
164 + "AsPath{pathSegments=" +
165 + "[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " +
166 + "PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}";
167 + assertThat(asPath.toString(), is(expectedString));
168 + }
169 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import static org.easymock.EasyMock.createMock;
4 +import static org.easymock.EasyMock.expect;
5 +import static org.easymock.EasyMock.replay;
6 +import static org.hamcrest.Matchers.is;
7 +import static org.hamcrest.Matchers.not;
8 +import static org.junit.Assert.assertThat;
9 +
10 +import java.util.ArrayList;
11 +
12 +import org.junit.Before;
13 +import org.junit.Test;
14 +import org.onlab.packet.IpAddress;
15 +import org.onlab.packet.IpPrefix;
16 +
17 +/**
18 + * Unit tests for the BgpRouteEntry class.
19 + */
20 +public class BgpRouteEntryTest {
21 + private BgpSession bgpSession;
22 + private static final IpAddress BGP_SESSION_BGP_ID =
23 + IpAddress.valueOf("10.0.0.1");
24 + private static final IpAddress BGP_SESSION_IP_ADDRESS =
25 + IpAddress.valueOf("20.0.0.1");
26 +
27 + private BgpSession bgpSession2;
28 + private static final IpAddress BGP_SESSION_BGP_ID2 =
29 + IpAddress.valueOf("10.0.0.2");
30 + private static final IpAddress BGP_SESSION_IP_ADDRESS2 =
31 + IpAddress.valueOf("20.0.0.1");
32 +
33 + private BgpSession bgpSession3;
34 + private static final IpAddress BGP_SESSION_BGP_ID3 =
35 + IpAddress.valueOf("10.0.0.1");
36 + private static final IpAddress BGP_SESSION_IP_ADDRESS3 =
37 + IpAddress.valueOf("20.0.0.2");
38 +
39 + @Before
40 + public void setUp() throws Exception {
41 + // Mock objects for testing
42 + bgpSession = createMock(BgpSession.class);
43 + bgpSession2 = createMock(BgpSession.class);
44 + bgpSession3 = createMock(BgpSession.class);
45 +
46 + // Setup the BGP Sessions
47 + expect(bgpSession.getRemoteBgpId())
48 + .andReturn(BGP_SESSION_BGP_ID).anyTimes();
49 + expect(bgpSession.getRemoteIp4Address())
50 + .andReturn(BGP_SESSION_IP_ADDRESS).anyTimes();
51 + //
52 + expect(bgpSession2.getRemoteBgpId())
53 + .andReturn(BGP_SESSION_BGP_ID2).anyTimes();
54 + expect(bgpSession2.getRemoteIp4Address())
55 + .andReturn(BGP_SESSION_IP_ADDRESS2).anyTimes();
56 + //
57 + expect(bgpSession3.getRemoteBgpId())
58 + .andReturn(BGP_SESSION_BGP_ID3).anyTimes();
59 + expect(bgpSession3.getRemoteIp4Address())
60 + .andReturn(BGP_SESSION_IP_ADDRESS3).anyTimes();
61 +
62 + replay(bgpSession);
63 + replay(bgpSession2);
64 + replay(bgpSession3);
65 + }
66 +
67 + /**
68 + * Generates a BGP Route Entry.
69 + *
70 + * @return a generated BGP Route Entry
71 + */
72 + private BgpRouteEntry generateBgpRouteEntry() {
73 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
74 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
75 + byte origin = BgpConstants.Update.Origin.IGP;
76 + // Setup the AS Path
77 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
78 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
79 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
80 + segmentAsNumbers1.add((long) 1);
81 + segmentAsNumbers1.add((long) 2);
82 + segmentAsNumbers1.add((long) 3);
83 + BgpRouteEntry.PathSegment pathSegment1 =
84 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
85 + pathSegments.add(pathSegment1);
86 + //
87 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
88 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
89 + segmentAsNumbers2.add((long) 4);
90 + segmentAsNumbers2.add((long) 5);
91 + segmentAsNumbers2.add((long) 6);
92 + BgpRouteEntry.PathSegment pathSegment2 =
93 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
94 + pathSegments.add(pathSegment2);
95 + //
96 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
97 + //
98 + long localPref = 100;
99 + long multiExitDisc = 20;
100 +
101 + BgpRouteEntry bgpRouteEntry =
102 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
103 + localPref);
104 + bgpRouteEntry.setMultiExitDisc(multiExitDisc);
105 +
106 + return bgpRouteEntry;
107 + }
108 +
109 + /**
110 + * Tests valid class constructor.
111 + */
112 + @Test
113 + public void testConstructor() {
114 + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
115 +
116 + String expectedString =
117 + "BgpRouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8, " +
118 + "bgpId=10.0.0.1, origin=0, asPath=AsPath{pathSegments=" +
119 + "[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " +
120 + "PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}, " +
121 + "localPref=100, multiExitDisc=20}";
122 + assertThat(bgpRouteEntry.toString(), is(expectedString));
123 + }
124 +
125 + /**
126 + * Tests invalid class constructor for null BGP Session.
127 + */
128 + @Test(expected = NullPointerException.class)
129 + public void testInvalidConstructorNullBgpSession() {
130 + BgpSession bgpSessionNull = null;
131 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
132 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
133 + byte origin = BgpConstants.Update.Origin.IGP;
134 + // Setup the AS Path
135 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
136 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
137 + //
138 + long localPref = 100;
139 +
140 + new BgpRouteEntry(bgpSessionNull, prefix, nextHop, origin, asPath,
141 + localPref);
142 + }
143 +
144 + /**
145 + * Tests invalid class constructor for null AS Path.
146 + */
147 + @Test(expected = NullPointerException.class)
148 + public void testInvalidConstructorNullAsPath() {
149 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
150 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
151 + byte origin = BgpConstants.Update.Origin.IGP;
152 + BgpRouteEntry.AsPath asPath = null;
153 + long localPref = 100;
154 +
155 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
156 + localPref);
157 + }
158 +
159 + /**
160 + * Tests getting the fields of a BGP route entry.
161 + */
162 + @Test
163 + public void testGetFields() {
164 + // Create the fields to compare against
165 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
166 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
167 + byte origin = BgpConstants.Update.Origin.IGP;
168 + // Setup the AS Path
169 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
170 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
171 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
172 + segmentAsNumbers1.add((long) 1);
173 + segmentAsNumbers1.add((long) 2);
174 + segmentAsNumbers1.add((long) 3);
175 + BgpRouteEntry.PathSegment pathSegment1 =
176 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
177 + pathSegments.add(pathSegment1);
178 + //
179 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
180 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
181 + segmentAsNumbers2.add((long) 4);
182 + segmentAsNumbers2.add((long) 5);
183 + segmentAsNumbers2.add((long) 6);
184 + BgpRouteEntry.PathSegment pathSegment2 =
185 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
186 + pathSegments.add(pathSegment2);
187 + //
188 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
189 + //
190 + long localPref = 100;
191 + long multiExitDisc = 20;
192 +
193 + // Generate the entry to test
194 + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
195 +
196 + assertThat(bgpRouteEntry.prefix(), is(prefix));
197 + assertThat(bgpRouteEntry.nextHop(), is(nextHop));
198 + assertThat(bgpRouteEntry.getBgpSession(), is(bgpSession));
199 + assertThat(bgpRouteEntry.getOrigin(), is(origin));
200 + assertThat(bgpRouteEntry.getAsPath(), is(asPath));
201 + assertThat(bgpRouteEntry.getLocalPref(), is(localPref));
202 + assertThat(bgpRouteEntry.getMultiExitDisc(), is(multiExitDisc));
203 + }
204 +
205 + /**
206 + * Tests whether a BGP route entry is a local route.
207 + */
208 + @Test
209 + public void testIsLocalRoute() {
210 + //
211 + // Test non-local route
212 + //
213 + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
214 + assertThat(bgpRouteEntry.isLocalRoute(), is(false));
215 +
216 + //
217 + // Test local route with AS Path that begins with AS_SET
218 + //
219 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
220 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
221 + byte origin = BgpConstants.Update.Origin.IGP;
222 + // Setup the AS Path
223 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
224 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SET;
225 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
226 + segmentAsNumbers1.add((long) 1);
227 + segmentAsNumbers1.add((long) 2);
228 + segmentAsNumbers1.add((long) 3);
229 + BgpRouteEntry.PathSegment pathSegment1 =
230 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
231 + pathSegments.add(pathSegment1);
232 + //
233 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
234 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
235 + segmentAsNumbers2.add((long) 4);
236 + segmentAsNumbers2.add((long) 5);
237 + segmentAsNumbers2.add((long) 6);
238 + BgpRouteEntry.PathSegment pathSegment2 =
239 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
240 + pathSegments.add(pathSegment2);
241 + //
242 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
243 + //
244 + long localPref = 100;
245 + long multiExitDisc = 20;
246 + //
247 + bgpRouteEntry =
248 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
249 + localPref);
250 + bgpRouteEntry.setMultiExitDisc(multiExitDisc);
251 + assertThat(bgpRouteEntry.isLocalRoute(), is(true));
252 +
253 + //
254 + // Test local route with empty AS Path
255 + //
256 + pathSegments = new ArrayList<>();
257 + asPath = new BgpRouteEntry.AsPath(pathSegments);
258 + bgpRouteEntry =
259 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
260 + localPref);
261 + bgpRouteEntry.setMultiExitDisc(multiExitDisc);
262 + assertThat(bgpRouteEntry.isLocalRoute(), is(true));
263 + }
264 +
265 + /**
266 + * Tests getting the BGP Neighbor AS number for a route.
267 + */
268 + @Test
269 + public void testGetNeighborAs() {
270 + //
271 + // Get neighbor AS for non-local route
272 + //
273 + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
274 + assertThat(bgpRouteEntry.getNeighborAs(), is((long) 1));
275 +
276 + //
277 + // Get neighbor AS for a local route
278 + //
279 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
280 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
281 + byte origin = BgpConstants.Update.Origin.IGP;
282 + // Setup the AS Path
283 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
284 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
285 + //
286 + long localPref = 100;
287 + long multiExitDisc = 20;
288 + //
289 + bgpRouteEntry =
290 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
291 + localPref);
292 + bgpRouteEntry.setMultiExitDisc(multiExitDisc);
293 + assertThat(bgpRouteEntry.getNeighborAs(), is(BgpConstants.BGP_AS_0));
294 + }
295 +
296 + /**
297 + * Tests whether a BGP route entry has AS Path loop.
298 + */
299 + @Test
300 + public void testHasAsPathLoop() {
301 + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
302 +
303 + // Test for loops: test each AS number in the interval [1, 6]
304 + for (int i = 1; i <= 6; i++) {
305 + assertThat(bgpRouteEntry.hasAsPathLoop(i), is(true));
306 + }
307 +
308 + // Test for non-loops
309 + assertThat(bgpRouteEntry.hasAsPathLoop(500), is(false));
310 + }
311 +
312 + /**
313 + * Tests the BGP Decision Process comparison of BGP routes.
314 + */
315 + @Test
316 + public void testBgpDecisionProcessComparison() {
317 + BgpRouteEntry bgpRouteEntry1 = generateBgpRouteEntry();
318 + BgpRouteEntry bgpRouteEntry2 = generateBgpRouteEntry();
319 +
320 + //
321 + // Compare two routes that are same
322 + //
323 + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
324 + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(true));
325 +
326 + //
327 + // Compare two routes with different LOCAL_PREF
328 + //
329 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
330 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
331 + byte origin = BgpConstants.Update.Origin.IGP;
332 + // Setup the AS Path
333 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
334 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
335 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
336 + segmentAsNumbers1.add((long) 1);
337 + segmentAsNumbers1.add((long) 2);
338 + segmentAsNumbers1.add((long) 3);
339 + BgpRouteEntry.PathSegment pathSegment1 =
340 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
341 + pathSegments.add(pathSegment1);
342 + //
343 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
344 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
345 + segmentAsNumbers2.add((long) 4);
346 + segmentAsNumbers2.add((long) 5);
347 + segmentAsNumbers2.add((long) 6);
348 + BgpRouteEntry.PathSegment pathSegment2 =
349 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
350 + pathSegments.add(pathSegment2);
351 + //
352 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
353 + //
354 + long localPref = 50; // Different
355 + long multiExitDisc = 20;
356 + bgpRouteEntry2 =
357 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
358 + localPref);
359 + bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
360 + //
361 + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
362 + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
363 + localPref = bgpRouteEntry1.getLocalPref(); // Restore
364 +
365 + //
366 + // Compare two routes with different AS_PATH length
367 + //
368 + ArrayList<BgpRouteEntry.PathSegment> pathSegments2 = new ArrayList<>();
369 + pathSegments2.add(pathSegment1);
370 + // Different AS Path
371 + BgpRouteEntry.AsPath asPath2 = new BgpRouteEntry.AsPath(pathSegments2);
372 + bgpRouteEntry2 =
373 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath2,
374 + localPref);
375 + bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
376 + //
377 + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(false));
378 + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(true));
379 +
380 + //
381 + // Compare two routes with different ORIGIN
382 + //
383 + origin = BgpConstants.Update.Origin.EGP; // Different
384 + bgpRouteEntry2 =
385 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
386 + localPref);
387 + bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
388 + //
389 + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
390 + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
391 + origin = bgpRouteEntry1.getOrigin(); // Restore
392 +
393 + //
394 + // Compare two routes with different MULTI_EXIT_DISC
395 + //
396 + multiExitDisc = 10; // Different
397 + bgpRouteEntry2 =
398 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
399 + localPref);
400 + bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
401 + //
402 + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
403 + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
404 + multiExitDisc = bgpRouteEntry1.getMultiExitDisc(); // Restore
405 +
406 + //
407 + // Compare two routes with different BGP ID
408 + //
409 + bgpRouteEntry2 =
410 + new BgpRouteEntry(bgpSession2, prefix, nextHop, origin, asPath,
411 + localPref);
412 + bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
413 + //
414 + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
415 + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
416 +
417 + //
418 + // Compare two routes with different BGP address
419 + //
420 + bgpRouteEntry2 =
421 + new BgpRouteEntry(bgpSession3, prefix, nextHop, origin, asPath,
422 + localPref);
423 + bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
424 + //
425 + assertThat(bgpRouteEntry1.isBetterThan(bgpRouteEntry2), is(true));
426 + assertThat(bgpRouteEntry2.isBetterThan(bgpRouteEntry1), is(false));
427 + }
428 +
429 + /**
430 + * Tests equality of {@link BgpRouteEntry}.
431 + */
432 + @Test
433 + public void testEquality() {
434 + BgpRouteEntry bgpRouteEntry1 = generateBgpRouteEntry();
435 + BgpRouteEntry bgpRouteEntry2 = generateBgpRouteEntry();
436 +
437 + assertThat(bgpRouteEntry1, is(bgpRouteEntry2));
438 + }
439 +
440 + /**
441 + * Tests non-equality of {@link BgpRouteEntry}.
442 + */
443 + @Test
444 + public void testNonEquality() {
445 + BgpRouteEntry bgpRouteEntry1 = generateBgpRouteEntry();
446 +
447 + // Setup BGP Route 2
448 + IpPrefix prefix = IpPrefix.valueOf("1.2.3.0/24");
449 + IpAddress nextHop = IpAddress.valueOf("5.6.7.8");
450 + byte origin = BgpConstants.Update.Origin.IGP;
451 + // Setup the AS Path
452 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
453 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
454 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
455 + segmentAsNumbers1.add((long) 1);
456 + segmentAsNumbers1.add((long) 2);
457 + segmentAsNumbers1.add((long) 3);
458 + BgpRouteEntry.PathSegment pathSegment1 =
459 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
460 + pathSegments.add(pathSegment1);
461 + //
462 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
463 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
464 + segmentAsNumbers2.add((long) 4);
465 + segmentAsNumbers2.add((long) 5);
466 + segmentAsNumbers2.add((long) 6);
467 + BgpRouteEntry.PathSegment pathSegment2 =
468 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
469 + pathSegments.add(pathSegment2);
470 + //
471 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
472 + //
473 + long localPref = 500; // Different
474 + long multiExitDisc = 20;
475 + BgpRouteEntry bgpRouteEntry2 =
476 + new BgpRouteEntry(bgpSession, prefix, nextHop, origin, asPath,
477 + localPref);
478 + bgpRouteEntry2.setMultiExitDisc(multiExitDisc);
479 +
480 + assertThat(bgpRouteEntry1, is(not(bgpRouteEntry2)));
481 + }
482 +
483 + /**
484 + * Tests object string representation.
485 + */
486 + @Test
487 + public void testToString() {
488 + BgpRouteEntry bgpRouteEntry = generateBgpRouteEntry();
489 +
490 + String expectedString =
491 + "BgpRouteEntry{prefix=1.2.3.0/24, nextHop=5.6.7.8, " +
492 + "bgpId=10.0.0.1, origin=0, asPath=AsPath{pathSegments=" +
493 + "[PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}, " +
494 + "PathSegment{type=1, segmentAsNumbers=[4, 5, 6]}]}, " +
495 + "localPref=100, multiExitDisc=20}";
496 + assertThat(bgpRouteEntry.toString(), is(expectedString));
497 + }
498 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import static org.hamcrest.Matchers.hasItem;
4 +import static org.hamcrest.Matchers.hasSize;
5 +import static org.hamcrest.Matchers.is;
6 +import static org.hamcrest.Matchers.notNullValue;
7 +import static org.junit.Assert.assertThat;
8 +
9 +import java.net.InetAddress;
10 +import java.net.InetSocketAddress;
11 +import java.net.SocketAddress;
12 +import java.util.ArrayList;
13 +import java.util.Collection;
14 +import java.util.LinkedList;
15 +import java.util.concurrent.Executors;
16 +import java.util.concurrent.TimeUnit;
17 +
18 +import org.jboss.netty.bootstrap.ClientBootstrap;
19 +import org.jboss.netty.buffer.ChannelBuffer;
20 +import org.jboss.netty.channel.Channel;
21 +import org.jboss.netty.channel.ChannelFactory;
22 +import org.jboss.netty.channel.ChannelPipeline;
23 +import org.jboss.netty.channel.ChannelPipelineFactory;
24 +import org.jboss.netty.channel.Channels;
25 +import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
26 +import org.junit.After;
27 +import org.junit.Before;
28 +import org.junit.Test;
29 +import org.onlab.onos.sdnip.RouteListener;
30 +import org.onlab.onos.sdnip.RouteUpdate;
31 +import org.onlab.packet.IpAddress;
32 +import org.onlab.packet.IpPrefix;
33 +import org.onlab.util.TestUtils;
34 +import org.onlab.util.TestUtils.TestUtilsException;
35 +
36 +import com.google.common.net.InetAddresses;
37 +
38 +/**
39 + * Unit tests for the BgpSessionManager class.
40 + */
41 +public class BgpSessionManagerTest {
42 + private static final IpAddress IP_LOOPBACK_ID =
43 + IpAddress.valueOf("127.0.0.1");
44 + private static final IpAddress BGP_PEER1_ID = IpAddress.valueOf("10.0.0.1");
45 + private static final long DEFAULT_LOCAL_PREF = 10;
46 + private static final long DEFAULT_MULTI_EXIT_DISC = 20;
47 +
48 + // The BGP Session Manager to test
49 + private BgpSessionManager bgpSessionManager;
50 +
51 + // Remote Peer state
52 + private ClientBootstrap peerBootstrap;
53 + private TestBgpPeerChannelHandler peerChannelHandler =
54 + new TestBgpPeerChannelHandler(BGP_PEER1_ID, DEFAULT_LOCAL_PREF);
55 + private TestBgpPeerFrameDecoder peerFrameDecoder =
56 + new TestBgpPeerFrameDecoder();
57 +
58 + // The socket that the Remote Peer should connect to
59 + private InetSocketAddress connectToSocket;
60 +
61 + private final DummyRouteListener dummyRouteListener =
62 + new DummyRouteListener();
63 +
64 + /**
65 + * Dummy implementation for the RouteListener interface.
66 + */
67 + private class DummyRouteListener implements RouteListener {
68 + @Override
69 + public void update(RouteUpdate routeUpdate) {
70 + // Nothing to do
71 + }
72 + }
73 +
74 + @Before
75 + public void setUp() throws Exception {
76 + //
77 + // Setup the BGP Session Manager to test, and start listening for BGP
78 + // connections.
79 + //
80 + bgpSessionManager = new BgpSessionManager(dummyRouteListener);
81 + // NOTE: We use port 0 to bind on any available port
82 + bgpSessionManager.startUp(0);
83 +
84 + // Get the port number the BGP Session Manager is listening on
85 + Channel serverChannel = TestUtils.getField(bgpSessionManager,
86 + "serverChannel");
87 + SocketAddress socketAddress = serverChannel.getLocalAddress();
88 + InetSocketAddress inetSocketAddress =
89 + (InetSocketAddress) socketAddress;
90 +
91 + //
92 + // Setup the BGP Peer, i.e., the "remote" BGP router that will
93 + // initiate the BGP connection, send BGP UPDATE messages, etc.
94 + //
95 + ChannelFactory channelFactory =
96 + new NioClientSocketChannelFactory(Executors.newCachedThreadPool(),
97 + Executors.newCachedThreadPool());
98 + ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory() {
99 + @Override
100 + public ChannelPipeline getPipeline() throws Exception {
101 + // Setup the transmitting pipeline
102 + ChannelPipeline pipeline = Channels.pipeline();
103 + pipeline.addLast("TestBgpPeerFrameDecoder",
104 + peerFrameDecoder);
105 + pipeline.addLast("TestBgpPeerChannelHandler",
106 + peerChannelHandler);
107 + return pipeline;
108 + }
109 + };
110 +
111 + peerBootstrap = new ClientBootstrap(channelFactory);
112 + peerBootstrap.setOption("child.keepAlive", true);
113 + peerBootstrap.setOption("child.tcpNoDelay", true);
114 + peerBootstrap.setPipelineFactory(pipelineFactory);
115 +
116 + InetAddress connectToAddress = InetAddresses.forString("127.0.0.1");
117 + connectToSocket = new InetSocketAddress(connectToAddress,
118 + inetSocketAddress.getPort());
119 + }
120 +
121 + @After
122 + public void tearDown() throws Exception {
123 + bgpSessionManager.shutDown();
124 + bgpSessionManager = null;
125 + }
126 +
127 + /**
128 + * Gets BGP RIB-IN routes by waiting until they are received.
129 + * <p/>
130 + * NOTE: We keep checking once a second the number of received routes,
131 + * up to 5 seconds.
132 + *
133 + * @param bgpSession the BGP session that is expected to receive the
134 + * routes
135 + * @param expectedRoutes the expected number of routes
136 + * @return the BGP RIB-IN routes as received within the expected
137 + * time interval
138 + */
139 + private Collection<BgpRouteEntry> waitForBgpRibIn(BgpSession bgpSession,
140 + long expectedRoutes)
141 + throws InterruptedException {
142 + Collection<BgpRouteEntry> bgpRibIn = bgpSession.getBgpRibIn();
143 +
144 + final int maxChecks = 5; // Max wait of 5 seconds
145 + for (int i = 0; i < maxChecks; i++) {
146 + if (bgpRibIn.size() == expectedRoutes) {
147 + break;
148 + }
149 + Thread.sleep(1000);
150 + bgpRibIn = bgpSession.getBgpRibIn();
151 + }
152 +
153 + return bgpRibIn;
154 + }
155 +
156 + /**
157 + * Gets BGP merged routes by waiting until they are received.
158 + * <p/>
159 + * NOTE: We keep checking once a second the number of received routes,
160 + * up to 5 seconds.
161 + *
162 + * @param expectedRoutes the expected number of routes
163 + * @return the BGP Session Manager routes as received within the expected
164 + * time interval
165 + */
166 + private Collection<BgpRouteEntry> waitForBgpRoutes(long expectedRoutes)
167 + throws InterruptedException {
168 + Collection<BgpRouteEntry> bgpRoutes = bgpSessionManager.getBgpRoutes();
169 +
170 + final int maxChecks = 5; // Max wait of 5 seconds
171 + for (int i = 0; i < maxChecks; i++) {
172 + if (bgpRoutes.size() == expectedRoutes) {
173 + break;
174 + }
175 + Thread.sleep(1000);
176 + bgpRoutes = bgpSessionManager.getBgpRoutes();
177 + }
178 +
179 + return bgpRoutes;
180 + }
181 +
182 + /**
183 + * Tests that the BGP OPEN messages have been exchanged, followed by
184 + * KEEPALIVE.
185 + * <p>
186 + * The BGP Peer opens the sessions and transmits OPEN Message, eventually
187 + * followed by KEEPALIVE. The tested BGP listener should respond by
188 + * OPEN Message, followed by KEEPALIVE.
189 + *
190 + * @throws TestUtilsException TestUtils error
191 + */
192 + @Test
193 + public void testExchangedBgpOpenMessages()
194 + throws InterruptedException, TestUtilsException {
195 + // Initiate the connection
196 + peerBootstrap.connect(connectToSocket);
197 +
198 + // Wait until the OPEN message is received
199 + peerFrameDecoder.receivedOpenMessageLatch.await(2000,
200 + TimeUnit.MILLISECONDS);
201 + // Wait until the KEEPALIVE message is received
202 + peerFrameDecoder.receivedKeepaliveMessageLatch.await(2000,
203 + TimeUnit.MILLISECONDS);
204 +
205 + //
206 + // Test the fields from the BGP OPEN message:
207 + // BGP version, AS number, BGP ID
208 + //
209 + assertThat(peerFrameDecoder.remoteBgpVersion,
210 + is(BgpConstants.BGP_VERSION));
211 + assertThat(peerFrameDecoder.remoteAs,
212 + is(TestBgpPeerChannelHandler.PEER_AS));
213 + assertThat(peerFrameDecoder.remoteBgpIdentifier, is(IP_LOOPBACK_ID));
214 +
215 + //
216 + // Test that a BgpSession instance has been created
217 + //
218 + assertThat(bgpSessionManager.getMyBgpId(), is(IP_LOOPBACK_ID));
219 + assertThat(bgpSessionManager.getBgpSessions(), hasSize(1));
220 + BgpSession bgpSession =
221 + bgpSessionManager.getBgpSessions().iterator().next();
222 + assertThat(bgpSession, notNullValue());
223 + long sessionAs = TestUtils.getField(bgpSession, "localAs");
224 + assertThat(sessionAs, is(TestBgpPeerChannelHandler.PEER_AS));
225 + }
226 +
227 + /**
228 + * Tests that the BGP UPDATE messages have been received and processed.
229 + */
230 + @Test
231 + public void testProcessedBgpUpdateMessages() throws InterruptedException {
232 + BgpSession bgpSession;
233 + IpAddress nextHopRouter;
234 + BgpRouteEntry bgpRouteEntry;
235 + ChannelBuffer message;
236 + Collection<BgpRouteEntry> bgpRibIn;
237 + Collection<BgpRouteEntry> bgpRoutes;
238 +
239 + // Initiate the connection
240 + peerBootstrap.connect(connectToSocket);
241 +
242 + // Wait until the OPEN message is received
243 + peerFrameDecoder.receivedOpenMessageLatch.await(2000,
244 + TimeUnit.MILLISECONDS);
245 + // Wait until the KEEPALIVE message is received
246 + peerFrameDecoder.receivedKeepaliveMessageLatch.await(2000,
247 + TimeUnit.MILLISECONDS);
248 +
249 + // Get the BGP Session handler
250 + bgpSession = bgpSessionManager.getBgpSessions().iterator().next();
251 +
252 + // Prepare routes to add/delete
253 + nextHopRouter = IpAddress.valueOf("10.20.30.40");
254 + Collection<IpPrefix> addedRoutes = new LinkedList<>();
255 + Collection<IpPrefix> withdrawnRoutes = new LinkedList<>();
256 + addedRoutes.add(IpPrefix.valueOf("0.0.0.0/0"));
257 + addedRoutes.add(IpPrefix.valueOf("20.0.0.0/8"));
258 + addedRoutes.add(IpPrefix.valueOf("30.0.0.0/16"));
259 + addedRoutes.add(IpPrefix.valueOf("40.0.0.0/24"));
260 + addedRoutes.add(IpPrefix.valueOf("50.0.0.0/32"));
261 + withdrawnRoutes.add(IpPrefix.valueOf("60.0.0.0/8"));
262 + withdrawnRoutes.add(IpPrefix.valueOf("70.0.0.0/16"));
263 + withdrawnRoutes.add(IpPrefix.valueOf("80.0.0.0/24"));
264 + withdrawnRoutes.add(IpPrefix.valueOf("90.0.0.0/32"));
265 + // Write the routes
266 + message = peerChannelHandler.prepareBgpUpdate(nextHopRouter,
267 + addedRoutes,
268 + withdrawnRoutes);
269 + peerChannelHandler.savedCtx.getChannel().write(message);
270 +
271 + // Check that the routes have been received, processed and stored
272 + bgpRibIn = waitForBgpRibIn(bgpSession, 5);
273 + assertThat(bgpRibIn, hasSize(5));
274 + bgpRoutes = waitForBgpRoutes(5);
275 + assertThat(bgpRoutes, hasSize(5));
276 +
277 + // Setup the AS Path
278 + ArrayList<BgpRouteEntry.PathSegment> pathSegments = new ArrayList<>();
279 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
280 + ArrayList<Long> segmentAsNumbers1 = new ArrayList<>();
281 + segmentAsNumbers1.add((long) 65010);
282 + segmentAsNumbers1.add((long) 65020);
283 + segmentAsNumbers1.add((long) 65030);
284 + BgpRouteEntry.PathSegment pathSegment1 =
285 + new BgpRouteEntry.PathSegment(pathSegmentType1, segmentAsNumbers1);
286 + pathSegments.add(pathSegment1);
287 + //
288 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
289 + ArrayList<Long> segmentAsNumbers2 = new ArrayList<>();
290 + segmentAsNumbers2.add((long) 65041);
291 + segmentAsNumbers2.add((long) 65042);
292 + segmentAsNumbers2.add((long) 65043);
293 + BgpRouteEntry.PathSegment pathSegment2 =
294 + new BgpRouteEntry.PathSegment(pathSegmentType2, segmentAsNumbers2);
295 + pathSegments.add(pathSegment2);
296 + //
297 + BgpRouteEntry.AsPath asPath = new BgpRouteEntry.AsPath(pathSegments);
298 +
299 + //
300 + bgpRouteEntry =
301 + new BgpRouteEntry(bgpSession,
302 + IpPrefix.valueOf("0.0.0.0/0"),
303 + nextHopRouter,
304 + (byte) BgpConstants.Update.Origin.IGP,
305 + asPath,
306 + DEFAULT_LOCAL_PREF);
307 + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
308 + assertThat(bgpRibIn, hasItem(bgpRouteEntry));
309 + //
310 + bgpRouteEntry =
311 + new BgpRouteEntry(bgpSession,
312 + IpPrefix.valueOf("20.0.0.0/8"),
313 + nextHopRouter,
314 + (byte) BgpConstants.Update.Origin.IGP,
315 + asPath,
316 + DEFAULT_LOCAL_PREF);
317 + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
318 + assertThat(bgpRibIn, hasItem(bgpRouteEntry));
319 + //
320 + bgpRouteEntry =
321 + new BgpRouteEntry(bgpSession,
322 + IpPrefix.valueOf("30.0.0.0/16"),
323 + nextHopRouter,
324 + (byte) BgpConstants.Update.Origin.IGP,
325 + asPath,
326 + DEFAULT_LOCAL_PREF);
327 + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
328 + assertThat(bgpRibIn, hasItem(bgpRouteEntry));
329 + //
330 + bgpRouteEntry =
331 + new BgpRouteEntry(bgpSession,
332 + IpPrefix.valueOf("40.0.0.0/24"),
333 + nextHopRouter,
334 + (byte) BgpConstants.Update.Origin.IGP,
335 + asPath,
336 + DEFAULT_LOCAL_PREF);
337 + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
338 + assertThat(bgpRibIn, hasItem(bgpRouteEntry));
339 + //
340 + bgpRouteEntry =
341 + new BgpRouteEntry(bgpSession,
342 + IpPrefix.valueOf("50.0.0.0/32"),
343 + nextHopRouter,
344 + (byte) BgpConstants.Update.Origin.IGP,
345 + asPath,
346 + DEFAULT_LOCAL_PREF);
347 + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
348 + assertThat(bgpRibIn, hasItem(bgpRouteEntry));
349 +
350 + // Delete some routes
351 + addedRoutes = new LinkedList<>();
352 + withdrawnRoutes = new LinkedList<>();
353 + withdrawnRoutes.add(IpPrefix.valueOf("0.0.0.0/0"));
354 + withdrawnRoutes.add(IpPrefix.valueOf("50.0.0.0/32"));
355 +
356 + // Write the routes
357 + message = peerChannelHandler.prepareBgpUpdate(nextHopRouter,
358 + addedRoutes,
359 + withdrawnRoutes);
360 + peerChannelHandler.savedCtx.getChannel().write(message);
361 +
362 + // Check that the routes have been received, processed and stored
363 + bgpRibIn = waitForBgpRibIn(bgpSession, 3);
364 + assertThat(bgpRibIn, hasSize(3));
365 + bgpRoutes = waitForBgpRoutes(3);
366 + assertThat(bgpRoutes, hasSize(3));
367 + //
368 + bgpRouteEntry =
369 + new BgpRouteEntry(bgpSession,
370 + IpPrefix.valueOf("20.0.0.0/8"),
371 + nextHopRouter,
372 + (byte) BgpConstants.Update.Origin.IGP,
373 + asPath,
374 + DEFAULT_LOCAL_PREF);
375 + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
376 + assertThat(bgpRibIn, hasItem(bgpRouteEntry));
377 + //
378 + bgpRouteEntry =
379 + new BgpRouteEntry(bgpSession,
380 + IpPrefix.valueOf("30.0.0.0/16"),
381 + nextHopRouter,
382 + (byte) BgpConstants.Update.Origin.IGP,
383 + asPath,
384 + DEFAULT_LOCAL_PREF);
385 + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
386 + assertThat(bgpRibIn, hasItem(bgpRouteEntry));
387 + //
388 + bgpRouteEntry =
389 + new BgpRouteEntry(bgpSession,
390 + IpPrefix.valueOf("40.0.0.0/24"),
391 + nextHopRouter,
392 + (byte) BgpConstants.Update.Origin.IGP,
393 + asPath,
394 + DEFAULT_LOCAL_PREF);
395 + bgpRouteEntry.setMultiExitDisc(DEFAULT_MULTI_EXIT_DISC);
396 + assertThat(bgpRibIn, hasItem(bgpRouteEntry));
397 +
398 + // Close the channel and test there are no routes
399 + peerChannelHandler.closeChannel();
400 + bgpRoutes = waitForBgpRoutes(0);
401 + assertThat(bgpRoutes, hasSize(0));
402 + }
403 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import static org.hamcrest.Matchers.is;
4 +import static org.hamcrest.Matchers.not;
5 +import static org.junit.Assert.assertThat;
6 +
7 +import java.util.ArrayList;
8 +
9 +import org.junit.Test;
10 +
11 +/**
12 + * Unit tests for the BgpRouteEntry.PathSegment class.
13 + */
14 +public class PathSegmentTest {
15 + /**
16 + * Generates a Path Segment.
17 + *
18 + * @return a generated PathSegment
19 + */
20 + private BgpRouteEntry.PathSegment generatePathSegment() {
21 + byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
22 + ArrayList<Long> segmentAsNumbers = new ArrayList<>();
23 + segmentAsNumbers.add((long) 1);
24 + segmentAsNumbers.add((long) 2);
25 + segmentAsNumbers.add((long) 3);
26 + BgpRouteEntry.PathSegment pathSegment =
27 + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
28 +
29 + return pathSegment;
30 + }
31 +
32 + /**
33 + * Tests valid class constructor.
34 + */
35 + @Test
36 + public void testConstructor() {
37 + BgpRouteEntry.PathSegment pathSegment = generatePathSegment();
38 +
39 + String expectedString =
40 + "PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}";
41 + assertThat(pathSegment.toString(), is(expectedString));
42 + }
43 +
44 + /**
45 + * Tests invalid class constructor for null Segment AS Numbers.
46 + */
47 + @Test(expected = NullPointerException.class)
48 + public void testInvalidConstructorNullSegmentAsNumbers() {
49 + byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
50 + ArrayList<Long> segmentAsNumbers = null;
51 + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
52 + }
53 +
54 + /**
55 + * Tests getting the fields of a Path Segment.
56 + */
57 + @Test
58 + public void testGetFields() {
59 + // Create the fields to compare against
60 + byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
61 + ArrayList<Long> segmentAsNumbers = new ArrayList<>();
62 + segmentAsNumbers.add((long) 1);
63 + segmentAsNumbers.add((long) 2);
64 + segmentAsNumbers.add((long) 3);
65 +
66 + // Generate the entry to test
67 + BgpRouteEntry.PathSegment pathSegment = generatePathSegment();
68 +
69 + assertThat(pathSegment.getType(), is(pathSegmentType));
70 + assertThat(pathSegment.getSegmentAsNumbers(), is(segmentAsNumbers));
71 + }
72 +
73 + /**
74 + * Tests equality of {@link BgpRouteEntry.PathSegment}.
75 + */
76 + @Test
77 + public void testEquality() {
78 + BgpRouteEntry.PathSegment pathSegment1 = generatePathSegment();
79 + BgpRouteEntry.PathSegment pathSegment2 = generatePathSegment();
80 +
81 + assertThat(pathSegment1, is(pathSegment2));
82 + }
83 +
84 + /**
85 + * Tests non-equality of {@link BgpRouteEntry.PathSegment}.
86 + */
87 + @Test
88 + public void testNonEquality() {
89 + BgpRouteEntry.PathSegment pathSegment1 = generatePathSegment();
90 +
91 + // Setup Path Segment 2
92 + byte pathSegmentType = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
93 + ArrayList<Long> segmentAsNumbers = new ArrayList<>();
94 + segmentAsNumbers.add((long) 1);
95 + segmentAsNumbers.add((long) 22); // Different
96 + segmentAsNumbers.add((long) 3);
97 + //
98 + BgpRouteEntry.PathSegment pathSegment2 =
99 + new BgpRouteEntry.PathSegment(pathSegmentType, segmentAsNumbers);
100 +
101 + assertThat(pathSegment1, is(not(pathSegment2)));
102 + }
103 +
104 + /**
105 + * Tests object string representation.
106 + */
107 + @Test
108 + public void testToString() {
109 + BgpRouteEntry.PathSegment pathSegment = generatePathSegment();
110 +
111 + String expectedString =
112 + "PathSegment{type=2, segmentAsNumbers=[1, 2, 3]}";
113 + assertThat(pathSegment.toString(), is(expectedString));
114 + }
115 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import java.util.Collection;
4 +
5 +import org.jboss.netty.buffer.ChannelBuffer;
6 +import org.jboss.netty.buffer.ChannelBuffers;
7 +import org.jboss.netty.channel.ChannelHandlerContext;
8 +import org.jboss.netty.channel.ChannelStateEvent;
9 +import org.jboss.netty.channel.SimpleChannelHandler;
10 +import org.onlab.packet.IpAddress;
11 +import org.onlab.packet.IpPrefix;
12 +
13 +/**
14 + * Class for handling the remote BGP Peer session.
15 + */
16 +class TestBgpPeerChannelHandler extends SimpleChannelHandler {
17 + static final long PEER_AS = 65001;
18 + static final int PEER_HOLDTIME = 120; // 120 seconds
19 + final IpAddress bgpId; // The BGP ID
20 + final long localPref; // Local preference for routes
21 + final long multiExitDisc = 20; // MED value
22 +
23 + ChannelHandlerContext savedCtx;
24 +
25 + /**
26 + * Constructor for given BGP ID.
27 + *
28 + * @param bgpId the BGP ID to use
29 + * @param localPref the local preference for the routes to use
30 + */
31 + TestBgpPeerChannelHandler(IpAddress bgpId,
32 + long localPref) {
33 + this.bgpId = bgpId;
34 + this.localPref = localPref;
35 + }
36 +
37 + /**
38 + * Closes the channel.
39 + */
40 + void closeChannel() {
41 + savedCtx.getChannel().close();
42 + }
43 +
44 + @Override
45 + public void channelConnected(ChannelHandlerContext ctx,
46 + ChannelStateEvent channelEvent) {
47 + this.savedCtx = ctx;
48 + // Prepare and transmit BGP OPEN message
49 + ChannelBuffer message = prepareBgpOpen();
50 + ctx.getChannel().write(message);
51 +
52 + // Prepare and transmit BGP KEEPALIVE message
53 + message = prepareBgpKeepalive();
54 + ctx.getChannel().write(message);
55 + }
56 +
57 + @Override
58 + public void channelDisconnected(ChannelHandlerContext ctx,
59 + ChannelStateEvent channelEvent) {
60 + // Nothing to do
61 + }
62 +
63 + /**
64 + * Prepares BGP OPEN message.
65 + *
66 + * @return the message to transmit (BGP header included)
67 + */
68 + ChannelBuffer prepareBgpOpen() {
69 + ChannelBuffer message =
70 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
71 + message.writeByte(BgpConstants.BGP_VERSION);
72 + message.writeShort((int) PEER_AS);
73 + message.writeShort(PEER_HOLDTIME);
74 + message.writeInt(bgpId.toInt());
75 + message.writeByte(0); // No Optional Parameters
76 + return prepareBgpMessage(BgpConstants.BGP_TYPE_OPEN, message);
77 + }
78 +
79 + /**
80 + * Prepares BGP UPDATE message.
81 + *
82 + * @param nextHopRouter the next-hop router address for the routes to add
83 + * @param addedRoutes the routes to add
84 + * @param withdrawnRoutes the routes to withdraw
85 + * @return the message to transmit (BGP header included)
86 + */
87 + ChannelBuffer prepareBgpUpdate(IpAddress nextHopRouter,
88 + Collection<IpPrefix> addedRoutes,
89 + Collection<IpPrefix> withdrawnRoutes) {
90 + int attrFlags;
91 + ChannelBuffer message =
92 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
93 + ChannelBuffer pathAttributes =
94 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
95 +
96 + // Encode the Withdrawn Routes
97 + ChannelBuffer encodedPrefixes = encodePackedPrefixes(withdrawnRoutes);
98 + message.writeShort(encodedPrefixes.readableBytes());
99 + message.writeBytes(encodedPrefixes);
100 +
101 + // Encode the Path Attributes
102 + // ORIGIN: IGP
103 + attrFlags = 0x40; // Transitive flag
104 + pathAttributes.writeByte(attrFlags);
105 + pathAttributes.writeByte(BgpConstants.Update.Origin.TYPE);
106 + pathAttributes.writeByte(1); // Data length
107 + pathAttributes.writeByte(BgpConstants.Update.Origin.IGP);
108 + // AS_PATH: Two Path Segments of 3 ASes each
109 + attrFlags = 0x40; // Transitive flag
110 + pathAttributes.writeByte(attrFlags);
111 + pathAttributes.writeByte(BgpConstants.Update.AsPath.TYPE);
112 + pathAttributes.writeByte(16); // Data length
113 + byte pathSegmentType1 = (byte) BgpConstants.Update.AsPath.AS_SEQUENCE;
114 + pathAttributes.writeByte(pathSegmentType1);
115 + pathAttributes.writeByte(3); // Three ASes
116 + pathAttributes.writeShort(65010); // AS=65010
117 + pathAttributes.writeShort(65020); // AS=65020
118 + pathAttributes.writeShort(65030); // AS=65030
119 + byte pathSegmentType2 = (byte) BgpConstants.Update.AsPath.AS_SET;
120 + pathAttributes.writeByte(pathSegmentType2);
121 + pathAttributes.writeByte(3); // Three ASes
122 + pathAttributes.writeShort(65041); // AS=65041
123 + pathAttributes.writeShort(65042); // AS=65042
124 + pathAttributes.writeShort(65043); // AS=65043
125 + // NEXT_HOP: nextHopRouter
126 + attrFlags = 0x40; // Transitive flag
127 + pathAttributes.writeByte(attrFlags);
128 + pathAttributes.writeByte(BgpConstants.Update.NextHop.TYPE);
129 + pathAttributes.writeByte(4); // Data length
130 + pathAttributes.writeInt(nextHopRouter.toInt()); // Next-hop router
131 + // LOCAL_PREF: localPref
132 + attrFlags = 0x40; // Transitive flag
133 + pathAttributes.writeByte(attrFlags);
134 + pathAttributes.writeByte(BgpConstants.Update.LocalPref.TYPE);
135 + pathAttributes.writeByte(4); // Data length
136 + pathAttributes.writeInt((int) localPref); // Preference value
137 + // MULTI_EXIT_DISC: multiExitDisc
138 + attrFlags = 0x80; // Optional
139 + // Non-Transitive flag
140 + pathAttributes.writeByte(attrFlags);
141 + pathAttributes.writeByte(BgpConstants.Update.MultiExitDisc.TYPE);
142 + pathAttributes.writeByte(4); // Data length
143 + pathAttributes.writeInt((int) multiExitDisc); // Preference value
144 + // The NLRI prefixes
145 + encodedPrefixes = encodePackedPrefixes(addedRoutes);
146 +
147 + // Write the Path Attributes, beginning with its length
148 + message.writeShort(pathAttributes.readableBytes());
149 + message.writeBytes(pathAttributes);
150 + message.writeBytes(encodedPrefixes);
151 +
152 + return prepareBgpMessage(BgpConstants.BGP_TYPE_UPDATE, message);
153 + }
154 +
155 + /**
156 + * Encodes a collection of IPv4 network prefixes in a packed format.
157 + * <p>
158 + * The IPv4 prefixes are encoded in the form:
159 + * <Length, Prefix> where Length is the length in bits of the IPv4 prefix,
160 + * and Prefix is the IPv4 prefix (padded with trailing bits to the end
161 + * of an octet).
162 + *
163 + * @param prefixes the prefixes to encode
164 + * @return the buffer with the encoded prefixes
165 + */
166 + private ChannelBuffer encodePackedPrefixes(Collection<IpPrefix> prefixes) {
167 + ChannelBuffer message =
168 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
169 +
170 + // Write each of the prefixes
171 + for (IpPrefix prefix : prefixes) {
172 + int prefixBitlen = prefix.prefixLength();
173 + int prefixBytelen = (prefixBitlen + 7) / 8; // Round-up
174 + message.writeByte(prefixBitlen);
175 +
176 + IpAddress address = prefix.toIpAddress();
177 + long value = address.toInt() & 0xffffffffL;
178 + for (int i = 0; i < IpAddress.INET_LEN; i++) {
179 + if (prefixBytelen-- == 0) {
180 + break;
181 + }
182 + long nextByte =
183 + (value >> ((IpAddress.INET_LEN - i - 1) * 8)) & 0xff;
184 + message.writeByte((int) nextByte);
185 + }
186 + }
187 +
188 + return message;
189 + }
190 +
191 + /**
192 + * Prepares BGP KEEPALIVE message.
193 + *
194 + * @return the message to transmit (BGP header included)
195 + */
196 + ChannelBuffer prepareBgpKeepalive() {
197 + ChannelBuffer message =
198 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
199 + return prepareBgpMessage(BgpConstants.BGP_TYPE_KEEPALIVE, message);
200 + }
201 +
202 + /**
203 + * Prepares BGP NOTIFICATION message.
204 + *
205 + * @param errorCode the BGP NOTIFICATION Error Code
206 + * @param errorSubcode the BGP NOTIFICATION Error Subcode if applicable,
207 + * otherwise BgpConstants.Notifications.ERROR_SUBCODE_UNSPECIFIC
208 + * @param payload the BGP NOTIFICATION Data if applicable, otherwise null
209 + * @return the message to transmit (BGP header included)
210 + */
211 + ChannelBuffer prepareBgpNotification(int errorCode, int errorSubcode,
212 + ChannelBuffer data) {
213 + ChannelBuffer message =
214 + ChannelBuffers.buffer(BgpConstants.BGP_MESSAGE_MAX_LENGTH);
215 + // Prepare the NOTIFICATION message payload
216 + message.writeByte(errorCode);
217 + message.writeByte(errorSubcode);
218 + if (data != null) {
219 + message.writeBytes(data);
220 + }
221 + return prepareBgpMessage(BgpConstants.BGP_TYPE_NOTIFICATION, message);
222 + }
223 +
224 + /**
225 + * Prepares BGP message.
226 + *
227 + * @param type the BGP message type
228 + * @param payload the message payload to transmit (BGP header excluded)
229 + * @return the message to transmit (BGP header included)
230 + */
231 + private ChannelBuffer prepareBgpMessage(int type, ChannelBuffer payload) {
232 + ChannelBuffer message =
233 + ChannelBuffers.buffer(BgpConstants.BGP_HEADER_LENGTH +
234 + payload.readableBytes());
235 +
236 + // Write the marker
237 + for (int i = 0; i < BgpConstants.BGP_HEADER_MARKER_LENGTH; i++) {
238 + message.writeByte(0xff);
239 + }
240 +
241 + // Write the rest of the BGP header
242 + message.writeShort(BgpConstants.BGP_HEADER_LENGTH +
243 + payload.readableBytes());
244 + message.writeByte(type);
245 +
246 + // Write the payload
247 + message.writeBytes(payload);
248 + return message;
249 + }
250 +}
1 +package org.onlab.onos.sdnip.bgp;
2 +
3 +import java.util.concurrent.CountDownLatch;
4 +
5 +import org.jboss.netty.buffer.ChannelBuffer;
6 +import org.jboss.netty.channel.Channel;
7 +import org.jboss.netty.channel.ChannelHandlerContext;
8 +import org.jboss.netty.handler.codec.frame.FrameDecoder;
9 +import org.onlab.packet.IpAddress;
10 +
11 +/**
12 + * Class for handling the decoding of the BGP messages at the remote
13 + * BGP peer session.
14 + */
15 +class TestBgpPeerFrameDecoder extends FrameDecoder {
16 + int remoteBgpVersion; // 1 octet
17 + long remoteAs; // 2 octets
18 + long remoteHoldtime; // 2 octets
19 + IpAddress remoteBgpIdentifier; // 4 octets -> IPv4 address
20 +
21 + final CountDownLatch receivedOpenMessageLatch = new CountDownLatch(1);
22 + final CountDownLatch receivedKeepaliveMessageLatch = new CountDownLatch(1);
23 +
24 + @Override
25 + protected Object decode(ChannelHandlerContext ctx,
26 + Channel channel,
27 + ChannelBuffer buf) throws Exception {
28 + // Test for minimum length of the BGP message
29 + if (buf.readableBytes() < BgpConstants.BGP_HEADER_LENGTH) {
30 + // No enough data received
31 + return null;
32 + }
33 +
34 + //
35 + // Mark the current buffer position in case we haven't received
36 + // the whole message.
37 + //
38 + buf.markReaderIndex();
39 +
40 + //
41 + // Read and check the BGP message Marker field: it must be all ones
42 + //
43 + byte[] marker = new byte[BgpConstants.BGP_HEADER_MARKER_LENGTH];
44 + buf.readBytes(marker);
45 + for (int i = 0; i < marker.length; i++) {
46 + if (marker[i] != (byte) 0xff) {
47 + // ERROR: Connection Not Synchronized. Close the channel.
48 + ctx.getChannel().close();
49 + return null;
50 + }
51 + }
52 +
53 + //
54 + // Read and check the BGP message Length field
55 + //
56 + int length = buf.readUnsignedShort();
57 + if ((length < BgpConstants.BGP_HEADER_LENGTH) ||
58 + (length > BgpConstants.BGP_MESSAGE_MAX_LENGTH)) {
59 + // ERROR: Bad Message Length. Close the channel.
60 + ctx.getChannel().close();
61 + return null;
62 + }
63 +
64 + //
65 + // Test whether the rest of the message is received:
66 + // So far we have read the Marker (16 octets) and the
67 + // Length (2 octets) fields.
68 + //
69 + int remainingMessageLen =
70 + length - BgpConstants.BGP_HEADER_MARKER_LENGTH - 2;
71 + if (buf.readableBytes() < remainingMessageLen) {
72 + // No enough data received
73 + buf.resetReaderIndex();
74 + return null;
75 + }
76 +
77 + //
78 + // Read the BGP message Type field, and process based on that type
79 + //
80 + int type = buf.readUnsignedByte();
81 + remainingMessageLen--; // Adjust after reading the type
82 + ChannelBuffer message = buf.readBytes(remainingMessageLen);
83 +
84 + //
85 + // Process the remaining of the message based on the message type
86 + //
87 + switch (type) {
88 + case BgpConstants.BGP_TYPE_OPEN:
89 + processBgpOpen(ctx, message);
90 + break;
91 + case BgpConstants.BGP_TYPE_UPDATE:
92 + // NOTE: Not used as part of the test, because ONOS does not
93 + // originate UPDATE messages.
94 + break;
95 + case BgpConstants.BGP_TYPE_NOTIFICATION:
96 + // NOTE: Not used as part of the testing (yet)
97 + break;
98 + case BgpConstants.BGP_TYPE_KEEPALIVE:
99 + processBgpKeepalive(ctx, message);
100 + break;
101 + default:
102 + // ERROR: Bad Message Type. Close the channel.
103 + ctx.getChannel().close();
104 + return null;
105 + }
106 +
107 + return null;
108 + }
109 +
110 + /**
111 + * Processes BGP OPEN message.
112 + *
113 + * @param ctx the Channel Handler Context.
114 + * @param message the message to process.
115 + */
116 + private void processBgpOpen(ChannelHandlerContext ctx,
117 + ChannelBuffer message) {
118 + int minLength =
119 + BgpConstants.BGP_OPEN_MIN_LENGTH - BgpConstants.BGP_HEADER_LENGTH;
120 + if (message.readableBytes() < minLength) {
121 + // ERROR: Bad Message Length. Close the channel.
122 + ctx.getChannel().close();
123 + return;
124 + }
125 +
126 + //
127 + // Parse the OPEN message
128 + //
129 + remoteBgpVersion = message.readUnsignedByte();
130 + remoteAs = message.readUnsignedShort();
131 + remoteHoldtime = message.readUnsignedShort();
132 + remoteBgpIdentifier = IpAddress.valueOf((int) message.readUnsignedInt());
133 + // Optional Parameters
134 + int optParamLen = message.readUnsignedByte();
135 + if (message.readableBytes() < optParamLen) {
136 + // ERROR: Bad Message Length. Close the channel.
137 + ctx.getChannel().close();
138 + return;
139 + }
140 + message.readBytes(optParamLen); // NOTE: data ignored
141 +
142 + // BGP OPEN message successfully received
143 + receivedOpenMessageLatch.countDown();
144 + }
145 +
146 + /**
147 + * Processes BGP KEEPALIVE message.
148 + *
149 + * @param ctx the Channel Handler Context.
150 + * @param message the message to process.
151 + */
152 + private void processBgpKeepalive(ChannelHandlerContext ctx,
153 + ChannelBuffer message) {
154 + if (message.readableBytes() + BgpConstants.BGP_HEADER_LENGTH !=
155 + BgpConstants.BGP_KEEPALIVE_EXPECTED_LENGTH) {
156 + // ERROR: Bad Message Length. Close the channel.
157 + ctx.getChannel().close();
158 + return;
159 + }
160 + // BGP KEEPALIVE message successfully received
161 + receivedKeepaliveMessageLatch.countDown();
162 + }
163 +}
...@@ -26,6 +26,16 @@ ...@@ -26,6 +26,16 @@
26 <groupId>org.onlab.onos</groupId> 26 <groupId>org.onlab.onos</groupId>
27 <artifactId>onlab-osgi</artifactId> 27 <artifactId>onlab-osgi</artifactId>
28 </dependency> 28 </dependency>
29 +
30 + <dependency>
31 + <groupId>com.fasterxml.jackson.core</groupId>
32 + <artifactId>jackson-databind</artifactId>
33 + </dependency>
34 + <dependency>
35 + <groupId>com.fasterxml.jackson.core</groupId>
36 + <artifactId>jackson-annotations</artifactId>
37 + </dependency>
38 +
29 <dependency> 39 <dependency>
30 <groupId>org.osgi</groupId> 40 <groupId>org.osgi</groupId>
31 <artifactId>org.osgi.core</artifactId> 41 <artifactId>org.osgi.core</artifactId>
......
1 package org.onlab.onos.cli; 1 package org.onlab.onos.cli;
2 2
3 +import org.apache.karaf.shell.commands.Option;
3 import org.apache.karaf.shell.console.OsgiCommandSupport; 4 import org.apache.karaf.shell.console.OsgiCommandSupport;
4 import org.onlab.osgi.DefaultServiceDirectory; 5 import org.onlab.osgi.DefaultServiceDirectory;
5 import org.onlab.osgi.ServiceNotFoundException; 6 import org.onlab.osgi.ServiceNotFoundException;
...@@ -9,6 +10,10 @@ import org.onlab.osgi.ServiceNotFoundException; ...@@ -9,6 +10,10 @@ import org.onlab.osgi.ServiceNotFoundException;
9 */ 10 */
10 public abstract class AbstractShellCommand extends OsgiCommandSupport { 11 public abstract class AbstractShellCommand extends OsgiCommandSupport {
11 12
13 + @Option(name = "-j", aliases = "--json", description = "Output JSON",
14 + required = false, multiValued = false)
15 + private boolean json = false;
16 +
12 /** 17 /**
13 * Returns the reference to the implementation of the specified service. 18 * Returns the reference to the implementation of the specified service.
14 * 19 *
...@@ -46,6 +51,15 @@ public abstract class AbstractShellCommand extends OsgiCommandSupport { ...@@ -46,6 +51,15 @@ public abstract class AbstractShellCommand extends OsgiCommandSupport {
46 */ 51 */
47 protected abstract void execute(); 52 protected abstract void execute();
48 53
54 + /**
55 + * Indicates whether JSON format should be output.
56 + *
57 + * @return true if JSON is requested
58 + */
59 + protected boolean outputJson() {
60 + return json;
61 + }
62 +
49 @Override 63 @Override
50 protected Object doExecute() throws Exception { 64 protected Object doExecute() throws Exception {
51 try { 65 try {
......
1 package org.onlab.onos.cli; 1 package org.onlab.onos.cli;
2 2
3 +import com.fasterxml.jackson.databind.JsonNode;
4 +import com.fasterxml.jackson.databind.ObjectMapper;
5 +import com.fasterxml.jackson.databind.node.ArrayNode;
3 import com.google.common.collect.Lists; 6 import com.google.common.collect.Lists;
4 -
5 import org.apache.karaf.shell.commands.Command; 7 import org.apache.karaf.shell.commands.Command;
6 import org.onlab.onos.cluster.ClusterService; 8 import org.onlab.onos.cluster.ClusterService;
7 import org.onlab.onos.cluster.ControllerNode; 9 import org.onlab.onos.cluster.ControllerNode;
...@@ -26,15 +28,50 @@ public class MastersListCommand extends AbstractShellCommand { ...@@ -26,15 +28,50 @@ public class MastersListCommand extends AbstractShellCommand {
26 MastershipService mastershipService = get(MastershipService.class); 28 MastershipService mastershipService = get(MastershipService.class);
27 List<ControllerNode> nodes = newArrayList(service.getNodes()); 29 List<ControllerNode> nodes = newArrayList(service.getNodes());
28 Collections.sort(nodes, Comparators.NODE_COMPARATOR); 30 Collections.sort(nodes, Comparators.NODE_COMPARATOR);
31 +
32 + if (outputJson()) {
33 + print("%s", json(service, mastershipService, nodes));
34 + } else {
35 + for (ControllerNode node : nodes) {
36 + List<DeviceId> ids = Lists.newArrayList(mastershipService.getDevicesOf(node.id()));
37 + Collections.sort(ids, Comparators.ELEMENT_ID_COMPARATOR);
38 + print("%s: %d devices", node.id(), ids.size());
39 + for (DeviceId deviceId : ids) {
40 + print(" %s", deviceId);
41 + }
42 + }
43 + }
44 + }
45 +
46 + // Produces JSON structure.
47 + private JsonNode json(ClusterService service, MastershipService mastershipService,
48 + List<ControllerNode> nodes) {
49 + ObjectMapper mapper = new ObjectMapper();
50 + ArrayNode result = mapper.createArrayNode();
29 ControllerNode self = service.getLocalNode(); 51 ControllerNode self = service.getLocalNode();
30 for (ControllerNode node : nodes) { 52 for (ControllerNode node : nodes) {
31 List<DeviceId> ids = Lists.newArrayList(mastershipService.getDevicesOf(node.id())); 53 List<DeviceId> ids = Lists.newArrayList(mastershipService.getDevicesOf(node.id()));
32 - Collections.sort(ids, Comparators.ELEMENT_ID_COMPARATOR); 54 + result.add(mapper.createObjectNode()
33 - print("%s: %d devices", node.id(), ids.size()); 55 + .put("id", node.id().toString())
34 - for (DeviceId deviceId : ids) { 56 + .put("size", ids.size())
35 - print(" %s", deviceId); 57 + .set("devices", json(mapper, ids)));
36 - } 58 + }
59 + return result;
60 + }
61 +
62 + /**
63 + * Produces a JSON array containing the specified device identifiers.
64 + *
65 + * @param mapper object mapper
66 + * @param ids collection of device identifiers
67 + * @return JSON array
68 + */
69 + public static JsonNode json(ObjectMapper mapper, Iterable<DeviceId> ids) {
70 + ArrayNode result = mapper.createArrayNode();
71 + for (DeviceId deviceId : ids) {
72 + result.add(deviceId.toString());
37 } 73 }
74 + return result;
38 } 75 }
39 76
40 } 77 }
......
1 package org.onlab.onos.cli; 1 package org.onlab.onos.cli;
2 2
3 +import com.fasterxml.jackson.databind.JsonNode;
4 +import com.fasterxml.jackson.databind.ObjectMapper;
5 +import com.fasterxml.jackson.databind.node.ArrayNode;
3 import org.apache.karaf.shell.commands.Command; 6 import org.apache.karaf.shell.commands.Command;
4 import org.onlab.onos.cluster.ClusterService; 7 import org.onlab.onos.cluster.ClusterService;
5 import org.onlab.onos.cluster.ControllerNode; 8 import org.onlab.onos.cluster.ControllerNode;
...@@ -24,12 +27,32 @@ public class NodesListCommand extends AbstractShellCommand { ...@@ -24,12 +27,32 @@ public class NodesListCommand extends AbstractShellCommand {
24 ClusterService service = get(ClusterService.class); 27 ClusterService service = get(ClusterService.class);
25 List<ControllerNode> nodes = newArrayList(service.getNodes()); 28 List<ControllerNode> nodes = newArrayList(service.getNodes());
26 Collections.sort(nodes, Comparators.NODE_COMPARATOR); 29 Collections.sort(nodes, Comparators.NODE_COMPARATOR);
30 + if (outputJson()) {
31 + print("%s", json(service, nodes));
32 + } else {
33 + ControllerNode self = service.getLocalNode();
34 + for (ControllerNode node : nodes) {
35 + print(FMT, node.id(), node.ip(), node.tcpPort(),
36 + service.getState(node.id()),
37 + node.equals(self) ? "*" : "");
38 + }
39 + }
40 + }
41 +
42 + // Produces JSON structure.
43 + private JsonNode json(ClusterService service, List<ControllerNode> nodes) {
44 + ObjectMapper mapper = new ObjectMapper();
45 + ArrayNode result = mapper.createArrayNode();
27 ControllerNode self = service.getLocalNode(); 46 ControllerNode self = service.getLocalNode();
28 for (ControllerNode node : nodes) { 47 for (ControllerNode node : nodes) {
29 - print(FMT, node.id(), node.ip(), node.tcpPort(), 48 + result.add(mapper.createObjectNode()
30 - service.getState(node.id()), 49 + .put("id", node.id().toString())
31 - node.equals(self) ? "*" : ""); 50 + .put("ip", node.ip().toString())
51 + .put("tcpPort", node.tcpPort())
52 + .put("state", service.getState(node.id()).toString())
53 + .put("self", node.equals(self)));
32 } 54 }
55 + return result;
33 } 56 }
34 57
35 } 58 }
......
1 package org.onlab.onos.cli; 1 package org.onlab.onos.cli;
2 2
3 +import com.fasterxml.jackson.databind.ObjectMapper;
3 import org.apache.karaf.shell.commands.Command; 4 import org.apache.karaf.shell.commands.Command;
4 import org.onlab.onos.CoreService; 5 import org.onlab.onos.CoreService;
5 import org.onlab.onos.cluster.ClusterService; 6 import org.onlab.onos.cluster.ClusterService;
...@@ -22,18 +23,32 @@ public class SummaryCommand extends AbstractShellCommand { ...@@ -22,18 +23,32 @@ public class SummaryCommand extends AbstractShellCommand {
22 protected void execute() { 23 protected void execute() {
23 TopologyService topologyService = get(TopologyService.class); 24 TopologyService topologyService = get(TopologyService.class);
24 Topology topology = topologyService.currentTopology(); 25 Topology topology = topologyService.currentTopology();
25 - print("node=%s, version=%s", 26 + if (outputJson()) {
26 - get(ClusterService.class).getLocalNode().ip(), 27 + print("%s", new ObjectMapper().createObjectNode()
27 - get(CoreService.class).version().toString()); 28 + .put("node", get(ClusterService.class).getLocalNode().ip().toString())
28 - print("nodes=%d, devices=%d, links=%d, hosts=%d, clusters=%s, paths=%d, flows=%d, intents=%d", 29 + .put("version", get(CoreService.class).version().toString())
29 - get(ClusterService.class).getNodes().size(), 30 + .put("nodes", get(ClusterService.class).getNodes().size())
30 - get(DeviceService.class).getDeviceCount(), 31 + .put("devices", get(DeviceService.class).getDeviceCount())
31 - get(LinkService.class).getLinkCount(), 32 + .put("links", get(LinkService.class).getLinkCount())
32 - get(HostService.class).getHostCount(), 33 + .put("hosts", get(HostService.class).getHostCount())
33 - topologyService.getClusters(topology).size(), 34 + .put("clusters", topologyService.getClusters(topology).size())
34 - topology.pathCount(), 35 + .put("paths", topology.pathCount())
35 - get(FlowRuleService.class).getFlowRuleCount(), 36 + .put("flows", get(FlowRuleService.class).getFlowRuleCount())
36 - get(IntentService.class).getIntentCount()); 37 + .put("intents", get(IntentService.class).getIntentCount()));
38 + } else {
39 + print("node=%s, version=%s",
40 + get(ClusterService.class).getLocalNode().ip(),
41 + get(CoreService.class).version().toString());
42 + print("nodes=%d, devices=%d, links=%d, hosts=%d, clusters=%s, paths=%d, flows=%d, intents=%d",
43 + get(ClusterService.class).getNodes().size(),
44 + get(DeviceService.class).getDeviceCount(),
45 + get(LinkService.class).getLinkCount(),
46 + get(HostService.class).getHostCount(),
47 + topologyService.getClusters(topology).size(),
48 + topology.pathCount(),
49 + get(FlowRuleService.class).getFlowRuleCount(),
50 + get(IntentService.class).getIntentCount());
51 + }
37 } 52 }
38 53
39 } 54 }
......
1 package org.onlab.onos.cli.net; 1 package org.onlab.onos.cli.net;
2 2
3 +import com.fasterxml.jackson.databind.ObjectMapper;
3 import com.google.common.collect.Lists; 4 import com.google.common.collect.Lists;
4 import org.apache.karaf.shell.commands.Argument; 5 import org.apache.karaf.shell.commands.Argument;
5 import org.apache.karaf.shell.commands.Command; 6 import org.apache.karaf.shell.commands.Command;
...@@ -10,6 +11,7 @@ import org.onlab.onos.net.topology.TopologyCluster; ...@@ -10,6 +11,7 @@ import org.onlab.onos.net.topology.TopologyCluster;
10 import java.util.Collections; 11 import java.util.Collections;
11 import java.util.List; 12 import java.util.List;
12 13
14 +import static org.onlab.onos.cli.MastersListCommand.json;
13 import static org.onlab.onos.net.topology.ClusterId.clusterId; 15 import static org.onlab.onos.net.topology.ClusterId.clusterId;
14 16
15 /** 17 /**
...@@ -33,11 +35,14 @@ public class ClusterDevicesCommand extends ClustersListCommand { ...@@ -33,11 +35,14 @@ public class ClusterDevicesCommand extends ClustersListCommand {
33 } else { 35 } else {
34 List<DeviceId> ids = Lists.newArrayList(service.getClusterDevices(topology, cluster)); 36 List<DeviceId> ids = Lists.newArrayList(service.getClusterDevices(topology, cluster));
35 Collections.sort(ids, Comparators.ELEMENT_ID_COMPARATOR); 37 Collections.sort(ids, Comparators.ELEMENT_ID_COMPARATOR);
36 - for (DeviceId deviceId : ids) { 38 + if (outputJson()) {
37 - print("%s", deviceId); 39 + print("%s", json(new ObjectMapper(), ids));
40 + } else {
41 + for (DeviceId deviceId : ids) {
42 + print("%s", deviceId);
43 + }
38 } 44 }
39 } 45 }
40 } 46 }
41 47
42 -
43 } 48 }
......
...@@ -5,6 +5,7 @@ import org.apache.karaf.shell.commands.Command; ...@@ -5,6 +5,7 @@ import org.apache.karaf.shell.commands.Command;
5 import org.onlab.onos.net.Link; 5 import org.onlab.onos.net.Link;
6 import org.onlab.onos.net.topology.TopologyCluster; 6 import org.onlab.onos.net.topology.TopologyCluster;
7 7
8 +import static org.onlab.onos.cli.net.LinksListCommand.json;
8 import static org.onlab.onos.cli.net.LinksListCommand.linkString; 9 import static org.onlab.onos.cli.net.LinksListCommand.linkString;
9 import static org.onlab.onos.net.topology.ClusterId.clusterId; 10 import static org.onlab.onos.net.topology.ClusterId.clusterId;
10 11
...@@ -26,6 +27,8 @@ public class ClusterLinksCommand extends ClustersListCommand { ...@@ -26,6 +27,8 @@ public class ClusterLinksCommand extends ClustersListCommand {
26 TopologyCluster cluster = service.getCluster(topology, clusterId(cid)); 27 TopologyCluster cluster = service.getCluster(topology, clusterId(cid));
27 if (cluster == null) { 28 if (cluster == null) {
28 error("No such cluster %s", cid); 29 error("No such cluster %s", cid);
30 + } else if (outputJson()) {
31 + print("%s", json(service.getClusterLinks(topology, cluster)));
29 } else { 32 } else {
30 for (Link link : service.getClusterLinks(topology, cluster)) { 33 for (Link link : service.getClusterLinks(topology, cluster)) {
31 print(linkString(link)); 34 print(linkString(link));
......
1 package org.onlab.onos.cli.net; 1 package org.onlab.onos.cli.net;
2 2
3 +import com.fasterxml.jackson.databind.JsonNode;
4 +import com.fasterxml.jackson.databind.ObjectMapper;
5 +import com.fasterxml.jackson.databind.node.ArrayNode;
3 import com.google.common.collect.Lists; 6 import com.google.common.collect.Lists;
4 import org.apache.karaf.shell.commands.Command; 7 import org.apache.karaf.shell.commands.Command;
5 import org.onlab.onos.cli.Comparators; 8 import org.onlab.onos.cli.Comparators;
...@@ -24,9 +27,26 @@ public class ClustersListCommand extends TopologyCommand { ...@@ -24,9 +27,26 @@ public class ClustersListCommand extends TopologyCommand {
24 List<TopologyCluster> clusters = Lists.newArrayList(service.getClusters(topology)); 27 List<TopologyCluster> clusters = Lists.newArrayList(service.getClusters(topology));
25 Collections.sort(clusters, Comparators.CLUSTER_COMPARATOR); 28 Collections.sort(clusters, Comparators.CLUSTER_COMPARATOR);
26 29
30 + if (outputJson()) {
31 + print("%s", json(clusters));
32 + } else {
33 + for (TopologyCluster cluster : clusters) {
34 + print(FMT, cluster.id().index(), cluster.deviceCount(), cluster.linkCount());
35 + }
36 + }
37 + }
38 +
39 + // Produces a JSON result.
40 + private JsonNode json(Iterable<TopologyCluster> clusters) {
41 + ObjectMapper mapper = new ObjectMapper();
42 + ArrayNode result = mapper.createArrayNode();
27 for (TopologyCluster cluster : clusters) { 43 for (TopologyCluster cluster : clusters) {
28 - print(FMT, cluster.id().index(), cluster.deviceCount(), cluster.linkCount()); 44 + result.add(mapper.createObjectNode()
45 + .put("id", cluster.id().index())
46 + .put("deviceCount", cluster.deviceCount())
47 + .put("linkCount", cluster.linkCount()));
29 } 48 }
49 + return result;
30 } 50 }
31 51
32 } 52 }
......
1 package org.onlab.onos.cli.net; 1 package org.onlab.onos.cli.net;
2 2
3 +import com.fasterxml.jackson.databind.JsonNode;
4 +import com.fasterxml.jackson.databind.ObjectMapper;
5 +import com.fasterxml.jackson.databind.node.ArrayNode;
6 +import com.fasterxml.jackson.databind.node.ObjectNode;
3 import org.apache.karaf.shell.commands.Argument; 7 import org.apache.karaf.shell.commands.Argument;
4 import org.apache.karaf.shell.commands.Command; 8 import org.apache.karaf.shell.commands.Command;
9 +import org.apache.karaf.shell.commands.Option;
5 import org.onlab.onos.cli.Comparators; 10 import org.onlab.onos.cli.Comparators;
6 import org.onlab.onos.net.Device; 11 import org.onlab.onos.net.Device;
7 import org.onlab.onos.net.Port; 12 import org.onlab.onos.net.Port;
...@@ -22,6 +27,14 @@ public class DevicePortsListCommand extends DevicesListCommand { ...@@ -22,6 +27,14 @@ public class DevicePortsListCommand extends DevicesListCommand {
22 27
23 private static final String FMT = " port=%s, state=%s"; 28 private static final String FMT = " port=%s, state=%s";
24 29
30 + @Option(name = "-e", aliases = "--enabled", description = "Show only enabled ports",
31 + required = false, multiValued = false)
32 + private boolean enabled = false;
33 +
34 + @Option(name = "-d", aliases = "--disabled", description = "Show only disabled ports",
35 + required = false, multiValued = false)
36 + private boolean disabled = false;
37 +
25 @Argument(index = 0, name = "uri", description = "Device ID", 38 @Argument(index = 0, name = "uri", description = "Device ID",
26 required = false, multiValued = false) 39 required = false, multiValued = false)
27 String uri = null; 40 String uri = null;
...@@ -30,26 +43,78 @@ public class DevicePortsListCommand extends DevicesListCommand { ...@@ -30,26 +43,78 @@ public class DevicePortsListCommand extends DevicesListCommand {
30 protected void execute() { 43 protected void execute() {
31 DeviceService service = get(DeviceService.class); 44 DeviceService service = get(DeviceService.class);
32 if (uri == null) { 45 if (uri == null) {
33 - for (Device device : getSortedDevices(service)) { 46 + if (outputJson()) {
34 - printDevice(service, device); 47 + print("%s", jsonPorts(service, getSortedDevices(service)));
48 + } else {
49 + for (Device device : getSortedDevices(service)) {
50 + printDevice(service, device);
51 + }
35 } 52 }
53 +
36 } else { 54 } else {
37 Device device = service.getDevice(deviceId(uri)); 55 Device device = service.getDevice(deviceId(uri));
38 if (device == null) { 56 if (device == null) {
39 error("No such device %s", uri); 57 error("No such device %s", uri);
58 + } else if (outputJson()) {
59 + print("%s", jsonPorts(service, new ObjectMapper(), device));
40 } else { 60 } else {
41 printDevice(service, device); 61 printDevice(service, device);
42 } 62 }
43 } 63 }
44 } 64 }
45 65
66 + /**
67 + * Produces JSON array containing ports of the specified devices.
68 + *
69 + * @param service device service
70 + * @param devices collection of devices
71 + * @return JSON array
72 + */
73 + public JsonNode jsonPorts(DeviceService service, Iterable<Device> devices) {
74 + ObjectMapper mapper = new ObjectMapper();
75 + ArrayNode result = mapper.createArrayNode();
76 + for (Device device : devices) {
77 + result.add(jsonPorts(service, mapper, device));
78 + }
79 + return result;
80 + }
81 +
82 + /**
83 + * Produces JSON array containing ports of the specified device.
84 + *
85 + * @param service device service
86 + * @param mapper object mapper
87 + * @param device infrastructure devices
88 + * @return JSON array
89 + */
90 + public JsonNode jsonPorts(DeviceService service, ObjectMapper mapper, Device device) {
91 + ObjectNode result = mapper.createObjectNode();
92 + ArrayNode ports = mapper.createArrayNode();
93 + for (Port port : service.getPorts(device.id())) {
94 + if (isIncluded(port)) {
95 + ports.add(mapper.createObjectNode()
96 + .put("port", port.number().toString())
97 + .put("isEnabled", port.isEnabled()));
98 + }
99 + }
100 + return result.put("device", device.id().toString()).set("ports", ports);
101 + }
102 +
103 + // Determines if a port should be included in output.
104 + private boolean isIncluded(Port port) {
105 + return enabled && port.isEnabled() || disabled && !port.isEnabled() ||
106 + !enabled && !disabled;
107 + }
108 +
46 @Override 109 @Override
47 protected void printDevice(DeviceService service, Device device) { 110 protected void printDevice(DeviceService service, Device device) {
48 super.printDevice(service, device); 111 super.printDevice(service, device);
49 List<Port> ports = new ArrayList<>(service.getPorts(device.id())); 112 List<Port> ports = new ArrayList<>(service.getPorts(device.id()));
50 Collections.sort(ports, Comparators.PORT_COMPARATOR); 113 Collections.sort(ports, Comparators.PORT_COMPARATOR);
51 for (Port port : ports) { 114 for (Port port : ports) {
52 - print(FMT, port.number(), port.isEnabled() ? "enabled" : "disabled"); 115 + if (isIncluded(port)) {
116 + print(FMT, port.number(), port.isEnabled() ? "enabled" : "disabled");
117 + }
53 } 118 }
54 } 119 }
55 120
......
1 package org.onlab.onos.cli.net; 1 package org.onlab.onos.cli.net;
2 2
3 +import com.fasterxml.jackson.databind.JsonNode;
4 +import com.fasterxml.jackson.databind.ObjectMapper;
5 +import com.fasterxml.jackson.databind.node.ArrayNode;
6 +import com.fasterxml.jackson.databind.node.ObjectNode;
3 import org.apache.karaf.shell.commands.Command; 7 import org.apache.karaf.shell.commands.Command;
4 import org.onlab.onos.cli.AbstractShellCommand; 8 import org.onlab.onos.cli.AbstractShellCommand;
5 import org.onlab.onos.cli.Comparators; 9 import org.onlab.onos.cli.Comparators;
...@@ -24,12 +28,55 @@ public class DevicesListCommand extends AbstractShellCommand { ...@@ -24,12 +28,55 @@ public class DevicesListCommand extends AbstractShellCommand {
24 @Override 28 @Override
25 protected void execute() { 29 protected void execute() {
26 DeviceService service = get(DeviceService.class); 30 DeviceService service = get(DeviceService.class);
27 - for (Device device : getSortedDevices(service)) { 31 + if (outputJson()) {
28 - printDevice(service, device); 32 + print("%s", json(service, getSortedDevices(service)));
33 + } else {
34 + for (Device device : getSortedDevices(service)) {
35 + printDevice(service, device);
36 + }
29 } 37 }
30 } 38 }
31 39
32 /** 40 /**
41 + * Returns JSON node representing the specified devices.
42 + *
43 + * @param service device service
44 + * @param devices collection of devices
45 + * @return JSON node
46 + */
47 + public static JsonNode json(DeviceService service, Iterable<Device> devices) {
48 + ObjectMapper mapper = new ObjectMapper();
49 + ArrayNode result = mapper.createArrayNode();
50 + for (Device device : devices) {
51 + result.add(json(service, mapper, device));
52 + }
53 + return result;
54 + }
55 +
56 + /**
57 + * Returns JSON node representing the specified device.
58 + *
59 + * @param service device service
60 + * @param mapper object mapper
61 + * @param device infrastructure device
62 + * @return JSON node
63 + */
64 + public static ObjectNode json(DeviceService service, ObjectMapper mapper,
65 + Device device) {
66 + ObjectNode result = mapper.createObjectNode();
67 + if (device != null) {
68 + result.put("id", device.id().toString())
69 + .put("available", service.isAvailable(device.id()))
70 + .put("role", service.getRole(device.id()).toString())
71 + .put("mfr", device.manufacturer())
72 + .put("hw", device.hwVersion())
73 + .put("sw", device.swVersion())
74 + .put("serial", device.serialNumber());
75 + }
76 + return result;
77 + }
78 +
79 + /**
33 * Returns the list of devices sorted using the device ID URIs. 80 * Returns the list of devices sorted using the device ID URIs.
34 * 81 *
35 * @param service device service 82 * @param service device service
......
1 package org.onlab.onos.cli.net; 1 package org.onlab.onos.cli.net;
2 2
3 +import com.fasterxml.jackson.databind.JsonNode;
4 +import com.fasterxml.jackson.databind.ObjectMapper;
5 +import com.fasterxml.jackson.databind.node.ArrayNode;
6 +import com.fasterxml.jackson.databind.node.ObjectNode;
3 import com.google.common.collect.Maps; 7 import com.google.common.collect.Maps;
4 import org.apache.karaf.shell.commands.Argument; 8 import org.apache.karaf.shell.commands.Argument;
5 import org.apache.karaf.shell.commands.Command; 9 import org.apache.karaf.shell.commands.Command;
...@@ -12,6 +16,8 @@ import org.onlab.onos.net.device.DeviceService; ...@@ -12,6 +16,8 @@ import org.onlab.onos.net.device.DeviceService;
12 import org.onlab.onos.net.flow.FlowEntry; 16 import org.onlab.onos.net.flow.FlowEntry;
13 import org.onlab.onos.net.flow.FlowEntry.FlowEntryState; 17 import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
14 import org.onlab.onos.net.flow.FlowRuleService; 18 import org.onlab.onos.net.flow.FlowRuleService;
19 +import org.onlab.onos.net.flow.criteria.Criterion;
20 +import org.onlab.onos.net.flow.instructions.Instruction;
15 21
16 import java.util.Collections; 22 import java.util.Collections;
17 import java.util.List; 23 import java.util.List;
...@@ -48,9 +54,73 @@ public class FlowsListCommand extends AbstractShellCommand { ...@@ -48,9 +54,73 @@ public class FlowsListCommand extends AbstractShellCommand {
48 DeviceService deviceService = get(DeviceService.class); 54 DeviceService deviceService = get(DeviceService.class);
49 FlowRuleService service = get(FlowRuleService.class); 55 FlowRuleService service = get(FlowRuleService.class);
50 Map<Device, List<FlowEntry>> flows = getSortedFlows(deviceService, service); 56 Map<Device, List<FlowEntry>> flows = getSortedFlows(deviceService, service);
51 - for (Device d : getSortedDevices(deviceService)) { 57 +
52 - printFlows(d, flows.get(d), coreService); 58 + if (outputJson()) {
59 + print("%s", json(coreService, getSortedDevices(deviceService), flows));
60 + } else {
61 + for (Device d : getSortedDevices(deviceService)) {
62 + printFlows(d, flows.get(d), coreService);
63 + }
64 + }
65 + }
66 +
67 + /**
68 + * Produces a JSON array of flows grouped by the each device.
69 + *
70 + * @param coreService core service
71 + * @param devices collection of devices to group flow by
72 + * @param flows collection of flows per each device
73 + * @return JSON array
74 + */
75 + private JsonNode json(CoreService coreService, Iterable<Device> devices,
76 + Map<Device, List<FlowEntry>> flows) {
77 + ObjectMapper mapper = new ObjectMapper();
78 + ArrayNode result = mapper.createArrayNode();
79 + for (Device device : devices) {
80 + result.add(json(coreService, mapper, device, flows.get(device)));
53 } 81 }
82 + return result;
83 + }
84 +
85 + // Produces JSON object with the flows of the given device.
86 + private ObjectNode json(CoreService coreService, ObjectMapper mapper,
87 + Device device, List<FlowEntry> flows) {
88 + ObjectNode result = mapper.createObjectNode();
89 + ArrayNode array = mapper.createArrayNode();
90 +
91 + for (FlowEntry flow : flows) {
92 + array.add(json(coreService, mapper, flow));
93 + }
94 +
95 + result.put("device", device.id().toString())
96 + .put("flowCount", flows.size())
97 + .set("flows", array);
98 + return result;
99 + }
100 +
101 + // Produces JSON structure with the specified flow data.
102 + private ObjectNode json(CoreService coreService, ObjectMapper mapper,
103 + FlowEntry flow) {
104 + ObjectNode result = mapper.createObjectNode();
105 + ArrayNode crit = mapper.createArrayNode();
106 + for (Criterion c : flow.selector().criteria()) {
107 + crit.add(c.toString());
108 + }
109 +
110 + ArrayNode instr = mapper.createArrayNode();
111 + for (Instruction i : flow.treatment().instructions()) {
112 + instr.add(i.toString());
113 + }
114 +
115 + result.put("flowId", Long.toHexString(flow.id().value()))
116 + .put("state", flow.state().toString())
117 + .put("bytes", flow.bytes())
118 + .put("packets", flow.packets())
119 + .put("life", flow.life())
120 + .put("appId", coreService.getAppId(flow.appId()).name());
121 + result.set("selector", crit);
122 + result.set("treatment", instr);
123 + return result;
54 } 124 }
55 125
56 /** 126 /**
......
1 package org.onlab.onos.cli.net; 1 package org.onlab.onos.cli.net;
2 2
3 +import com.fasterxml.jackson.databind.JsonNode;
4 +import com.fasterxml.jackson.databind.ObjectMapper;
5 +import com.fasterxml.jackson.databind.node.ArrayNode;
6 +import com.fasterxml.jackson.databind.node.ObjectNode;
3 import org.apache.karaf.shell.commands.Command; 7 import org.apache.karaf.shell.commands.Command;
4 import org.onlab.onos.cli.AbstractShellCommand; 8 import org.onlab.onos.cli.AbstractShellCommand;
5 import org.onlab.onos.cli.Comparators; 9 import org.onlab.onos.cli.Comparators;
6 import org.onlab.onos.net.Host; 10 import org.onlab.onos.net.Host;
7 import org.onlab.onos.net.host.HostService; 11 import org.onlab.onos.net.host.HostService;
12 +import org.onlab.packet.IpPrefix;
8 13
9 import java.util.Collections; 14 import java.util.Collections;
10 import java.util.List; 15 import java.util.List;
...@@ -15,7 +20,7 @@ import static com.google.common.collect.Lists.newArrayList; ...@@ -15,7 +20,7 @@ import static com.google.common.collect.Lists.newArrayList;
15 * Lists all currently-known hosts. 20 * Lists all currently-known hosts.
16 */ 21 */
17 @Command(scope = "onos", name = "hosts", 22 @Command(scope = "onos", name = "hosts",
18 - description = "Lists all currently-known hosts.") 23 + description = "Lists all currently-known hosts.")
19 public class HostsListCommand extends AbstractShellCommand { 24 public class HostsListCommand extends AbstractShellCommand {
20 25
21 private static final String FMT = 26 private static final String FMT =
...@@ -24,11 +29,42 @@ public class HostsListCommand extends AbstractShellCommand { ...@@ -24,11 +29,42 @@ public class HostsListCommand extends AbstractShellCommand {
24 @Override 29 @Override
25 protected void execute() { 30 protected void execute() {
26 HostService service = get(HostService.class); 31 HostService service = get(HostService.class);
27 - for (Host host : getSortedHosts(service)) { 32 + if (outputJson()) {
28 - printHost(host); 33 + print("%s", json(getSortedHosts(service)));
34 + } else {
35 + for (Host host : getSortedHosts(service)) {
36 + printHost(host);
37 + }
29 } 38 }
30 } 39 }
31 40
41 + // Produces JSON structure.
42 + private static JsonNode json(Iterable<Host> hosts) {
43 + ObjectMapper mapper = new ObjectMapper();
44 + ArrayNode result = mapper.createArrayNode();
45 + for (Host host : hosts) {
46 + result.add(json(mapper, host));
47 + }
48 + return result;
49 + }
50 +
51 + // Produces JSON structure.
52 + private static JsonNode json(ObjectMapper mapper, Host host) {
53 + ObjectNode loc = LinksListCommand.json(mapper, host.location())
54 + .put("time", host.location().time());
55 + ArrayNode ips = mapper.createArrayNode();
56 + for (IpPrefix ip : host.ipAddresses()) {
57 + ips.add(ip.toString());
58 + }
59 + ObjectNode result = mapper.createObjectNode()
60 + .put("id", host.id().toString())
61 + .put("mac", host.mac().toString())
62 + .put("vlan", host.vlan().toString());
63 + result.set("location", loc);
64 + result.set("ips", ips);
65 + return result;
66 + }
67 +
32 /** 68 /**
33 * Returns the list of devices sorted using the device ID URIs. 69 * Returns the list of devices sorted using the device ID URIs.
34 * 70 *
...@@ -44,14 +80,14 @@ public class HostsListCommand extends AbstractShellCommand { ...@@ -44,14 +80,14 @@ public class HostsListCommand extends AbstractShellCommand {
44 /** 80 /**
45 * Prints information about a host. 81 * Prints information about a host.
46 * 82 *
47 - * @param host 83 + * @param host end-station host
48 */ 84 */
49 protected void printHost(Host host) { 85 protected void printHost(Host host) {
50 if (host != null) { 86 if (host != null) {
51 print(FMT, host.id(), host.mac(), 87 print(FMT, host.id(), host.mac(),
52 - host.location().deviceId(), 88 + host.location().deviceId(),
53 - host.location().port(), 89 + host.location().port(),
54 - host.vlan(), host.ipAddresses()); 90 + host.vlan(), host.ipAddresses());
55 } 91 }
56 } 92 }
57 - } 93 +}
......
1 package org.onlab.onos.cli.net; 1 package org.onlab.onos.cli.net;
2 2
3 +import com.fasterxml.jackson.databind.JsonNode;
4 +import com.fasterxml.jackson.databind.ObjectMapper;
5 +import com.fasterxml.jackson.databind.node.ArrayNode;
6 +import com.fasterxml.jackson.databind.node.ObjectNode;
3 import org.apache.karaf.shell.commands.Argument; 7 import org.apache.karaf.shell.commands.Argument;
4 import org.apache.karaf.shell.commands.Command; 8 import org.apache.karaf.shell.commands.Command;
5 import org.onlab.onos.cli.AbstractShellCommand; 9 import org.onlab.onos.cli.AbstractShellCommand;
10 +import org.onlab.onos.net.ConnectPoint;
6 import org.onlab.onos.net.Link; 11 import org.onlab.onos.net.Link;
7 import org.onlab.onos.net.link.LinkService; 12 import org.onlab.onos.net.link.LinkService;
8 13
...@@ -27,9 +32,55 @@ public class LinksListCommand extends AbstractShellCommand { ...@@ -27,9 +32,55 @@ public class LinksListCommand extends AbstractShellCommand {
27 LinkService service = get(LinkService.class); 32 LinkService service = get(LinkService.class);
28 Iterable<Link> links = uri != null ? 33 Iterable<Link> links = uri != null ?
29 service.getDeviceLinks(deviceId(uri)) : service.getLinks(); 34 service.getDeviceLinks(deviceId(uri)) : service.getLinks();
35 + if (outputJson()) {
36 + print("%s", json(links));
37 + } else {
38 + for (Link link : links) {
39 + print(linkString(link));
40 + }
41 + }
42 + }
43 +
44 + /**
45 + * Produces a JSON array containing the specified links.
46 + *
47 + * @param links collection of links
48 + * @return JSON array
49 + */
50 + public static JsonNode json(Iterable<Link> links) {
51 + ObjectMapper mapper = new ObjectMapper();
52 + ArrayNode result = mapper.createArrayNode();
30 for (Link link : links) { 53 for (Link link : links) {
31 - print(linkString(link)); 54 + result.add(json(mapper, link));
32 } 55 }
56 + return result;
57 + }
58 +
59 + /**
60 + * Produces a JSON object for the specified link.
61 + *
62 + * @param mapper object mapper
63 + * @param link link to encode
64 + * @return JSON object
65 + */
66 + public static ObjectNode json(ObjectMapper mapper, Link link) {
67 + ObjectNode result = mapper.createObjectNode();
68 + result.set("src", json(mapper, link.src()));
69 + result.set("dst", json(mapper, link.dst()));
70 + return result;
71 + }
72 +
73 + /**
74 + * Produces a JSON object for the specified connect point.
75 + *
76 + * @param mapper object mapper
77 + * @param connectPoint connection point to encode
78 + * @return JSON object
79 + */
80 + public static ObjectNode json(ObjectMapper mapper, ConnectPoint connectPoint) {
81 + return mapper.createObjectNode()
82 + .put("device", connectPoint.deviceId().toString())
83 + .put("port", connectPoint.port().toString());
33 } 84 }
34 85
35 /** 86 /**
......
1 package org.onlab.onos.cli.net; 1 package org.onlab.onos.cli.net;
2 2
3 +import com.fasterxml.jackson.databind.JsonNode;
4 +import com.fasterxml.jackson.databind.ObjectMapper;
5 +import com.fasterxml.jackson.databind.node.ArrayNode;
3 import org.apache.karaf.shell.commands.Argument; 6 import org.apache.karaf.shell.commands.Argument;
4 import org.apache.karaf.shell.commands.Command; 7 import org.apache.karaf.shell.commands.Command;
5 import org.onlab.onos.net.Link; 8 import org.onlab.onos.net.Link;
...@@ -32,9 +35,30 @@ public class PathListCommand extends TopologyCommand { ...@@ -32,9 +35,30 @@ public class PathListCommand extends TopologyCommand {
32 protected void execute() { 35 protected void execute() {
33 init(); 36 init();
34 Set<Path> paths = service.getPaths(topology, deviceId(src), deviceId(dst)); 37 Set<Path> paths = service.getPaths(topology, deviceId(src), deviceId(dst));
38 + if (outputJson()) {
39 + print("%s", json(paths));
40 + } else {
41 + for (Path path : paths) {
42 + print(pathString(path));
43 + }
44 + }
45 + }
46 +
47 + /**
48 + * Produces a JSON array containing the specified paths.
49 + *
50 + * @param paths collection of paths
51 + * @return JSON array
52 + */
53 + public static JsonNode json(Iterable<Path> paths) {
54 + ObjectMapper mapper = new ObjectMapper();
55 + ArrayNode result = mapper.createArrayNode();
35 for (Path path : paths) { 56 for (Path path : paths) {
36 - print(pathString(path)); 57 + result.add(LinksListCommand.json(mapper, path)
58 + .put("cost", path.cost())
59 + .set("links", LinksListCommand.json(path.links())));
37 } 60 }
61 + return result;
38 } 62 }
39 63
40 /** 64 /**
......
1 package org.onlab.onos.cli.net; 1 package org.onlab.onos.cli.net;
2 2
3 +import com.fasterxml.jackson.databind.ObjectMapper;
3 import org.apache.karaf.shell.commands.Command; 4 import org.apache.karaf.shell.commands.Command;
4 import org.onlab.onos.cli.AbstractShellCommand; 5 import org.onlab.onos.cli.AbstractShellCommand;
5 import org.onlab.onos.net.topology.Topology; 6 import org.onlab.onos.net.topology.Topology;
...@@ -30,8 +31,17 @@ public class TopologyCommand extends AbstractShellCommand { ...@@ -30,8 +31,17 @@ public class TopologyCommand extends AbstractShellCommand {
30 @Override 31 @Override
31 protected void execute() { 32 protected void execute() {
32 init(); 33 init();
33 - print(FMT, topology.time(), topology.deviceCount(), topology.linkCount(), 34 + if (outputJson()) {
34 - topology.clusterCount(), topology.pathCount()); 35 + print("%s", new ObjectMapper().createObjectNode()
36 + .put("time", topology.time())
37 + .put("deviceCount", topology.deviceCount())
38 + .put("linkCount", topology.linkCount())
39 + .put("clusterCount", topology.clusterCount())
40 + .put("pathCount", topology.pathCount()));
41 + } else {
42 + print(FMT, topology.time(), topology.deviceCount(), topology.linkCount(),
43 + topology.clusterCount(), topology.pathCount());
44 + }
35 } 45 }
36 46
37 } 47 }
......
...@@ -12,7 +12,11 @@ public final class ControllerNodeToNodeId ...@@ -12,7 +12,11 @@ public final class ControllerNodeToNodeId
12 12
13 @Override 13 @Override
14 public NodeId apply(ControllerNode input) { 14 public NodeId apply(ControllerNode input) {
15 - return input.id(); 15 + if (input == null) {
16 + return null;
17 + } else {
18 + return input.id();
19 + }
16 } 20 }
17 21
18 /** 22 /**
......
1 package org.onlab.onos.net.host; 1 package org.onlab.onos.net.host;
2 2
3 +import java.util.Collections;
4 +import java.util.Set;
5 +
3 import org.onlab.onos.net.AbstractDescription; 6 import org.onlab.onos.net.AbstractDescription;
4 import org.onlab.onos.net.HostLocation; 7 import org.onlab.onos.net.HostLocation;
5 import org.onlab.onos.net.SparseAnnotations; 8 import org.onlab.onos.net.SparseAnnotations;
...@@ -7,6 +10,8 @@ import org.onlab.packet.IpPrefix; ...@@ -7,6 +10,8 @@ import org.onlab.packet.IpPrefix;
7 import org.onlab.packet.MacAddress; 10 import org.onlab.packet.MacAddress;
8 import org.onlab.packet.VlanId; 11 import org.onlab.packet.VlanId;
9 12
13 +import com.google.common.collect.ImmutableSet;
14 +
10 import static com.google.common.base.MoreObjects.toStringHelper; 15 import static com.google.common.base.MoreObjects.toStringHelper;
11 16
12 /** 17 /**
...@@ -18,7 +23,7 @@ public class DefaultHostDescription extends AbstractDescription ...@@ -18,7 +23,7 @@ public class DefaultHostDescription extends AbstractDescription
18 private final MacAddress mac; 23 private final MacAddress mac;
19 private final VlanId vlan; 24 private final VlanId vlan;
20 private final HostLocation location; 25 private final HostLocation location;
21 - private final IpPrefix ip; 26 + private final Set<IpPrefix> ip;
22 27
23 /** 28 /**
24 * Creates a host description using the supplied information. 29 * Creates a host description using the supplied information.
...@@ -31,7 +36,7 @@ public class DefaultHostDescription extends AbstractDescription ...@@ -31,7 +36,7 @@ public class DefaultHostDescription extends AbstractDescription
31 public DefaultHostDescription(MacAddress mac, VlanId vlan, 36 public DefaultHostDescription(MacAddress mac, VlanId vlan,
32 HostLocation location, 37 HostLocation location,
33 SparseAnnotations... annotations) { 38 SparseAnnotations... annotations) {
34 - this(mac, vlan, location, null, annotations); 39 + this(mac, vlan, location, Collections.<IpPrefix>emptySet(), annotations);
35 } 40 }
36 41
37 /** 42 /**
...@@ -46,11 +51,26 @@ public class DefaultHostDescription extends AbstractDescription ...@@ -46,11 +51,26 @@ public class DefaultHostDescription extends AbstractDescription
46 public DefaultHostDescription(MacAddress mac, VlanId vlan, 51 public DefaultHostDescription(MacAddress mac, VlanId vlan,
47 HostLocation location, IpPrefix ip, 52 HostLocation location, IpPrefix ip,
48 SparseAnnotations... annotations) { 53 SparseAnnotations... annotations) {
54 + this(mac, vlan, location, ImmutableSet.of(ip), annotations);
55 + }
56 +
57 + /**
58 + * Creates a host description using the supplied information.
59 + *
60 + * @param mac host MAC address
61 + * @param vlan host VLAN identifier
62 + * @param location host location
63 + * @param ip host IP addresses
64 + * @param annotations optional key/value annotations map
65 + */
66 + public DefaultHostDescription(MacAddress mac, VlanId vlan,
67 + HostLocation location, Set<IpPrefix> ip,
68 + SparseAnnotations... annotations) {
49 super(annotations); 69 super(annotations);
50 this.mac = mac; 70 this.mac = mac;
51 this.vlan = vlan; 71 this.vlan = vlan;
52 this.location = location; 72 this.location = location;
53 - this.ip = ip; 73 + this.ip = ImmutableSet.copyOf(ip);
54 } 74 }
55 75
56 @Override 76 @Override
...@@ -69,7 +89,7 @@ public class DefaultHostDescription extends AbstractDescription ...@@ -69,7 +89,7 @@ public class DefaultHostDescription extends AbstractDescription
69 } 89 }
70 90
71 @Override 91 @Override
72 - public IpPrefix ipAddress() { 92 + public Set<IpPrefix> ipAddress() {
73 return ip; 93 return ip;
74 } 94 }
75 95
......
1 package org.onlab.onos.net.host; 1 package org.onlab.onos.net.host;
2 2
3 +import java.util.Set;
4 +
3 import org.onlab.onos.net.Description; 5 import org.onlab.onos.net.Description;
4 import org.onlab.onos.net.HostLocation; 6 import org.onlab.onos.net.HostLocation;
5 import org.onlab.packet.IpPrefix; 7 import org.onlab.packet.IpPrefix;
...@@ -38,6 +40,6 @@ public interface HostDescription extends Description { ...@@ -38,6 +40,6 @@ public interface HostDescription extends Description {
38 * @return host IP address 40 * @return host IP address
39 */ 41 */
40 // FIXME: Switch to IpAddress 42 // FIXME: Switch to IpAddress
41 - IpPrefix ipAddress(); 43 + Set<IpPrefix> ipAddress();
42 44
43 } 45 }
......
...@@ -12,7 +12,7 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -12,7 +12,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
12 /** 12 /**
13 * Abstraction of end-station to end-station bidirectional connectivity. 13 * Abstraction of end-station to end-station bidirectional connectivity.
14 */ 14 */
15 -public class HostToHostIntent extends ConnectivityIntent { 15 +public final class HostToHostIntent extends ConnectivityIntent {
16 16
17 private final HostId one; 17 private final HostId one;
18 private final HostId two; 18 private final HostId two;
......
...@@ -14,7 +14,7 @@ import com.google.common.base.MoreObjects; ...@@ -14,7 +14,7 @@ import com.google.common.base.MoreObjects;
14 * Abstraction of a connectivity intent that is implemented by a set of path 14 * Abstraction of a connectivity intent that is implemented by a set of path
15 * segments. 15 * segments.
16 */ 16 */
17 -public class LinkCollectionIntent extends ConnectivityIntent implements InstallableIntent { 17 +public final class LinkCollectionIntent extends ConnectivityIntent implements InstallableIntent {
18 18
19 private final Set<Link> links; 19 private final Set<Link> links;
20 20
...@@ -46,6 +46,12 @@ public class LinkCollectionIntent extends ConnectivityIntent implements Installa ...@@ -46,6 +46,12 @@ public class LinkCollectionIntent extends ConnectivityIntent implements Installa
46 return links; 46 return links;
47 } 47 }
48 48
49 + /**
50 + * Returns the set of links that represent the network connections needed
51 + * by this intent.
52 + *
53 + * @return Set of links for the network hops needed by this intent
54 + */
49 public Set<Link> links() { 55 public Set<Link> links() {
50 return links; 56 return links;
51 } 57 }
......
...@@ -15,7 +15,7 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -15,7 +15,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
15 /** 15 /**
16 * Abstraction of multiple source to single destination connectivity intent. 16 * Abstraction of multiple source to single destination connectivity intent.
17 */ 17 */
18 -public class MultiPointToSinglePointIntent extends ConnectivityIntent { 18 +public final class MultiPointToSinglePointIntent extends ConnectivityIntent {
19 19
20 private final Set<ConnectPoint> ingressPoints; 20 private final Set<ConnectPoint> ingressPoints;
21 private final ConnectPoint egressPoint; 21 private final ConnectPoint egressPoint;
......
1 package org.onlab.onos.cluster; 1 package org.onlab.onos.cluster;
2 2
3 +import static com.google.common.base.Predicates.notNull;
3 import static org.junit.Assert.*; 4 import static org.junit.Assert.*;
4 import static org.onlab.onos.cluster.ControllerNodeToNodeId.toNodeId; 5 import static org.onlab.onos.cluster.ControllerNodeToNodeId.toNodeId;
5 6
...@@ -30,12 +31,13 @@ public class ControllerNodeToNodeIdTest { ...@@ -30,12 +31,13 @@ public class ControllerNodeToNodeIdTest {
30 @Test 31 @Test
31 public final void testToNodeId() { 32 public final void testToNodeId() {
32 33
33 - final Iterable<ControllerNode> nodes = Arrays.asList(CN1, CN2, CN3); 34 + final Iterable<ControllerNode> nodes = Arrays.asList(CN1, CN2, CN3, null);
34 final List<NodeId> nodeIds = Arrays.asList(NID1, NID2, NID3); 35 final List<NodeId> nodeIds = Arrays.asList(NID1, NID2, NID3);
35 36
36 assertEquals(nodeIds, 37 assertEquals(nodeIds,
37 FluentIterable.from(nodes) 38 FluentIterable.from(nodes)
38 .transform(toNodeId()) 39 .transform(toNodeId())
40 + .filter(notNull())
39 .toList()); 41 .toList());
40 } 42 }
41 43
......
...@@ -48,10 +48,16 @@ public final class NetTestTools { ...@@ -48,10 +48,16 @@ public final class NetTestTools {
48 new HashSet<IpPrefix>()); 48 new HashSet<IpPrefix>());
49 } 49 }
50 50
51 + // Short-hand for creating a connection point.
52 + public static ConnectPoint connectPoint(String id, int port) {
53 + return new ConnectPoint(did(id), portNumber(port));
54 + }
55 +
51 // Short-hand for creating a link. 56 // Short-hand for creating a link.
52 public static Link link(String src, int sp, String dst, int dp) { 57 public static Link link(String src, int sp, String dst, int dp) {
53 - return new DefaultLink(PID, new ConnectPoint(did(src), portNumber(sp)), 58 + return new DefaultLink(PID,
54 - new ConnectPoint(did(dst), portNumber(dp)), 59 + connectPoint(src, sp),
60 + connectPoint(dst, dp),
55 Link.Type.DIRECT); 61 Link.Type.DIRECT);
56 } 62 }
57 63
......
...@@ -8,6 +8,8 @@ import org.onlab.packet.IpPrefix; ...@@ -8,6 +8,8 @@ import org.onlab.packet.IpPrefix;
8 import org.onlab.packet.MacAddress; 8 import org.onlab.packet.MacAddress;
9 import org.onlab.packet.VlanId; 9 import org.onlab.packet.VlanId;
10 10
11 +import com.google.common.collect.ImmutableSet;
12 +
11 import static org.junit.Assert.assertEquals; 13 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertTrue; 14 import static org.junit.Assert.assertTrue;
13 15
...@@ -33,7 +35,7 @@ public class DefualtHostDecriptionTest { ...@@ -33,7 +35,7 @@ public class DefualtHostDecriptionTest {
33 assertEquals("incorrect mac", MAC, host.hwAddress()); 35 assertEquals("incorrect mac", MAC, host.hwAddress());
34 assertEquals("incorrect vlan", VLAN, host.vlan()); 36 assertEquals("incorrect vlan", VLAN, host.vlan());
35 assertEquals("incorrect location", LOC, host.location()); 37 assertEquals("incorrect location", LOC, host.location());
36 - assertEquals("incorrect ip's", IP, host.ipAddress()); 38 + assertEquals("incorrect ip's", ImmutableSet.of(IP), host.ipAddress());
37 assertTrue("incorrect toString", host.toString().contains("vlan=10")); 39 assertTrue("incorrect toString", host.toString().contains("vlan=10"));
38 } 40 }
39 41
......
...@@ -6,15 +6,15 @@ ...@@ -6,15 +6,15 @@
6 6
7 <parent> 7 <parent>
8 <groupId>org.onlab.onos</groupId> 8 <groupId>org.onlab.onos</groupId>
9 - <artifactId>onos-core-hz</artifactId> 9 + <artifactId>onos-core</artifactId>
10 <version>1.0.0-SNAPSHOT</version> 10 <version>1.0.0-SNAPSHOT</version>
11 <relativePath>../pom.xml</relativePath> 11 <relativePath>../pom.xml</relativePath>
12 </parent> 12 </parent>
13 13
14 - <artifactId>onos-core-hz-net</artifactId> 14 + <artifactId>onos-json</artifactId>
15 <packaging>bundle</packaging> 15 <packaging>bundle</packaging>
16 16
17 - <description>ONOS Hazelcast based distributed store subsystems</description> 17 + <description>ONOS JSON encode/decode facilities</description>
18 18
19 <dependencies> 19 <dependencies>
20 <dependency> 20 <dependency>
...@@ -23,24 +23,22 @@ ...@@ -23,24 +23,22 @@
23 </dependency> 23 </dependency>
24 <dependency> 24 <dependency>
25 <groupId>org.onlab.onos</groupId> 25 <groupId>org.onlab.onos</groupId>
26 - <artifactId>onos-core-hz-common</artifactId> 26 + <artifactId>onos-api</artifactId>
27 - <version>${project.version}</version> 27 + <classifier>tests</classifier>
28 + <scope>test</scope>
28 </dependency> 29 </dependency>
30 +
29 <dependency> 31 <dependency>
30 <groupId>org.onlab.onos</groupId> 32 <groupId>org.onlab.onos</groupId>
31 - <artifactId>onos-core-hz-common</artifactId> 33 + <artifactId>onos-core-trivial</artifactId>
32 - <classifier>tests</classifier>
33 - <scope>test</scope>
34 <version>${project.version}</version> 34 <version>${project.version}</version>
35 + <scope>test</scope>
35 </dependency> 36 </dependency>
37 +
36 <dependency> 38 <dependency>
37 <groupId>org.apache.felix</groupId> 39 <groupId>org.apache.felix</groupId>
38 <artifactId>org.apache.felix.scr.annotations</artifactId> 40 <artifactId>org.apache.felix.scr.annotations</artifactId>
39 </dependency> 41 </dependency>
40 - <dependency>
41 - <groupId>com.hazelcast</groupId>
42 - <artifactId>hazelcast</artifactId>
43 - </dependency>
44 </dependencies> 42 </dependencies>
45 43
46 <build> 44 <build>
......
1 +package org.onlab.onos.json.impl;
2 +
3 +/**
4 + * Created by tom on 10/16/14.
5 + */
6 +public class DeleteMe {
7 +}
1 +/**
2 + * Implementation of JSON codec factory and of the builtin codecs.
3 + */
4 +package org.onlab.onos.json.impl;
...\ No newline at end of file ...\ No newline at end of file
...@@ -36,26 +36,9 @@ ...@@ -36,26 +36,9 @@
36 <scope>test</scope> 36 <scope>test</scope>
37 </dependency> 37 </dependency>
38 38
39 - <dependency>
40 - <groupId>org.easymock</groupId>
41 - <artifactId>easymock</artifactId>
42 - <scope>test</scope>
43 - </dependency>
44 -
45 - <!-- TODO Consider removing store dependency.
46 - Currently required for DistributedDeviceManagerTest. -->
47 <dependency> 39 <dependency>
48 - <groupId>org.onlab.onos</groupId> 40 + <groupId>org.easymock</groupId>
49 - <artifactId>onos-core-hz-net</artifactId> 41 + <artifactId>easymock</artifactId>
50 - <version>${project.version}</version>
51 - <scope>test</scope>
52 - </dependency>
53 - <dependency>
54 - <groupId>org.onlab.onos</groupId>
55 - <!-- FIXME: should be somewhere else -->
56 - <artifactId>onos-core-hz-common</artifactId>
57 - <version>${project.version}</version>
58 - <classifier>tests</classifier>
59 <scope>test</scope> 42 <scope>test</scope>
60 </dependency> 43 </dependency>
61 44
......
1 /** 1 /**
2 - * 2 + * Miscellaneous core system implementations.
3 */ 3 */
4 package org.onlab.onos.impl; 4 package org.onlab.onos.impl;
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -41,7 +41,7 @@ public class HostToHostIntentCompiler ...@@ -41,7 +41,7 @@ public class HostToHostIntentCompiler
41 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 41 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
42 protected HostService hostService; 42 protected HostService hostService;
43 43
44 - private IdGenerator<IntentId> intentIdGenerator; 44 + protected IdGenerator<IntentId> intentIdGenerator;
45 45
46 @Activate 46 @Activate
47 public void activate() { 47 public void activate() {
......
...@@ -37,7 +37,7 @@ public class MultiPointToSinglePointIntentCompiler ...@@ -37,7 +37,7 @@ public class MultiPointToSinglePointIntentCompiler
37 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 37 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
38 protected PathService pathService; 38 protected PathService pathService;
39 39
40 - private IdGenerator<IntentId> intentIdGenerator; 40 + protected IdGenerator<IntentId> intentIdGenerator;
41 41
42 @Activate 42 @Activate
43 public void activate() { 43 public void activate() {
......
...@@ -355,7 +355,7 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -355,7 +355,7 @@ public class ProxyArpManager implements ProxyArpService {
355 355
356 arp.setTargetProtocolAddress(((ARP) request.getPayload()) 356 arp.setTargetProtocolAddress(((ARP) request.getPayload())
357 .getSenderProtocolAddress()); 357 .getSenderProtocolAddress());
358 - arp.setSenderProtocolAddress(srcIp.toRealInt()); 358 + arp.setSenderProtocolAddress(srcIp.toInt());
359 eth.setPayload(arp); 359 eth.setPayload(arp);
360 return eth; 360 return eth;
361 } 361 }
......
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.ArrayList;
4 +import java.util.Arrays;
5 +import java.util.Collections;
6 +import java.util.HashSet;
7 +import java.util.List;
8 +import java.util.Set;
9 +
10 +import org.onlab.onos.net.ElementId;
11 +import org.onlab.onos.net.Path;
12 +import org.onlab.onos.net.flow.TrafficSelector;
13 +import org.onlab.onos.net.flow.TrafficTreatment;
14 +import org.onlab.onos.net.flow.criteria.Criterion;
15 +import org.onlab.onos.net.flow.instructions.Instruction;
16 +import org.onlab.onos.net.topology.LinkWeight;
17 +import org.onlab.onos.net.topology.PathService;
18 +
19 +import static org.onlab.onos.net.NetTestTools.createPath;
20 +
21 +/**
22 + * Common mocks used by the intent framework tests.
23 + */
24 +public class IntentTestsMocks {
25 + /**
26 + * Mock traffic selector class used for satisfying API requirements.
27 + */
28 + public static class MockSelector implements TrafficSelector {
29 + @Override
30 + public Set<Criterion> criteria() {
31 + return new HashSet<>();
32 + }
33 + }
34 +
35 + /**
36 + * Mock traffic treatment class used for satisfying API requirements.
37 + */
38 + public static class MockTreatment implements TrafficTreatment {
39 + @Override
40 + public List<Instruction> instructions() {
41 + return new ArrayList<>();
42 + }
43 + }
44 +
45 + /**
46 + * Mock path service for creating paths within the test.
47 + */
48 + public static class MockPathService implements PathService {
49 +
50 + final String[] pathHops;
51 + final String[] reversePathHops;
52 +
53 + /**
54 + * Constructor that provides a set of hops to mock.
55 + *
56 + * @param pathHops path hops to mock
57 + */
58 + public MockPathService(String[] pathHops) {
59 + this.pathHops = pathHops;
60 + String[] reversed = pathHops.clone();
61 + Collections.reverse(Arrays.asList(reversed));
62 + reversePathHops = reversed;
63 + }
64 +
65 + @Override
66 + public Set<Path> getPaths(ElementId src, ElementId dst) {
67 + Set<Path> result = new HashSet<>();
68 +
69 + String[] allHops = new String[pathHops.length];
70 +
71 + if (src.toString().endsWith(pathHops[0])) {
72 + System.arraycopy(pathHops, 0, allHops, 0, pathHops.length);
73 + } else {
74 + System.arraycopy(reversePathHops, 0, allHops, 0, pathHops.length);
75 + }
76 +
77 + result.add(createPath(allHops));
78 + return result;
79 + }
80 +
81 + @Override
82 + public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeight weight) {
83 + return getPaths(src, dst);
84 + }
85 + }
86 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.Collection;
4 +
5 +import org.hamcrest.Description;
6 +import org.hamcrest.TypeSafeMatcher;
7 +import org.onlab.onos.net.Link;
8 +
9 +/**
10 + * Matcher to determine if a Collection of Links contains a path between a source
11 + * and a destination.
12 + */
13 +public class LinksHaveEntryWithSourceDestinationPairMatcher extends
14 + TypeSafeMatcher<Collection<Link>> {
15 + private final String source;
16 + private final String destination;
17 +
18 + /**
19 + * Creates a matcher for a given path represented by a source and
20 + * a destination.
21 + *
22 + * @param source string identifier for the source of the path
23 + * @param destination string identifier for the destination of the path
24 + */
25 + LinksHaveEntryWithSourceDestinationPairMatcher(String source,
26 + String destination) {
27 + this.source = source;
28 + this.destination = destination;
29 + }
30 +
31 + @Override
32 + public boolean matchesSafely(Collection<Link> links) {
33 + for (Link link : links) {
34 + if (link.src().elementId().toString().endsWith(source) &&
35 + link.dst().elementId().toString().endsWith(destination)) {
36 + return true;
37 + }
38 + }
39 +
40 + return false;
41 + }
42 +
43 + @Override
44 + public void describeTo(Description description) {
45 + description.appendText("link lookup for source \"");
46 + description.appendText(source);
47 + description.appendText(" and destination ");
48 + description.appendText(destination);
49 + description.appendText("\"");
50 + }
51 +
52 + @Override
53 + public void describeMismatchSafely(Collection<Link> links,
54 + Description mismatchDescription) {
55 + mismatchDescription.appendText("was ").
56 + appendText(links.toString());
57 + }
58 +
59 + /**
60 + * Creates a link has path matcher.
61 + *
62 + * @param source string identifier for the source of the path
63 + * @param destination string identifier for the destination of the path
64 + * @return matcher to match the path
65 + */
66 + public static LinksHaveEntryWithSourceDestinationPairMatcher linksHasPath(
67 + String source,
68 + String destination) {
69 + return new LinksHaveEntryWithSourceDestinationPairMatcher(source,
70 + destination);
71 + }
72 +}
73 +
1 +package org.onlab.onos.net.intent;
2 +
3 +import org.junit.Test;
4 +import org.onlab.onos.net.HostId;
5 +import org.onlab.onos.net.flow.TrafficSelector;
6 +import org.onlab.onos.net.flow.TrafficTreatment;
7 +
8 +import static org.hamcrest.MatcherAssert.assertThat;
9 +import static org.hamcrest.Matchers.equalTo;
10 +import static org.hamcrest.Matchers.is;
11 +import static org.hamcrest.Matchers.not;
12 +import static org.onlab.onos.net.NetTestTools.hid;
13 +
14 +/**
15 + * Unit tests for the HostToHostIntent class.
16 + */
17 +public class TestHostToHostIntent {
18 +
19 + private TrafficSelector selector = new IntentTestsMocks.MockSelector();
20 + private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment();
21 +
22 + private HostToHostIntent makeHostToHost(long id, HostId one, HostId two) {
23 + return new HostToHostIntent(new IntentId(id),
24 + one,
25 + two,
26 + selector,
27 + treatment);
28 + }
29 +
30 + /**
31 + * Tests the equals() method where two HostToHostIntents have references
32 + * to the same hosts. These should compare equal.
33 + */
34 + @Test
35 + public void testSameEquals() {
36 +
37 + HostId one = hid("00:00:00:00:00:01/-1");
38 + HostId two = hid("00:00:00:00:00:02/-1");
39 + HostToHostIntent i1 = makeHostToHost(12, one, two);
40 + HostToHostIntent i2 = makeHostToHost(12, one, two);
41 +
42 + assertThat(i1, is(equalTo(i2)));
43 + }
44 +
45 + /**
46 + * Tests the equals() method where two HostToHostIntents have references
47 + * to different Hosts. These should compare not equal.
48 + */
49 + @Test
50 + public void testLinksDifferentEquals() {
51 +
52 + HostId one = hid("00:00:00:00:00:01/-1");
53 + HostId two = hid("00:00:00:00:00:02/-1");
54 + HostToHostIntent i1 = makeHostToHost(12, one, two);
55 + HostToHostIntent i2 = makeHostToHost(12, two, one);
56 +
57 + assertThat(i1, is(not(equalTo(i2))));
58 + }
59 +
60 + /**
61 + * Tests the equals() method where two HostToHostIntents have different
62 + * ids. These should compare not equal.
63 + */
64 +
65 + @Test
66 + public void testBaseDifferentEquals() {
67 + HostId one = hid("00:00:00:00:00:01/-1");
68 + HostId two = hid("00:00:00:00:00:02/-1");
69 + HostToHostIntent i1 = makeHostToHost(12, one, two);
70 + HostToHostIntent i2 = makeHostToHost(11, one, two);
71 +
72 + assertThat(i1, is(not(equalTo(i2))));
73 + }
74 +
75 + /**
76 + * Tests that the hashCode() values for two equivalent HostToHostIntent
77 + * objects are the same.
78 + */
79 +
80 + @Test
81 + public void testHashCodeEquals() {
82 + HostId one = hid("00:00:00:00:00:01/-1");
83 + HostId two = hid("00:00:00:00:00:02/-1");
84 + HostToHostIntent i1 = makeHostToHost(12, one, two);
85 + HostToHostIntent i2 = makeHostToHost(12, one, two);
86 +
87 + assertThat(i1.hashCode(), is(equalTo(i2.hashCode())));
88 + }
89 +
90 + /**
91 + * Tests that the hashCode() values for two distinct LinkCollectionIntent
92 + * objects are different.
93 + */
94 +
95 + @Test
96 + public void testHashCodeDifferent() {
97 + HostId one = hid("00:00:00:00:00:01/-1");
98 + HostId two = hid("00:00:00:00:00:02/-1");
99 + HostToHostIntent i1 = makeHostToHost(12, one, two);
100 + HostToHostIntent i2 = makeHostToHost(112, one, two);
101 +
102 + assertThat(i1.hashCode(), is(not(equalTo(i2.hashCode()))));
103 + }
104 +
105 + /**
106 + * Checks that the HostToHostIntent class is immutable.
107 + */
108 + @Test
109 + public void checkImmutability() {
110 + ImmutableClassChecker.assertThatClassIsImmutable(HostToHostIntent.class);
111 + }
112 +}
1 package org.onlab.onos.net.intent; 1 package org.onlab.onos.net.intent;
2 2
3 -import java.util.ArrayList;
4 import java.util.HashSet; 3 import java.util.HashSet;
5 -import java.util.List;
6 import java.util.Set; 4 import java.util.Set;
7 5
6 +import org.junit.Before;
8 import org.junit.Test; 7 import org.junit.Test;
9 import org.onlab.onos.net.Link; 8 import org.onlab.onos.net.Link;
10 import org.onlab.onos.net.flow.TrafficSelector; 9 import org.onlab.onos.net.flow.TrafficSelector;
11 import org.onlab.onos.net.flow.TrafficTreatment; 10 import org.onlab.onos.net.flow.TrafficTreatment;
12 -import org.onlab.onos.net.flow.criteria.Criterion;
13 -import org.onlab.onos.net.flow.instructions.Instruction;
14 11
12 +import static org.hamcrest.CoreMatchers.not;
15 import static org.hamcrest.MatcherAssert.assertThat; 13 import static org.hamcrest.MatcherAssert.assertThat;
14 +import static org.hamcrest.Matchers.equalTo;
16 import static org.hamcrest.Matchers.is; 15 import static org.hamcrest.Matchers.is;
16 +import static org.onlab.onos.net.NetTestTools.link;
17 17
18 +/**
19 + * Unit tests for the LinkCollectionIntent class.
20 + */
18 public class TestLinkCollectionIntent { 21 public class TestLinkCollectionIntent {
19 22
20 - private static class MockSelector implements TrafficSelector { 23 + private Link link1 = link("dev1", 1, "dev2", 2);
21 - @Override 24 + private Link link2 = link("dev1", 1, "dev3", 2);
22 - public Set<Criterion> criteria() { 25 + private Link link3 = link("dev2", 1, "dev3", 2);
23 - return new HashSet<Criterion>(); 26 +
24 - } 27 + private Set<Link> links1;
28 + private Set<Link> links2;
29 +
30 + private TrafficSelector selector = new IntentTestsMocks.MockSelector();
31 + private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment();
32 +
33 + private LinkCollectionIntent makeLinkCollection(long id, Set<Link> links) {
34 + return new LinkCollectionIntent(new IntentId(id),
35 + selector, treatment, links);
36 + }
37 +
38 + @Before
39 + public void setup() {
40 + links1 = new HashSet<>();
41 + links2 = new HashSet<>();
25 } 42 }
26 43
27 - private static class MockTreatment implements TrafficTreatment { 44 + /**
28 - @Override 45 + * Tests the equals() method where two LinkCollectionIntents have references
29 - public List<Instruction> instructions() { 46 + * to the same Links in different orders. These should compare equal.
30 - return new ArrayList<>(); 47 + */
31 - } 48 + @Test
49 + public void testSameEquals() {
50 + links1.add(link1);
51 + links1.add(link2);
52 + links1.add(link3);
53 +
54 + links2.add(link3);
55 + links2.add(link2);
56 + links2.add(link1);
57 +
58 + LinkCollectionIntent i1 = makeLinkCollection(12, links1);
59 + LinkCollectionIntent i2 = makeLinkCollection(12, links2);
60 +
61 + assertThat(i1, is(equalTo(i2)));
32 } 62 }
33 63
64 + /**
65 + * Tests the equals() method where two LinkCollectionIntents have references
66 + * to different Links. These should compare not equal.
67 + */
34 @Test 68 @Test
35 - public void testComparison() { 69 + public void testLinksDifferentEquals() {
36 - TrafficSelector selector = new MockSelector(); 70 + links1.add(link1);
37 - TrafficTreatment treatment = new MockTreatment(); 71 + links1.add(link2);
38 - Set<Link> links = new HashSet<>(); 72 +
39 - LinkCollectionIntent i1 = new LinkCollectionIntent(new IntentId(12), 73 + links2.add(link3);
40 - selector, treatment, links); 74 + links2.add(link1);
41 - LinkCollectionIntent i2 = new LinkCollectionIntent(new IntentId(12),
42 - selector, treatment, links);
43 75
44 - assertThat(i1.equals(i2), is(true)); 76 + LinkCollectionIntent i1 = makeLinkCollection(12, links1);
77 + LinkCollectionIntent i2 = makeLinkCollection(12, links2);
78 +
79 + assertThat(i1, is(not(equalTo(i2))));
45 } 80 }
46 81
82 + /**
83 + * Tests the equals() method where two LinkCollectionIntents have different
84 + * ids. These should compare not equal.
85 + */
86 + @Test
87 + public void testBaseDifferentEquals() {
88 + links1.add(link1);
89 + links1.add(link2);
90 +
91 + links2.add(link2);
92 + links2.add(link1);
93 +
94 + LinkCollectionIntent i1 = makeLinkCollection(1, links1);
95 + LinkCollectionIntent i2 = makeLinkCollection(2, links2);
96 +
97 + assertThat(i1, is(not(equalTo(i2))));
98 + }
99 +
100 + /**
101 + * Tests that the hashCode() values for two equivalent LinkCollectionIntent
102 + * objects are the same.
103 + */
104 + @Test
105 + public void testHashCodeEquals() {
106 + links1.add(link1);
107 + links1.add(link2);
108 + links1.add(link3);
109 +
110 + links2.add(link3);
111 + links2.add(link2);
112 + links2.add(link1);
113 +
114 + LinkCollectionIntent i1 = makeLinkCollection(1, links1);
115 + LinkCollectionIntent i2 = makeLinkCollection(1, links2);
116 +
117 + assertThat(i1.hashCode(), is(equalTo(i2.hashCode())));
118 + }
119 +
120 + /**
121 + * Tests that the hashCode() values for two distinct LinkCollectionIntent
122 + * objects are different.
123 + */
124 + @Test
125 + public void testHashCodeDifferent() {
126 + links1.add(link1);
127 + links1.add(link2);
128 +
129 + links2.add(link1);
130 + links2.add(link3);
131 +
132 + LinkCollectionIntent i1 = makeLinkCollection(1, links1);
133 + LinkCollectionIntent i2 = makeLinkCollection(1, links2);
134 +
135 + assertThat(i1.hashCode(), is(not(equalTo(i2.hashCode()))));
136 + }
137 +
138 + /**
139 + * Checks that the HostToHostIntent class is immutable.
140 + */
141 + @Test
142 + public void checkImmutability() {
143 + ImmutableClassChecker.assertThatClassIsImmutable(LinkCollectionIntent.class);
144 + }
47 } 145 }
......
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.HashSet;
4 +import java.util.Set;
5 +
6 +import org.junit.Before;
7 +import org.junit.Test;
8 +import org.onlab.onos.net.ConnectPoint;
9 +import org.onlab.onos.net.flow.TrafficSelector;
10 +import org.onlab.onos.net.flow.TrafficTreatment;
11 +
12 +import static org.hamcrest.CoreMatchers.not;
13 +import static org.hamcrest.MatcherAssert.assertThat;
14 +import static org.hamcrest.Matchers.equalTo;
15 +import static org.hamcrest.Matchers.is;
16 +import static org.onlab.onos.net.NetTestTools.connectPoint;
17 +
18 +/**
19 + * Unit tests for the MultiPointToSinglePointIntent class.
20 + */
21 +public class TestMultiPointToSinglePointIntent {
22 +
23 + private ConnectPoint point1 = connectPoint("dev1", 1);
24 + private ConnectPoint point2 = connectPoint("dev2", 1);
25 + private ConnectPoint point3 = connectPoint("dev3", 1);
26 +
27 + private TrafficSelector selector = new IntentTestsMocks.MockSelector();
28 + private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment();
29 +
30 + Set<ConnectPoint> ingress1;
31 + Set<ConnectPoint> ingress2;
32 +
33 + /**
34 + * Creates a MultiPointToSinglePointIntent object.
35 + *
36 + * @param id identifier to use for the new intent
37 + * @param ingress set of ingress points
38 + * @param egress egress point
39 + * @return MultiPointToSinglePoint intent
40 + */
41 + private MultiPointToSinglePointIntent makeIntent(long id,
42 + Set<ConnectPoint> ingress,
43 + ConnectPoint egress) {
44 + return new MultiPointToSinglePointIntent(new IntentId(id),
45 + selector,
46 + treatment,
47 + ingress,
48 + egress);
49 + }
50 +
51 + /**
52 + * Initializes the ingress sets.
53 + */
54 + @Before
55 + public void setup() {
56 + ingress1 = new HashSet<>();
57 + ingress2 = new HashSet<>();
58 + }
59 +
60 + /**
61 + * Tests the equals() method where two MultiPointToSinglePoint have references
62 + * to the same Links in different orders. These should compare equal.
63 + */
64 + @Test
65 + public void testSameEquals() {
66 +
67 + Set<ConnectPoint> ingress1 = new HashSet<>();
68 + ingress1.add(point2);
69 + ingress1.add(point3);
70 +
71 + Set<ConnectPoint> ingress2 = new HashSet<>();
72 + ingress2.add(point3);
73 + ingress2.add(point2);
74 +
75 + Intent i1 = makeIntent(12, ingress1, point1);
76 + Intent i2 = makeIntent(12, ingress2, point1);
77 +
78 + assertThat(i1, is(equalTo(i2)));
79 + }
80 +
81 + /**
82 + * Tests the equals() method where two MultiPointToSinglePoint have references
83 + * to different Links. These should compare not equal.
84 + */
85 + @Test
86 + public void testLinksDifferentEquals() {
87 + ingress1.add(point3);
88 +
89 + ingress2.add(point3);
90 + ingress2.add(point2);
91 +
92 + Intent i1 = makeIntent(12, ingress1, point1);
93 + Intent i2 = makeIntent(12, ingress2, point1);
94 +
95 + assertThat(i1, is(not(equalTo(i2))));
96 + }
97 +
98 + /**
99 + * Tests the equals() method where two MultiPointToSinglePoint have different
100 + * ids. These should compare not equal.
101 + */
102 + @Test
103 + public void testBaseDifferentEquals() {
104 + ingress1.add(point3);
105 + ingress2.add(point3);
106 +
107 + Intent i1 = makeIntent(12, ingress1, point1);
108 + Intent i2 = makeIntent(11, ingress2, point1);
109 +
110 + assertThat(i1, is(not(equalTo(i2))));
111 + }
112 +
113 + /**
114 + * Tests that the hashCode() values for two equivalent MultiPointToSinglePoint
115 + * objects are the same.
116 + */
117 + @Test
118 + public void testHashCodeEquals() {
119 + ingress1.add(point2);
120 + ingress1.add(point3);
121 +
122 + ingress2.add(point3);
123 + ingress2.add(point2);
124 +
125 + Intent i1 = makeIntent(12, ingress1, point1);
126 + Intent i2 = makeIntent(12, ingress2, point1);
127 +
128 + assertThat(i1.hashCode(), is(equalTo(i2.hashCode())));
129 + }
130 +
131 + /**
132 + * Tests that the hashCode() values for two distinct MultiPointToSinglePoint
133 + * objects are different.
134 + */
135 + @Test
136 + public void testHashCodeDifferent() {
137 + ingress1.add(point2);
138 +
139 + ingress2.add(point3);
140 + ingress2.add(point2);
141 +
142 + Intent i1 = makeIntent(12, ingress1, point1);
143 + Intent i2 = makeIntent(12, ingress2, point1);
144 +
145 +
146 + assertThat(i1.hashCode(), is(not(equalTo(i2.hashCode()))));
147 + }
148 +
149 + /**
150 + * Checks that the MultiPointToSinglePointIntent class is immutable.
151 + */
152 + @Test
153 + public void checkImmutability() {
154 + ImmutableClassChecker.
155 + assertThatClassIsImmutable(MultiPointToSinglePointIntent.class);
156 + }
157 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import java.util.List;
4 +
5 +import org.hamcrest.Matchers;
6 +import org.junit.Before;
7 +import org.junit.Test;
8 +import org.onlab.onos.net.Host;
9 +import org.onlab.onos.net.HostId;
10 +import org.onlab.onos.net.flow.TrafficSelector;
11 +import org.onlab.onos.net.flow.TrafficTreatment;
12 +import org.onlab.onos.net.host.HostService;
13 +import org.onlab.onos.net.intent.HostToHostIntent;
14 +import org.onlab.onos.net.intent.Intent;
15 +import org.onlab.onos.net.intent.IntentId;
16 +import org.onlab.onos.net.intent.IntentTestsMocks;
17 +import org.onlab.onos.net.intent.PathIntent;
18 +import org.onlab.packet.MacAddress;
19 +import org.onlab.packet.VlanId;
20 +
21 +import static org.easymock.EasyMock.createMock;
22 +import static org.easymock.EasyMock.eq;
23 +import static org.easymock.EasyMock.expect;
24 +import static org.easymock.EasyMock.replay;
25 +import static org.hamcrest.CoreMatchers.notNullValue;
26 +import static org.hamcrest.MatcherAssert.assertThat;
27 +import static org.hamcrest.Matchers.hasSize;
28 +import static org.hamcrest.Matchers.is;
29 +import static org.onlab.onos.net.NetTestTools.hid;
30 +import static org.onlab.onos.net.intent.LinksHaveEntryWithSourceDestinationPairMatcher.linksHasPath;
31 +
32 +/**
33 + * Unit tests for the HostToHost intent compiler.
34 + */
35 +public class TestHostToHostIntentCompiler {
36 + private static final String HOST_ONE_MAC = "00:00:00:00:00:01";
37 + private static final String HOST_TWO_MAC = "00:00:00:00:00:02";
38 + private static final String HOST_ONE_VLAN = "-1";
39 + private static final String HOST_TWO_VLAN = "-1";
40 + private static final String HOST_ONE = HOST_ONE_MAC + "/" + HOST_ONE_VLAN;
41 + private static final String HOST_TWO = HOST_TWO_MAC + "/" + HOST_TWO_VLAN;
42 +
43 + private TrafficSelector selector = new IntentTestsMocks.MockSelector();
44 + private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment();
45 +
46 + private HostId hostOneId = HostId.hostId(HOST_ONE);
47 + private HostId hostTwoId = HostId.hostId(HOST_TWO);
48 + private HostService mockHostService;
49 +
50 + @Before
51 + public void setup() {
52 + Host hostOne = createMock(Host.class);
53 + expect(hostOne.mac()).andReturn(new MacAddress(HOST_ONE_MAC.getBytes())).anyTimes();
54 + expect(hostOne.vlan()).andReturn(VlanId.vlanId()).anyTimes();
55 + replay(hostOne);
56 +
57 + Host hostTwo = createMock(Host.class);
58 + expect(hostTwo.mac()).andReturn(new MacAddress(HOST_TWO_MAC.getBytes())).anyTimes();
59 + expect(hostTwo.vlan()).andReturn(VlanId.vlanId()).anyTimes();
60 + replay(hostTwo);
61 +
62 + mockHostService = createMock(HostService.class);
63 + expect(mockHostService.getHost(eq(hostOneId))).andReturn(hostOne).anyTimes();
64 + expect(mockHostService.getHost(eq(hostTwoId))).andReturn(hostTwo).anyTimes();
65 + replay(mockHostService);
66 + }
67 +
68 + /**
69 + * Creates a HostToHost intent based on two host Ids.
70 + *
71 + * @param oneIdString string for host one id
72 + * @param twoIdString string for host two id
73 + * @return HostToHostIntent for the two hosts
74 + */
75 + private HostToHostIntent makeIntent(String oneIdString, String twoIdString) {
76 + return new HostToHostIntent(new IntentId(12),
77 + hid(oneIdString),
78 + hid(twoIdString),
79 + selector,
80 + treatment);
81 + }
82 +
83 + /**
84 + * Creates a compiler for HostToHost intents.
85 + *
86 + * @param hops string array describing the path hops to use when compiling
87 + * @return HostToHost intent compiler
88 + */
89 + private HostToHostIntentCompiler makeCompiler(String[] hops) {
90 + HostToHostIntentCompiler compiler =
91 + new HostToHostIntentCompiler();
92 + compiler.pathService = new IntentTestsMocks.MockPathService(hops);
93 + compiler.hostService = mockHostService;
94 + IdBlockAllocator idBlockAllocator = new DummyIdBlockAllocator();
95 + compiler.intentIdGenerator =
96 + new IdBlockAllocatorBasedIntentIdGenerator(idBlockAllocator);
97 + return compiler;
98 + }
99 +
100 +
101 + /**
102 + * Tests a pair of hosts with 8 hops between them.
103 + */
104 + @Test
105 + public void testSingleLongPathCompilation() {
106 +
107 + HostToHostIntent intent = makeIntent(HOST_ONE,
108 + HOST_TWO);
109 + assertThat(intent, is(notNullValue()));
110 +
111 + String[] hops = {HOST_ONE, "h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8", HOST_TWO};
112 + HostToHostIntentCompiler compiler = makeCompiler(hops);
113 + assertThat(compiler, is(notNullValue()));
114 +
115 + List<Intent> result = compiler.compile(intent);
116 + assertThat(result, is(Matchers.notNullValue()));
117 + assertThat(result, hasSize(2));
118 + Intent forwardResultIntent = result.get(0);
119 + assertThat(forwardResultIntent instanceof PathIntent, is(true));
120 + Intent reverseResultIntent = result.get(1);
121 + assertThat(reverseResultIntent instanceof PathIntent, is(true));
122 +
123 + if (forwardResultIntent instanceof PathIntent) {
124 + PathIntent forwardPathIntent = (PathIntent) forwardResultIntent;
125 + assertThat(forwardPathIntent.path().links(), hasSize(9));
126 + assertThat(forwardPathIntent.path().links(), linksHasPath(HOST_ONE, "h1"));
127 + assertThat(forwardPathIntent.path().links(), linksHasPath("h1", "h2"));
128 + assertThat(forwardPathIntent.path().links(), linksHasPath("h2", "h3"));
129 + assertThat(forwardPathIntent.path().links(), linksHasPath("h3", "h4"));
130 + assertThat(forwardPathIntent.path().links(), linksHasPath("h4", "h5"));
131 + assertThat(forwardPathIntent.path().links(), linksHasPath("h5", "h6"));
132 + assertThat(forwardPathIntent.path().links(), linksHasPath("h6", "h7"));
133 + assertThat(forwardPathIntent.path().links(), linksHasPath("h7", "h8"));
134 + assertThat(forwardPathIntent.path().links(), linksHasPath("h8", HOST_TWO));
135 + }
136 +
137 + if (reverseResultIntent instanceof PathIntent) {
138 + PathIntent reversePathIntent = (PathIntent) reverseResultIntent;
139 + assertThat(reversePathIntent.path().links(), hasSize(9));
140 + assertThat(reversePathIntent.path().links(), linksHasPath("h1", HOST_ONE));
141 + assertThat(reversePathIntent.path().links(), linksHasPath("h2", "h1"));
142 + assertThat(reversePathIntent.path().links(), linksHasPath("h3", "h2"));
143 + assertThat(reversePathIntent.path().links(), linksHasPath("h4", "h3"));
144 + assertThat(reversePathIntent.path().links(), linksHasPath("h5", "h4"));
145 + assertThat(reversePathIntent.path().links(), linksHasPath("h6", "h5"));
146 + assertThat(reversePathIntent.path().links(), linksHasPath("h7", "h6"));
147 + assertThat(reversePathIntent.path().links(), linksHasPath("h8", "h7"));
148 + assertThat(reversePathIntent.path().links(), linksHasPath(HOST_TWO, "h8"));
149 + }
150 + }
151 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import java.util.HashSet;
4 +import java.util.List;
5 +import java.util.Set;
6 +
7 +import org.hamcrest.Matchers;
8 +import org.junit.Test;
9 +import org.onlab.onos.net.ConnectPoint;
10 +import org.onlab.onos.net.ElementId;
11 +import org.onlab.onos.net.Path;
12 +import org.onlab.onos.net.flow.TrafficSelector;
13 +import org.onlab.onos.net.flow.TrafficTreatment;
14 +import org.onlab.onos.net.intent.Intent;
15 +import org.onlab.onos.net.intent.IntentId;
16 +import org.onlab.onos.net.intent.IntentTestsMocks;
17 +import org.onlab.onos.net.intent.LinkCollectionIntent;
18 +import org.onlab.onos.net.intent.MultiPointToSinglePointIntent;
19 +import org.onlab.onos.net.topology.LinkWeight;
20 +import org.onlab.onos.net.topology.PathService;
21 +
22 +import static org.hamcrest.CoreMatchers.notNullValue;
23 +import static org.hamcrest.MatcherAssert.assertThat;
24 +import static org.hamcrest.Matchers.hasSize;
25 +import static org.hamcrest.Matchers.is;
26 +import static org.onlab.onos.net.NetTestTools.connectPoint;
27 +import static org.onlab.onos.net.NetTestTools.createPath;
28 +import static org.onlab.onos.net.intent.LinksHaveEntryWithSourceDestinationPairMatcher.linksHasPath;
29 +
30 +/**
31 + * Unit tests for the MultiPointToSinglePoint intent compiler.
32 + */
33 +public class TestMultiPointToSinglePointIntentCompiler {
34 +
35 + private TrafficSelector selector = new IntentTestsMocks.MockSelector();
36 + private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment();
37 +
38 + /**
39 + * Mock path service for creating paths within the test.
40 + */
41 + private static class MockPathService implements PathService {
42 +
43 + final String[] pathHops;
44 +
45 + /**
46 + * Constructor that provides a set of hops to mock.
47 + *
48 + * @param pathHops path hops to mock
49 + */
50 + MockPathService(String[] pathHops) {
51 + this.pathHops = pathHops;
52 + }
53 +
54 + @Override
55 + public Set<Path> getPaths(ElementId src, ElementId dst) {
56 + Set<Path> result = new HashSet<>();
57 +
58 + String[] allHops = new String[pathHops.length + 1];
59 + allHops[0] = src.toString();
60 + System.arraycopy(pathHops, 0, allHops, 1, pathHops.length);
61 +
62 + result.add(createPath(allHops));
63 + return result;
64 + }
65 +
66 + @Override
67 + public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeight weight) {
68 + return null;
69 + }
70 + }
71 +
72 + /**
73 + * Creates a MultiPointToSinglePoint intent for a group of ingress points
74 + * and an egress point.
75 + *
76 + * @param ingressIds array of ingress device ids
77 + * @param egressId device id of the egress point
78 + * @return MultiPointToSinglePoint intent
79 + */
80 + private MultiPointToSinglePointIntent makeIntent(String[] ingressIds, String egressId) {
81 + Set<ConnectPoint> ingressPoints = new HashSet<>();
82 + ConnectPoint egressPoint = connectPoint(egressId, 1);
83 +
84 + for (String ingressId : ingressIds) {
85 + ingressPoints.add(connectPoint(ingressId, 1));
86 + }
87 +
88 + return new MultiPointToSinglePointIntent(
89 + new IntentId(12),
90 + selector,
91 + treatment,
92 + ingressPoints,
93 + egressPoint);
94 + }
95 +
96 + /**
97 + * Creates a compiler for MultiPointToSinglePoint intents.
98 + *
99 + * @param hops hops to use while computing paths for this intent
100 + * @return MultiPointToSinglePoint intent
101 + */
102 + private MultiPointToSinglePointIntentCompiler makeCompiler(String[] hops) {
103 + MultiPointToSinglePointIntentCompiler compiler =
104 + new MultiPointToSinglePointIntentCompiler();
105 + compiler.pathService = new MockPathService(hops);
106 + IdBlockAllocator idBlockAllocator = new DummyIdBlockAllocator();
107 + compiler.intentIdGenerator =
108 + new IdBlockAllocatorBasedIntentIdGenerator(idBlockAllocator);
109 + return compiler;
110 + }
111 +
112 + /**
113 + * Tests a single ingress point with 8 hops to its egress point.
114 + */
115 + @Test
116 + public void testSingleLongPathCompilation() {
117 +
118 + String[] ingress = {"ingress"};
119 + String egress = "egress";
120 +
121 + MultiPointToSinglePointIntent intent = makeIntent(ingress, egress);
122 + assertThat(intent, is(notNullValue()));
123 +
124 + String[] hops = {"h1", "h2", "h3", "h4", "h5", "h6", "h7", "h8",
125 + egress};
126 + MultiPointToSinglePointIntentCompiler compiler = makeCompiler(hops);
127 + assertThat(compiler, is(notNullValue()));
128 +
129 + List<Intent> result = compiler.compile(intent);
130 + assertThat(result, is(Matchers.notNullValue()));
131 + assertThat(result, hasSize(1));
132 + Intent resultIntent = result.get(0);
133 + assertThat(resultIntent instanceof LinkCollectionIntent, is(true));
134 +
135 + if (resultIntent instanceof LinkCollectionIntent) {
136 + LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent;
137 + assertThat(linkIntent.links(), hasSize(9));
138 + assertThat(linkIntent.links(), linksHasPath("ingress", "h1"));
139 + assertThat(linkIntent.links(), linksHasPath("h1", "h2"));
140 + assertThat(linkIntent.links(), linksHasPath("h2", "h3"));
141 + assertThat(linkIntent.links(), linksHasPath("h4", "h5"));
142 + assertThat(linkIntent.links(), linksHasPath("h5", "h6"));
143 + assertThat(linkIntent.links(), linksHasPath("h7", "h8"));
144 + assertThat(linkIntent.links(), linksHasPath("h8", "egress"));
145 + }
146 + }
147 +
148 + /**
149 + * Tests a simple topology where two ingress points share some path segments
150 + * and some path segments are not shared.
151 + */
152 + @Test
153 + public void testTwoIngressCompilation() {
154 + String[] ingress = {"ingress1", "ingress2"};
155 + String egress = "egress";
156 +
157 + MultiPointToSinglePointIntent intent = makeIntent(ingress, egress);
158 + assertThat(intent, is(notNullValue()));
159 +
160 + final String[] hops = {"inner1", "inner2", egress};
161 + MultiPointToSinglePointIntentCompiler compiler = makeCompiler(hops);
162 + assertThat(compiler, is(notNullValue()));
163 +
164 + List<Intent> result = compiler.compile(intent);
165 + assertThat(result, is(notNullValue()));
166 + assertThat(result, hasSize(1));
167 + Intent resultIntent = result.get(0);
168 + assertThat(resultIntent instanceof LinkCollectionIntent, is(true));
169 +
170 + if (resultIntent instanceof LinkCollectionIntent) {
171 + LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent;
172 + assertThat(linkIntent.links(), hasSize(4));
173 + assertThat(linkIntent.links(), linksHasPath("ingress1", "inner1"));
174 + assertThat(linkIntent.links(), linksHasPath("ingress2", "inner1"));
175 + assertThat(linkIntent.links(), linksHasPath("inner1", "inner2"));
176 + assertThat(linkIntent.links(), linksHasPath("inner2", "egress"));
177 + }
178 + }
179 +
180 + /**
181 + * Tests a large number of ingress points that share a common path to the
182 + * egress point.
183 + */
184 + @Test
185 + public void testMultiIngressCompilation() {
186 + String[] ingress = {"i1", "i2", "i3", "i4", "i5",
187 + "i6", "i7", "i8", "i9", "i10"};
188 + String egress = "e";
189 +
190 + MultiPointToSinglePointIntent intent = makeIntent(ingress, egress);
191 + assertThat(intent, is(notNullValue()));
192 +
193 + final String[] hops = {"n1", egress};
194 + MultiPointToSinglePointIntentCompiler compiler = makeCompiler(hops);
195 + assertThat(compiler, is(notNullValue()));
196 +
197 + List<Intent> result = compiler.compile(intent);
198 + assertThat(result, is(notNullValue()));
199 + assertThat(result, hasSize(1));
200 + Intent resultIntent = result.get(0);
201 + assertThat(resultIntent instanceof LinkCollectionIntent, is(true));
202 +
203 + if (resultIntent instanceof LinkCollectionIntent) {
204 + LinkCollectionIntent linkIntent = (LinkCollectionIntent) resultIntent;
205 + assertThat(linkIntent.links(), hasSize(ingress.length + 1));
206 + for (String ingressToCheck : ingress) {
207 + assertThat(linkIntent.links(),
208 + linksHasPath(ingressToCheck,
209 + "n1"));
210 + }
211 + assertThat(linkIntent.links(), linksHasPath("n1", egress));
212 + }
213 + }
214 +}
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
20 <module>api</module> 20 <module>api</module>
21 <module>net</module> 21 <module>net</module>
22 <module>store</module> 22 <module>store</module>
23 + <module>json</module>
23 </modules> 24 </modules>
24 25
25 <dependencies> 26 <dependencies>
......
...@@ -19,21 +19,10 @@ ...@@ -19,21 +19,10 @@
19 <dependencies> 19 <dependencies>
20 <dependency> 20 <dependency>
21 <groupId>org.onlab.onos</groupId> 21 <groupId>org.onlab.onos</groupId>
22 - <artifactId>onos-api</artifactId>
23 - </dependency>
24 - <dependency>
25 - <groupId>org.onlab.onos</groupId>
26 <artifactId>onos-core-serializers</artifactId> 22 <artifactId>onos-core-serializers</artifactId>
27 <version>${project.version}</version> 23 <version>${project.version}</version>
28 </dependency> 24 </dependency>
29 25
30 -
31 - <dependency>
32 - <groupId>org.onlab.onos</groupId>
33 - <artifactId>onlab-nio</artifactId>
34 - <version>${project.version}</version>
35 - </dependency>
36 -
37 <dependency> 26 <dependency>
38 <groupId>org.onlab.onos</groupId> 27 <groupId>org.onlab.onos</groupId>
39 <artifactId>onlab-netty</artifactId> 28 <artifactId>onlab-netty</artifactId>
...@@ -50,10 +39,6 @@ ...@@ -50,10 +39,6 @@
50 </dependency> 39 </dependency>
51 40
52 <dependency> 41 <dependency>
53 - <groupId>org.apache.felix</groupId>
54 - <artifactId>org.apache.felix.scr.annotations</artifactId>
55 - </dependency>
56 - <dependency>
57 <groupId>com.google.guava</groupId> 42 <groupId>com.google.guava</groupId>
58 <artifactId>guava-testlib</artifactId> 43 <artifactId>guava-testlib</artifactId>
59 <scope>test</scope> 44 <scope>test</scope>
...@@ -69,13 +54,4 @@ ...@@ -69,13 +54,4 @@
69 </dependency> 54 </dependency>
70 </dependencies> 55 </dependencies>
71 56
72 - <build>
73 - <plugins>
74 - <plugin>
75 - <groupId>org.apache.felix</groupId>
76 - <artifactId>maven-scr-plugin</artifactId>
77 - </plugin>
78 - </plugins>
79 - </build>
80 -
81 </project> 57 </project>
......
...@@ -114,7 +114,7 @@ public class ClusterCommunicationManager ...@@ -114,7 +114,7 @@ public class ClusterCommunicationManager
114 message.subject().value(), SERIALIZER.encode(message)); 114 message.subject().value(), SERIALIZER.encode(message));
115 return true; 115 return true;
116 } catch (IOException e) { 116 } catch (IOException e) {
117 - log.error("Failed to send cluster message to nodeId: " + toNodeId, e); 117 + log.trace("Failed to send cluster message to nodeId: " + toNodeId, e);
118 throw e; 118 throw e;
119 } 119 }
120 } 120 }
......
1 -/**
2 - * Common abstractions and facilities for implementing distributed store
3 - * using gossip protocol.
4 - */
5 -package org.onlab.onos.store.common.impl;
...@@ -15,7 +15,7 @@ import org.onlab.onos.net.device.DefaultPortDescription; ...@@ -15,7 +15,7 @@ import org.onlab.onos.net.device.DefaultPortDescription;
15 import org.onlab.onos.net.device.DeviceDescription; 15 import org.onlab.onos.net.device.DeviceDescription;
16 import org.onlab.onos.net.device.PortDescription; 16 import org.onlab.onos.net.device.PortDescription;
17 import org.onlab.onos.store.Timestamp; 17 import org.onlab.onos.store.Timestamp;
18 -import org.onlab.onos.store.common.impl.Timestamped; 18 +import org.onlab.onos.store.impl.Timestamped;
19 19
20 /* 20 /*
21 * Collection of Description of a Device and Ports, given from a Provider. 21 * Collection of Description of a Device and Ports, given from a Provider.
......
...@@ -38,7 +38,7 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; ...@@ -38,7 +38,7 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
38 import org.onlab.onos.store.cluster.messaging.ClusterMessage; 38 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
39 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler; 39 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
40 import org.onlab.onos.store.cluster.messaging.MessageSubject; 40 import org.onlab.onos.store.cluster.messaging.MessageSubject;
41 -import org.onlab.onos.store.common.impl.Timestamped; 41 +import org.onlab.onos.store.impl.Timestamped;
42 import org.onlab.onos.store.serializers.KryoSerializer; 42 import org.onlab.onos.store.serializers.KryoSerializer;
43 import org.onlab.onos.store.serializers.DistributedStoreSerializers; 43 import org.onlab.onos.store.serializers.DistributedStoreSerializers;
44 import org.onlab.packet.ChassisId; 44 import org.onlab.packet.ChassisId;
...@@ -516,12 +516,12 @@ public class GossipDeviceStore ...@@ -516,12 +516,12 @@ public class GossipDeviceStore
516 Map<PortNumber, Port> ports, 516 Map<PortNumber, Port> ports,
517 Set<PortNumber> processed) { 517 Set<PortNumber> processed) {
518 List<DeviceEvent> events = new ArrayList<>(); 518 List<DeviceEvent> events = new ArrayList<>();
519 - Iterator<PortNumber> iterator = ports.keySet().iterator(); 519 + Iterator<Entry<PortNumber, Port>> iterator = ports.entrySet().iterator();
520 while (iterator.hasNext()) { 520 while (iterator.hasNext()) {
521 - PortNumber portNumber = iterator.next(); 521 + Entry<PortNumber, Port> e = iterator.next();
522 + PortNumber portNumber = e.getKey();
522 if (!processed.contains(portNumber)) { 523 if (!processed.contains(portNumber)) {
523 - events.add(new DeviceEvent(PORT_REMOVED, device, 524 + events.add(new DeviceEvent(PORT_REMOVED, device, e.getValue()));
524 - ports.get(portNumber)));
525 iterator.remove(); 525 iterator.remove();
526 } 526 }
527 } 527 }
...@@ -1139,7 +1139,7 @@ public class GossipDeviceStore ...@@ -1139,7 +1139,7 @@ public class GossipDeviceStore
1139 try { 1139 try {
1140 unicastMessage(peer, DEVICE_ADVERTISE, ad); 1140 unicastMessage(peer, DEVICE_ADVERTISE, ad);
1141 } catch (IOException e) { 1141 } catch (IOException e) {
1142 - log.error("Failed to send anti-entropy advertisement", e); 1142 + log.debug("Failed to send anti-entropy advertisement to {}", peer);
1143 return; 1143 return;
1144 } 1144 }
1145 } catch (Exception e) { 1145 } catch (Exception e) {
......
1 -package org.onlab.onos.store.device.impl;
2 -
3 -import static com.google.common.base.Preconditions.checkNotNull;
4 -
5 -import org.apache.commons.lang3.concurrent.ConcurrentException;
6 -import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
7 -import org.onlab.onos.net.device.DeviceDescription;
8 -import org.onlab.onos.store.common.impl.Timestamped;
9 -
10 -// FIXME: consider removing this class
11 -public final class InitDeviceDescs
12 - implements ConcurrentInitializer<DeviceDescriptions> {
13 -
14 - private final Timestamped<DeviceDescription> deviceDesc;
15 -
16 - public InitDeviceDescs(Timestamped<DeviceDescription> deviceDesc) {
17 - this.deviceDesc = checkNotNull(deviceDesc);
18 - }
19 - @Override
20 - public DeviceDescriptions get() throws ConcurrentException {
21 - return new DeviceDescriptions(deviceDesc);
22 - }
23 -}
...@@ -3,7 +3,7 @@ package org.onlab.onos.store.device.impl; ...@@ -3,7 +3,7 @@ package org.onlab.onos.store.device.impl;
3 import org.onlab.onos.net.DeviceId; 3 import org.onlab.onos.net.DeviceId;
4 import org.onlab.onos.net.device.DeviceDescription; 4 import org.onlab.onos.net.device.DeviceDescription;
5 import org.onlab.onos.net.provider.ProviderId; 5 import org.onlab.onos.net.provider.ProviderId;
6 -import org.onlab.onos.store.common.impl.Timestamped; 6 +import org.onlab.onos.store.impl.Timestamped;
7 7
8 import com.google.common.base.MoreObjects; 8 import com.google.common.base.MoreObjects;
9 9
......
...@@ -3,7 +3,7 @@ package org.onlab.onos.store.device.impl; ...@@ -3,7 +3,7 @@ package org.onlab.onos.store.device.impl;
3 import org.onlab.onos.net.DeviceId; 3 import org.onlab.onos.net.DeviceId;
4 import org.onlab.onos.net.device.DeviceDescription; 4 import org.onlab.onos.net.device.DeviceDescription;
5 import org.onlab.onos.net.provider.ProviderId; 5 import org.onlab.onos.net.provider.ProviderId;
6 -import org.onlab.onos.store.common.impl.Timestamped; 6 +import org.onlab.onos.store.impl.Timestamped;
7 7
8 import com.esotericsoftware.kryo.Kryo; 8 import com.esotericsoftware.kryo.Kryo;
9 import com.esotericsoftware.kryo.Serializer; 9 import com.esotericsoftware.kryo.Serializer;
......
...@@ -5,7 +5,7 @@ import java.util.List; ...@@ -5,7 +5,7 @@ import java.util.List;
5 import org.onlab.onos.net.DeviceId; 5 import org.onlab.onos.net.DeviceId;
6 import org.onlab.onos.net.device.PortDescription; 6 import org.onlab.onos.net.device.PortDescription;
7 import org.onlab.onos.net.provider.ProviderId; 7 import org.onlab.onos.net.provider.ProviderId;
8 -import org.onlab.onos.store.common.impl.Timestamped; 8 +import org.onlab.onos.store.impl.Timestamped;
9 9
10 import com.google.common.base.MoreObjects; 10 import com.google.common.base.MoreObjects;
11 11
......
...@@ -5,7 +5,7 @@ import java.util.List; ...@@ -5,7 +5,7 @@ import java.util.List;
5 import org.onlab.onos.net.DeviceId; 5 import org.onlab.onos.net.DeviceId;
6 import org.onlab.onos.net.device.PortDescription; 6 import org.onlab.onos.net.device.PortDescription;
7 import org.onlab.onos.net.provider.ProviderId; 7 import org.onlab.onos.net.provider.ProviderId;
8 -import org.onlab.onos.store.common.impl.Timestamped; 8 +import org.onlab.onos.store.impl.Timestamped;
9 9
10 import com.esotericsoftware.kryo.Kryo; 10 import com.esotericsoftware.kryo.Kryo;
11 import com.esotericsoftware.kryo.Serializer; 11 import com.esotericsoftware.kryo.Serializer;
......
...@@ -3,7 +3,7 @@ package org.onlab.onos.store.device.impl; ...@@ -3,7 +3,7 @@ package org.onlab.onos.store.device.impl;
3 import org.onlab.onos.net.DeviceId; 3 import org.onlab.onos.net.DeviceId;
4 import org.onlab.onos.net.device.PortDescription; 4 import org.onlab.onos.net.device.PortDescription;
5 import org.onlab.onos.net.provider.ProviderId; 5 import org.onlab.onos.net.provider.ProviderId;
6 -import org.onlab.onos.store.common.impl.Timestamped; 6 +import org.onlab.onos.store.impl.Timestamped;
7 7
8 import com.google.common.base.MoreObjects; 8 import com.google.common.base.MoreObjects;
9 9
......
...@@ -3,7 +3,7 @@ package org.onlab.onos.store.device.impl; ...@@ -3,7 +3,7 @@ package org.onlab.onos.store.device.impl;
3 import org.onlab.onos.net.DeviceId; 3 import org.onlab.onos.net.DeviceId;
4 import org.onlab.onos.net.device.PortDescription; 4 import org.onlab.onos.net.device.PortDescription;
5 import org.onlab.onos.net.provider.ProviderId; 5 import org.onlab.onos.net.provider.ProviderId;
6 -import org.onlab.onos.store.common.impl.Timestamped; 6 +import org.onlab.onos.store.impl.Timestamped;
7 7
8 import com.esotericsoftware.kryo.Kryo; 8 import com.esotericsoftware.kryo.Kryo;
9 import com.esotericsoftware.kryo.Serializer; 9 import com.esotericsoftware.kryo.Serializer;
......
1 /** 1 /**
2 - * Implementation of device store using distributed distributed p2p synchronization protocol. 2 + * Implementation of distributed device store using p2p synchronization protocol.
3 */ 3 */
4 package org.onlab.onos.store.device.impl; 4 package org.onlab.onos.store.device.impl;
......
1 +package org.onlab.onos.store.flow;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.util.Collection;
6 +import java.util.Collections;
7 +
8 +import org.onlab.onos.cluster.NodeId;
9 +
10 +import com.google.common.base.Optional;
11 +
12 +/**
13 + * Class to represent placement information about Master/Backup copy.
14 + */
15 +public final class ReplicaInfo {
16 +
17 + private final Optional<NodeId> master;
18 + private final Collection<NodeId> backups;
19 +
20 + /**
21 + * Creates a ReplicaInfo instance.
22 + *
23 + * @param master NodeId of the node where the master copy should be
24 + * @param backups collection of NodeId, where backup copies should be placed
25 + */
26 + public ReplicaInfo(NodeId master, Collection<NodeId> backups) {
27 + this.master = Optional.fromNullable(master);
28 + this.backups = checkNotNull(backups);
29 + }
30 +
31 + /**
32 + * Returns the NodeId, if there is a Node where the master copy should be.
33 + *
34 + * @return NodeId, where the master copy should be placed
35 + */
36 + public Optional<NodeId> master() {
37 + return master;
38 + }
39 +
40 + /**
41 + * Returns the collection of NodeId, where backup copies should be placed.
42 + *
43 + * @return collection of NodeId, where backup copies should be placed
44 + */
45 + public Collection<NodeId> backups() {
46 + return backups;
47 + }
48 +
49 + // for Serializer
50 + private ReplicaInfo() {
51 + this.master = Optional.absent();
52 + this.backups = Collections.emptyList();
53 + }
54 +}
1 +package org.onlab.onos.store.flow;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import org.onlab.onos.event.AbstractEvent;
6 +import org.onlab.onos.net.DeviceId;
7 +
8 +/**
9 + * Describes a device replicainfo event.
10 + */
11 +public class ReplicaInfoEvent extends AbstractEvent<ReplicaInfoEvent.Type, DeviceId> {
12 +
13 + private final ReplicaInfo replicaInfo;
14 +
15 + /**
16 + * Types of Replica info event.
17 + */
18 + public enum Type {
19 + /**
20 + * Event to notify that master placement should be changed.
21 + */
22 + MASTER_CHANGED,
23 + //
24 + // BACKUPS_CHANGED?
25 + }
26 +
27 +
28 + /**
29 + * Creates an event of a given type and for the specified device,
30 + * and replica info.
31 + *
32 + * @param type replicainfo event type
33 + * @param device event device subject
34 + * @param replicaInfo replicainfo
35 + */
36 + public ReplicaInfoEvent(Type type, DeviceId device, ReplicaInfo replicaInfo) {
37 + super(type, device);
38 + this.replicaInfo = checkNotNull(replicaInfo);
39 + }
40 +
41 + /**
42 + * Returns the current replica information for the subject.
43 + *
44 + * @return replica information for the subject
45 + */
46 + public ReplicaInfo replicaInfo() {
47 + return replicaInfo;
48 + };
49 +}
1 +package org.onlab.onos.store.flow;
2 +
3 +import org.onlab.onos.event.EventListener;
4 +
5 +/**
6 + * Entity capable of receiving Replica placement information-related events.
7 + */
8 +public interface ReplicaInfoEventListener extends EventListener<ReplicaInfoEvent> {
9 +
10 +}
11 +
1 +package org.onlab.onos.store.flow;
2 +
3 +import org.onlab.onos.net.DeviceId;
4 +
5 +/**
6 + * Service to return where the Replica should be placed.
7 + */
8 +public interface ReplicaInfoService {
9 +
10 + // returns where it should be.
11 + /**
12 + * Returns the placement information for given Device.
13 + *
14 + * @param deviceId identifier of the device
15 + * @return placement information
16 + */
17 + ReplicaInfo getReplicaInfoFor(DeviceId deviceId);
18 +}
1 +package org.onlab.onos.store.flow.impl;
2 +
3 +import static org.slf4j.LoggerFactory.getLogger;
4 +import static org.onlab.onos.store.flow.ReplicaInfoEvent.Type.MASTER_CHANGED;
5 +
6 +import java.util.Collections;
7 +import java.util.List;
8 +
9 +import org.apache.felix.scr.annotations.Activate;
10 +import org.apache.felix.scr.annotations.Component;
11 +import org.apache.felix.scr.annotations.Deactivate;
12 +import org.apache.felix.scr.annotations.Reference;
13 +import org.apache.felix.scr.annotations.ReferenceCardinality;
14 +import org.apache.felix.scr.annotations.Service;
15 +import org.onlab.onos.cluster.NodeId;
16 +import org.onlab.onos.event.AbstractListenerRegistry;
17 +import org.onlab.onos.event.EventDeliveryService;
18 +import org.onlab.onos.mastership.MastershipEvent;
19 +import org.onlab.onos.mastership.MastershipListener;
20 +import org.onlab.onos.mastership.MastershipService;
21 +import org.onlab.onos.net.DeviceId;
22 +import org.onlab.onos.store.flow.ReplicaInfo;
23 +import org.onlab.onos.store.flow.ReplicaInfoEvent;
24 +import org.onlab.onos.store.flow.ReplicaInfoEventListener;
25 +import org.onlab.onos.store.flow.ReplicaInfoService;
26 +import org.slf4j.Logger;
27 +
28 +/**
29 + * Manages replica placement information.
30 + */
31 +@Component(immediate = true)
32 +@Service
33 +public class ReplicaInfoManager implements ReplicaInfoService {
34 +
35 + private final Logger log = getLogger(getClass());
36 +
37 + private final MastershipListener mastershipListener = new InternalMastershipListener();
38 +
39 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
40 + protected EventDeliveryService eventDispatcher;
41 +
42 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
43 + protected MastershipService mastershipService;
44 +
45 + protected final AbstractListenerRegistry<ReplicaInfoEvent, ReplicaInfoEventListener>
46 + listenerRegistry = new AbstractListenerRegistry<>();
47 +
48 + @Activate
49 + public void activate() {
50 + eventDispatcher.addSink(ReplicaInfoEvent.class, listenerRegistry);
51 + mastershipService.addListener(mastershipListener);
52 + log.info("Started");
53 + }
54 +
55 + @Deactivate
56 + public void deactivate() {
57 + eventDispatcher.removeSink(ReplicaInfoEvent.class);
58 + mastershipService.removeListener(mastershipListener);
59 + log.info("Stopped");
60 + }
61 +
62 + @Override
63 + public ReplicaInfo getReplicaInfoFor(DeviceId deviceId) {
64 + // TODO: populate backup List when we reach the point we need them.
65 + return new ReplicaInfo(mastershipService.getMasterFor(deviceId),
66 + Collections.<NodeId>emptyList());
67 + }
68 +
69 + final class InternalMastershipListener implements MastershipListener {
70 +
71 + @Override
72 + public void event(MastershipEvent event) {
73 + // TODO: distinguish stby list update, when MastershipService,
74 + // start publishing them
75 + final List<NodeId> standbyList = Collections.<NodeId>emptyList();
76 + eventDispatcher.post(new ReplicaInfoEvent(MASTER_CHANGED,
77 + event.subject(),
78 + new ReplicaInfo(event.master(), standbyList)));
79 + }
80 + }
81 +
82 +}
1 +/**
2 + * Implementation of the distributed flow rule store using p2p synchronization
3 + * protocol.
4 + */
5 +package org.onlab.onos.store.flow.impl;
1 package org.onlab.onos.store.host.impl; 1 package org.onlab.onos.store.host.impl;
2 2
3 +import com.google.common.collect.FluentIterable;
3 import com.google.common.collect.HashMultimap; 4 import com.google.common.collect.HashMultimap;
5 +import com.google.common.collect.ImmutableList;
4 import com.google.common.collect.ImmutableSet; 6 import com.google.common.collect.ImmutableSet;
5 import com.google.common.collect.Multimap; 7 import com.google.common.collect.Multimap;
6 import com.google.common.collect.Sets; 8 import com.google.common.collect.Sets;
7 9
10 +import org.apache.commons.lang3.RandomUtils;
8 import org.apache.felix.scr.annotations.Activate; 11 import org.apache.felix.scr.annotations.Activate;
9 import org.apache.felix.scr.annotations.Component; 12 import org.apache.felix.scr.annotations.Component;
10 import org.apache.felix.scr.annotations.Deactivate; 13 import org.apache.felix.scr.annotations.Deactivate;
...@@ -12,6 +15,8 @@ import org.apache.felix.scr.annotations.Reference; ...@@ -12,6 +15,8 @@ import org.apache.felix.scr.annotations.Reference;
12 import org.apache.felix.scr.annotations.ReferenceCardinality; 15 import org.apache.felix.scr.annotations.ReferenceCardinality;
13 import org.apache.felix.scr.annotations.Service; 16 import org.apache.felix.scr.annotations.Service;
14 import org.onlab.onos.cluster.ClusterService; 17 import org.onlab.onos.cluster.ClusterService;
18 +import org.onlab.onos.cluster.ControllerNode;
19 +import org.onlab.onos.cluster.NodeId;
15 import org.onlab.onos.net.Annotations; 20 import org.onlab.onos.net.Annotations;
16 import org.onlab.onos.net.ConnectPoint; 21 import org.onlab.onos.net.ConnectPoint;
17 import org.onlab.onos.net.DefaultHost; 22 import org.onlab.onos.net.DefaultHost;
...@@ -19,6 +24,7 @@ import org.onlab.onos.net.DeviceId; ...@@ -19,6 +24,7 @@ import org.onlab.onos.net.DeviceId;
19 import org.onlab.onos.net.Host; 24 import org.onlab.onos.net.Host;
20 import org.onlab.onos.net.HostId; 25 import org.onlab.onos.net.HostId;
21 import org.onlab.onos.net.HostLocation; 26 import org.onlab.onos.net.HostLocation;
27 +import org.onlab.onos.net.host.DefaultHostDescription;
22 import org.onlab.onos.net.host.HostClockService; 28 import org.onlab.onos.net.host.HostClockService;
23 import org.onlab.onos.net.host.HostDescription; 29 import org.onlab.onos.net.host.HostDescription;
24 import org.onlab.onos.net.host.HostEvent; 30 import org.onlab.onos.net.host.HostEvent;
...@@ -32,7 +38,7 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; ...@@ -32,7 +38,7 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
32 import org.onlab.onos.store.cluster.messaging.ClusterMessage; 38 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
33 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler; 39 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
34 import org.onlab.onos.store.cluster.messaging.MessageSubject; 40 import org.onlab.onos.store.cluster.messaging.MessageSubject;
35 -import org.onlab.onos.store.common.impl.Timestamped; 41 +import org.onlab.onos.store.impl.Timestamped;
36 import org.onlab.onos.store.serializers.DistributedStoreSerializers; 42 import org.onlab.onos.store.serializers.DistributedStoreSerializers;
37 import org.onlab.onos.store.serializers.KryoSerializer; 43 import org.onlab.onos.store.serializers.KryoSerializer;
38 import org.onlab.packet.IpPrefix; 44 import org.onlab.packet.IpPrefix;
...@@ -42,12 +48,19 @@ import org.onlab.util.KryoPool; ...@@ -42,12 +48,19 @@ import org.onlab.util.KryoPool;
42 import org.slf4j.Logger; 48 import org.slf4j.Logger;
43 49
44 import java.io.IOException; 50 import java.io.IOException;
51 +import java.util.HashMap;
45 import java.util.HashSet; 52 import java.util.HashSet;
46 import java.util.Map; 53 import java.util.Map;
47 import java.util.Set; 54 import java.util.Set;
55 +import java.util.Map.Entry;
48 import java.util.concurrent.ConcurrentHashMap; 56 import java.util.concurrent.ConcurrentHashMap;
57 +import java.util.concurrent.ScheduledExecutorService;
58 +import java.util.concurrent.TimeUnit;
49 59
60 +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
61 +import static org.onlab.onos.cluster.ControllerNodeToNodeId.toNodeId;
50 import static org.onlab.onos.net.host.HostEvent.Type.*; 62 import static org.onlab.onos.net.host.HostEvent.Type.*;
63 +import static org.onlab.util.Tools.namedThreads;
51 import static org.slf4j.LoggerFactory.getLogger; 64 import static org.slf4j.LoggerFactory.getLogger;
52 65
53 //TODO: multi-provider, annotation not supported. 66 //TODO: multi-provider, annotation not supported.
...@@ -88,24 +101,58 @@ public class GossipHostStore ...@@ -88,24 +101,58 @@ public class GossipHostStore
88 protected void setupKryoPool() { 101 protected void setupKryoPool() {
89 serializerPool = KryoPool.newBuilder() 102 serializerPool = KryoPool.newBuilder()
90 .register(DistributedStoreSerializers.COMMON) 103 .register(DistributedStoreSerializers.COMMON)
104 + .register(InternalHostEvent.class)
91 .register(InternalHostRemovedEvent.class) 105 .register(InternalHostRemovedEvent.class)
106 + .register(HostFragmentId.class)
107 + .register(HostAntiEntropyAdvertisement.class)
92 .build() 108 .build()
93 .populate(1); 109 .populate(1);
94 } 110 }
95 }; 111 };
96 112
113 + private ScheduledExecutorService executor;
114 +
97 @Activate 115 @Activate
98 public void activate() { 116 public void activate() {
99 clusterCommunicator.addSubscriber( 117 clusterCommunicator.addSubscriber(
100 - GossipHostStoreMessageSubjects.HOST_UPDATED, new InternalHostEventListener()); 118 + GossipHostStoreMessageSubjects.HOST_UPDATED,
119 + new InternalHostEventListener());
120 + clusterCommunicator.addSubscriber(
121 + GossipHostStoreMessageSubjects.HOST_REMOVED,
122 + new InternalHostRemovedEventListener());
101 clusterCommunicator.addSubscriber( 123 clusterCommunicator.addSubscriber(
102 - GossipHostStoreMessageSubjects.HOST_REMOVED, new InternalHostRemovedEventListener()); 124 + GossipHostStoreMessageSubjects.HOST_ANTI_ENTROPY_ADVERTISEMENT,
125 + new InternalHostAntiEntropyAdvertisementListener());
126 +
127 + executor =
128 + newSingleThreadScheduledExecutor(namedThreads("link-anti-entropy-%d"));
129 +
130 + // TODO: Make these configurable
131 + long initialDelaySec = 5;
132 + long periodSec = 5;
133 + // start anti-entropy thread
134 + executor.scheduleAtFixedRate(new SendAdvertisementTask(),
135 + initialDelaySec, periodSec, TimeUnit.SECONDS);
103 136
104 log.info("Started"); 137 log.info("Started");
105 } 138 }
106 139
107 @Deactivate 140 @Deactivate
108 public void deactivate() { 141 public void deactivate() {
142 + executor.shutdownNow();
143 + try {
144 + if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
145 + log.error("Timeout during executor shutdown");
146 + }
147 + } catch (InterruptedException e) {
148 + log.error("Error during executor shutdown", e);
149 + }
150 +
151 + hosts.clear();
152 + removedHosts.clear();
153 + locations.clear();
154 + portAddresses.clear();
155 +
109 log.info("Stopped"); 156 log.info("Stopped");
110 } 157 }
111 158
...@@ -153,7 +200,7 @@ public class GossipHostStore ...@@ -153,7 +200,7 @@ public class GossipHostStore
153 descr.hwAddress(), 200 descr.hwAddress(),
154 descr.vlan(), 201 descr.vlan(),
155 new Timestamped<>(descr.location(), timestamp), 202 new Timestamped<>(descr.location(), timestamp),
156 - ImmutableSet.of(descr.ipAddress())); 203 + ImmutableSet.copyOf(descr.ipAddress()));
157 hosts.put(hostId, newhost); 204 hosts.put(hostId, newhost);
158 locations.put(descr.location(), newhost); 205 locations.put(descr.location(), newhost);
159 return new HostEvent(HOST_ADDED, newhost); 206 return new HostEvent(HOST_ADDED, newhost);
...@@ -169,12 +216,12 @@ public class GossipHostStore ...@@ -169,12 +216,12 @@ public class GossipHostStore
169 return new HostEvent(HOST_MOVED, host); 216 return new HostEvent(HOST_MOVED, host);
170 } 217 }
171 218
172 - if (host.ipAddresses().contains(descr.ipAddress())) { 219 + if (host.ipAddresses().containsAll(descr.ipAddress())) {
173 return null; 220 return null;
174 } 221 }
175 222
176 Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses()); 223 Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses());
177 - addresses.add(descr.ipAddress()); 224 + addresses.addAll(descr.ipAddress());
178 StoredHost updated = new StoredHost(providerId, host.id(), 225 StoredHost updated = new StoredHost(providerId, host.id(),
179 host.mac(), host.vlan(), 226 host.mac(), host.vlan(),
180 host.location, addresses); 227 host.location, addresses);
...@@ -381,6 +428,10 @@ public class GossipHostStore ...@@ -381,6 +428,10 @@ public class GossipHostStore
381 public HostLocation location() { 428 public HostLocation location() {
382 return location.value(); 429 return location.value();
383 } 430 }
431 +
432 + public Timestamp timestamp() {
433 + return location.timestamp();
434 + }
384 } 435 }
385 436
386 private void notifyPeers(InternalHostRemovedEvent event) throws IOException { 437 private void notifyPeers(InternalHostRemovedEvent event) throws IOException {
...@@ -399,6 +450,16 @@ public class GossipHostStore ...@@ -399,6 +450,16 @@ public class GossipHostStore
399 clusterCommunicator.broadcast(message); 450 clusterCommunicator.broadcast(message);
400 } 451 }
401 452
453 + private void unicastMessage(NodeId peer,
454 + MessageSubject subject,
455 + Object event) throws IOException {
456 + ClusterMessage message = new ClusterMessage(
457 + clusterService.getLocalNode().id(),
458 + subject,
459 + SERIALIZER.encode(event));
460 + clusterCommunicator.unicast(message, peer);
461 + }
462 +
402 private void notifyDelegateIfNotNull(HostEvent event) { 463 private void notifyDelegateIfNotNull(HostEvent event) {
403 if (event != null) { 464 if (event != null) {
404 notifyDelegate(event); 465 notifyDelegate(event);
...@@ -434,4 +495,165 @@ public class GossipHostStore ...@@ -434,4 +495,165 @@ public class GossipHostStore
434 notifyDelegateIfNotNull(removeHostInternal(hostId, timestamp)); 495 notifyDelegateIfNotNull(removeHostInternal(hostId, timestamp));
435 } 496 }
436 } 497 }
498 +
499 + private final class SendAdvertisementTask implements Runnable {
500 +
501 + @Override
502 + public void run() {
503 + if (Thread.currentThread().isInterrupted()) {
504 + log.info("Interrupted, quitting");
505 + return;
506 + }
507 +
508 + try {
509 + final NodeId self = clusterService.getLocalNode().id();
510 + Set<ControllerNode> nodes = clusterService.getNodes();
511 +
512 + ImmutableList<NodeId> nodeIds = FluentIterable.from(nodes)
513 + .transform(toNodeId())
514 + .toList();
515 +
516 + if (nodeIds.size() == 1 && nodeIds.get(0).equals(self)) {
517 + log.debug("No other peers in the cluster.");
518 + return;
519 + }
520 +
521 + NodeId peer;
522 + do {
523 + int idx = RandomUtils.nextInt(0, nodeIds.size());
524 + peer = nodeIds.get(idx);
525 + } while (peer.equals(self));
526 +
527 + HostAntiEntropyAdvertisement ad = createAdvertisement();
528 +
529 + if (Thread.currentThread().isInterrupted()) {
530 + log.info("Interrupted, quitting");
531 + return;
532 + }
533 +
534 + try {
535 + unicastMessage(peer, GossipHostStoreMessageSubjects.HOST_ANTI_ENTROPY_ADVERTISEMENT, ad);
536 + } catch (IOException e) {
537 + log.debug("Failed to send anti-entropy advertisement to {}", peer);
538 + return;
539 + }
540 + } catch (Exception e) {
541 + // catch all Exception to avoid Scheduled task being suppressed.
542 + log.error("Exception thrown while sending advertisement", e);
543 + }
544 + }
545 + }
546 +
547 + private HostAntiEntropyAdvertisement createAdvertisement() {
548 + final NodeId self = clusterService.getLocalNode().id();
549 +
550 + Map<HostFragmentId, Timestamp> timestamps = new HashMap<>(hosts.size());
551 + Map<HostId, Timestamp> tombstones = new HashMap<>(removedHosts.size());
552 +
553 + for (Entry<HostId, StoredHost> e : hosts.entrySet()) {
554 +
555 + final HostId hostId = e.getKey();
556 + final StoredHost hostInfo = e.getValue();
557 + final ProviderId providerId = hostInfo.providerId();
558 + timestamps.put(new HostFragmentId(hostId, providerId), hostInfo.timestamp());
559 + }
560 +
561 + for (Entry<HostId, Timestamped<Host>> e : removedHosts.entrySet()) {
562 + tombstones.put(e.getKey(), e.getValue().timestamp());
563 + }
564 +
565 + return new HostAntiEntropyAdvertisement(self, timestamps, tombstones);
566 + }
567 +
568 + private synchronized void handleAntiEntropyAdvertisement(HostAntiEntropyAdvertisement ad) {
569 +
570 + final NodeId sender = ad.sender();
571 +
572 + for (Entry<HostId, StoredHost> host : hosts.entrySet()) {
573 + // for each locally live Hosts...
574 + final HostId hostId = host.getKey();
575 + final StoredHost localHost = host.getValue();
576 + final ProviderId providerId = localHost.providerId();
577 + final HostFragmentId hostFragId = new HostFragmentId(hostId, providerId);
578 + final Timestamp localLiveTimestamp = localHost.timestamp();
579 +
580 + Timestamp remoteTimestamp = ad.timestamps().get(hostFragId);
581 + if (remoteTimestamp == null) {
582 + remoteTimestamp = ad.tombstones().get(hostId);
583 + }
584 + if (remoteTimestamp == null ||
585 + localLiveTimestamp.compareTo(remoteTimestamp) > 0) {
586 +
587 + // local is more recent, push
588 + // TODO: annotation is lost
589 + final HostDescription desc = new DefaultHostDescription(
590 + localHost.mac(),
591 + localHost.vlan(),
592 + localHost.location(),
593 + localHost.ipAddresses());
594 + try {
595 + unicastMessage(sender, GossipHostStoreMessageSubjects.HOST_UPDATED,
596 + new InternalHostEvent(providerId, hostId, desc, localHost.timestamp()));
597 + } catch (IOException e1) {
598 + log.debug("Failed to send advertisement response", e1);
599 + }
600 + }
601 +
602 + final Timestamp remoteDeadTimestamp = ad.tombstones().get(hostId);
603 + if (remoteDeadTimestamp != null &&
604 + remoteDeadTimestamp.compareTo(localLiveTimestamp) > 0) {
605 + // sender has recent remove
606 + notifyDelegateIfNotNull(removeHostInternal(hostId, remoteDeadTimestamp));
607 + }
608 + }
609 +
610 + for (Entry<HostId, Timestamped<Host>> dead : removedHosts.entrySet()) {
611 + // for each locally dead Hosts
612 + final HostId hostId = dead.getKey();
613 + final Timestamp localDeadTimestamp = dead.getValue().timestamp();
614 +
615 + // TODO: pick proper ProviderId, when supporting multi-provider
616 + final ProviderId providerId = dead.getValue().value().providerId();
617 + final HostFragmentId hostFragId = new HostFragmentId(hostId, providerId);
618 +
619 + final Timestamp remoteLiveTimestamp = ad.timestamps().get(hostFragId);
620 + if (remoteLiveTimestamp != null &&
621 + localDeadTimestamp.compareTo(remoteLiveTimestamp) > 0) {
622 + // sender has zombie, push
623 + try {
624 + unicastMessage(sender, GossipHostStoreMessageSubjects.HOST_REMOVED,
625 + new InternalHostRemovedEvent(hostId, localDeadTimestamp));
626 + } catch (IOException e1) {
627 + log.debug("Failed to send advertisement response", e1);
628 + }
629 + }
630 + }
631 +
632 +
633 + for (Entry<HostId, Timestamp> e : ad.tombstones().entrySet()) {
634 + // for each remote tombstone advertisement...
635 + final HostId hostId = e.getKey();
636 + final Timestamp adRemoveTimestamp = e.getValue();
637 +
638 + final StoredHost storedHost = hosts.get(hostId);
639 + if (storedHost == null) {
640 + continue;
641 + }
642 + if (adRemoveTimestamp.compareTo(storedHost.timestamp()) > 0) {
643 + // sender has recent remove info, locally remove
644 + notifyDelegateIfNotNull(removeHostInternal(hostId, adRemoveTimestamp));
645 + }
646 + }
647 + }
648 +
649 + private final class InternalHostAntiEntropyAdvertisementListener implements
650 + ClusterMessageHandler {
651 +
652 + @Override
653 + public void handle(ClusterMessage message) {
654 + log.debug("Received Host Anti-Entropy advertisement from peer: {}", message.sender());
655 + HostAntiEntropyAdvertisement advertisement = SERIALIZER.decode(message.payload());
656 + handleAntiEntropyAdvertisement(advertisement);
657 + }
658 + }
437 } 659 }
......
...@@ -4,6 +4,11 @@ import org.onlab.onos.store.cluster.messaging.MessageSubject; ...@@ -4,6 +4,11 @@ import org.onlab.onos.store.cluster.messaging.MessageSubject;
4 4
5 public final class GossipHostStoreMessageSubjects { 5 public final class GossipHostStoreMessageSubjects {
6 private GossipHostStoreMessageSubjects() {} 6 private GossipHostStoreMessageSubjects() {}
7 - public static final MessageSubject HOST_UPDATED = new MessageSubject("peer-host-updated"); 7 +
8 - public static final MessageSubject HOST_REMOVED = new MessageSubject("peer-host-removed"); 8 + public static final MessageSubject HOST_UPDATED
9 + = new MessageSubject("peer-host-updated");
10 + public static final MessageSubject HOST_REMOVED
11 + = new MessageSubject("peer-host-removed");
12 + public static final MessageSubject HOST_ANTI_ENTROPY_ADVERTISEMENT
13 + = new MessageSubject("host-enti-entropy-advertisement");;
9 } 14 }
......
1 +package org.onlab.onos.store.host.impl;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.util.Map;
6 +
7 +import org.onlab.onos.cluster.NodeId;
8 +import org.onlab.onos.net.HostId;
9 +import org.onlab.onos.store.Timestamp;
10 +
11 +/**
12 + * Host AE Advertisement message.
13 + */
14 +public final class HostAntiEntropyAdvertisement {
15 +
16 + private final NodeId sender;
17 + private final Map<HostFragmentId, Timestamp> timestamps;
18 + private final Map<HostId, Timestamp> tombstones;
19 +
20 +
21 + public HostAntiEntropyAdvertisement(NodeId sender,
22 + Map<HostFragmentId, Timestamp> timestamps,
23 + Map<HostId, Timestamp> tombstones) {
24 + this.sender = checkNotNull(sender);
25 + this.timestamps = checkNotNull(timestamps);
26 + this.tombstones = checkNotNull(tombstones);
27 + }
28 +
29 + public NodeId sender() {
30 + return sender;
31 + }
32 +
33 + public Map<HostFragmentId, Timestamp> timestamps() {
34 + return timestamps;
35 + }
36 +
37 + public Map<HostId, Timestamp> tombstones() {
38 + return tombstones;
39 + }
40 +
41 + // For serializer
42 + @SuppressWarnings("unused")
43 + private HostAntiEntropyAdvertisement() {
44 + this.sender = null;
45 + this.timestamps = null;
46 + this.tombstones = null;
47 + }
48 +}
1 +package org.onlab.onos.store.host.impl;
2 +
3 +import java.util.Objects;
4 +
5 +import org.onlab.onos.net.HostId;
6 +import org.onlab.onos.net.provider.ProviderId;
7 +
8 +import com.google.common.base.MoreObjects;
9 +
10 +/**
11 + * Identifier for HostDescription from a Provider.
12 + */
13 +public final class HostFragmentId {
14 + public final ProviderId providerId;
15 + public final HostId hostId;
16 +
17 + public HostFragmentId(HostId hostId, ProviderId providerId) {
18 + this.providerId = providerId;
19 + this.hostId = hostId;
20 + }
21 +
22 + public HostId hostId() {
23 + return hostId;
24 + }
25 +
26 + public ProviderId providerId() {
27 + return providerId;
28 + }
29 +
30 + @Override
31 + public int hashCode() {
32 + return Objects.hash(providerId, hostId);
33 + }
34 +
35 + @Override
36 + public boolean equals(Object obj) {
37 + if (this == obj) {
38 + return true;
39 + }
40 + if (!(obj instanceof HostFragmentId)) {
41 + return false;
42 + }
43 + HostFragmentId that = (HostFragmentId) obj;
44 + return Objects.equals(this.hostId, that.hostId) &&
45 + Objects.equals(this.providerId, that.providerId);
46 + }
47 +
48 + @Override
49 + public String toString() {
50 + return MoreObjects.toStringHelper(getClass())
51 + .add("providerId", providerId)
52 + .add("hostId", hostId)
53 + .toString();
54 + }
55 +
56 + // for serializer
57 + @SuppressWarnings("unused")
58 + private HostFragmentId() {
59 + this.providerId = null;
60 + this.hostId = null;
61 + }
62 +}
1 +/**
2 + * Implementation of the distributed host store using p2p synchronization protocol.
3 + */
4 +package org.onlab.onos.store.host.impl;
1 -package org.onlab.onos.store.common.impl; 1 +package org.onlab.onos.store.impl;
2 2
3 import static com.google.common.base.Preconditions.checkNotNull; 3 import static com.google.common.base.Preconditions.checkNotNull;
4 4
...@@ -58,12 +58,12 @@ public final class Timestamped<T> { ...@@ -58,12 +58,12 @@ public final class Timestamped<T> {
58 } 58 }
59 59
60 /** 60 /**
61 - * Tests if this timestamp is newer thatn the specified timestamp. 61 + * Tests if this timestamp is newer than the specified timestamp.
62 - * @param timestamp to compare agains 62 + * @param other timestamp to compare against
63 * @return true if this instance is newer 63 * @return true if this instance is newer
64 */ 64 */
65 - public boolean isNewer(Timestamp timestamp) { 65 + public boolean isNewer(Timestamp other) {
66 - return this.timestamp.compareTo(checkNotNull(timestamp)) > 0; 66 + return this.timestamp.compareTo(checkNotNull(other)) > 0;
67 } 67 }
68 68
69 @Override 69 @Override
......
1 package org.onlab.onos.store.link.impl; 1 package org.onlab.onos.store.link.impl;
2 2
3 import com.google.common.base.Function; 3 import com.google.common.base.Function;
4 -import com.google.common.base.Predicate;
5 import com.google.common.collect.FluentIterable; 4 import com.google.common.collect.FluentIterable;
6 import com.google.common.collect.HashMultimap; 5 import com.google.common.collect.HashMultimap;
7 import com.google.common.collect.ImmutableList; 6 import com.google.common.collect.ImmutableList;
...@@ -27,7 +26,6 @@ import org.onlab.onos.net.Link; ...@@ -27,7 +26,6 @@ import org.onlab.onos.net.Link;
27 import org.onlab.onos.net.SparseAnnotations; 26 import org.onlab.onos.net.SparseAnnotations;
28 import org.onlab.onos.net.Link.Type; 27 import org.onlab.onos.net.Link.Type;
29 import org.onlab.onos.net.LinkKey; 28 import org.onlab.onos.net.LinkKey;
30 -import org.onlab.onos.net.Provided;
31 import org.onlab.onos.net.device.DeviceClockService; 29 import org.onlab.onos.net.device.DeviceClockService;
32 import org.onlab.onos.net.link.DefaultLinkDescription; 30 import org.onlab.onos.net.link.DefaultLinkDescription;
33 import org.onlab.onos.net.link.LinkDescription; 31 import org.onlab.onos.net.link.LinkDescription;
...@@ -41,7 +39,7 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; ...@@ -41,7 +39,7 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
41 import org.onlab.onos.store.cluster.messaging.ClusterMessage; 39 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
42 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler; 40 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
43 import org.onlab.onos.store.cluster.messaging.MessageSubject; 41 import org.onlab.onos.store.cluster.messaging.MessageSubject;
44 -import org.onlab.onos.store.common.impl.Timestamped; 42 +import org.onlab.onos.store.impl.Timestamped;
45 import org.onlab.onos.store.serializers.DistributedStoreSerializers; 43 import org.onlab.onos.store.serializers.DistributedStoreSerializers;
46 import org.onlab.onos.store.serializers.KryoSerializer; 44 import org.onlab.onos.store.serializers.KryoSerializer;
47 import org.onlab.util.KryoPool; 45 import org.onlab.util.KryoPool;
...@@ -70,7 +68,9 @@ import static org.onlab.onos.net.link.LinkEvent.Type.*; ...@@ -70,7 +68,9 @@ import static org.onlab.onos.net.link.LinkEvent.Type.*;
70 import static org.onlab.util.Tools.namedThreads; 68 import static org.onlab.util.Tools.namedThreads;
71 import static org.slf4j.LoggerFactory.getLogger; 69 import static org.slf4j.LoggerFactory.getLogger;
72 import static com.google.common.collect.Multimaps.synchronizedSetMultimap; 70 import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
71 +import static com.google.common.base.Preconditions.checkNotNull;
73 import static com.google.common.base.Predicates.notNull; 72 import static com.google.common.base.Predicates.notNull;
73 +import static org.onlab.onos.store.link.impl.GossipLinkStoreMessageSubjects.LINK_ANTI_ENTROPY_ADVERTISEMENT;
74 74
75 /** 75 /**
76 * Manages inventory of infrastructure links in distributed data store 76 * Manages inventory of infrastructure links in distributed data store
...@@ -239,9 +239,9 @@ public class GossipLinkStore ...@@ -239,9 +239,9 @@ public class GossipLinkStore
239 LinkKey key = linkKey(linkDescription.src(), linkDescription.dst()); 239 LinkKey key = linkKey(linkDescription.src(), linkDescription.dst());
240 final LinkEvent event; 240 final LinkEvent event;
241 final Timestamped<LinkDescription> mergedDesc; 241 final Timestamped<LinkDescription> mergedDesc;
242 - synchronized (getLinkDescriptions(key)) { 242 + synchronized (getOrCreateLinkDescriptions(key)) {
243 event = createOrUpdateLinkInternal(providerId, deltaDesc); 243 event = createOrUpdateLinkInternal(providerId, deltaDesc);
244 - mergedDesc = getLinkDescriptions(key).get(providerId); 244 + mergedDesc = getOrCreateLinkDescriptions(key).get(providerId);
245 } 245 }
246 246
247 if (event != null) { 247 if (event != null) {
...@@ -265,7 +265,7 @@ public class GossipLinkStore ...@@ -265,7 +265,7 @@ public class GossipLinkStore
265 265
266 LinkKey key = linkKey(linkDescription.value().src(), 266 LinkKey key = linkKey(linkDescription.value().src(),
267 linkDescription.value().dst()); 267 linkDescription.value().dst());
268 - Map<ProviderId, Timestamped<LinkDescription>> descs = getLinkDescriptions(key); 268 + Map<ProviderId, Timestamped<LinkDescription>> descs = getOrCreateLinkDescriptions(key);
269 269
270 synchronized (descs) { 270 synchronized (descs) {
271 // if the link was previously removed, we should proceed if and 271 // if the link was previously removed, we should proceed if and
...@@ -296,7 +296,7 @@ public class GossipLinkStore ...@@ -296,7 +296,7 @@ public class GossipLinkStore
296 ProviderId providerId, 296 ProviderId providerId,
297 Timestamped<LinkDescription> linkDescription) { 297 Timestamped<LinkDescription> linkDescription) {
298 298
299 - // merge existing attributes and merge 299 + // merge existing annotations
300 Timestamped<LinkDescription> existingLinkDescription = descs.get(providerId); 300 Timestamped<LinkDescription> existingLinkDescription = descs.get(providerId);
301 if (existingLinkDescription != null && existingLinkDescription.isNewer(linkDescription)) { 301 if (existingLinkDescription != null && existingLinkDescription.isNewer(linkDescription)) {
302 return null; 302 return null;
...@@ -377,14 +377,54 @@ public class GossipLinkStore ...@@ -377,14 +377,54 @@ public class GossipLinkStore
377 return event; 377 return event;
378 } 378 }
379 379
380 + private static Timestamped<LinkDescription> getPrimaryDescription(
381 + Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions) {
382 +
383 + synchronized (linkDescriptions) {
384 + for (Entry<ProviderId, Timestamped<LinkDescription>>
385 + e : linkDescriptions.entrySet()) {
386 +
387 + if (!e.getKey().isAncillary()) {
388 + return e.getValue();
389 + }
390 + }
391 + }
392 + return null;
393 + }
394 +
395 +
396 + // TODO: consider slicing out as Timestamp utils
397 + /**
398 + * Checks is timestamp is more recent than timestamped object.
399 + *
400 + * @param timestamp to check if this is more recent then other
401 + * @param timestamped object to be tested against
402 + * @return true if {@code timestamp} is more recent than {@code timestamped}
403 + * or {@code timestamped is null}
404 + */
405 + private static boolean isMoreRecent(Timestamp timestamp, Timestamped<?> timestamped) {
406 + checkNotNull(timestamp);
407 + if (timestamped == null) {
408 + return true;
409 + }
410 + return timestamp.compareTo(timestamped.timestamp()) > 0;
411 + }
412 +
380 private LinkEvent removeLinkInternal(LinkKey key, Timestamp timestamp) { 413 private LinkEvent removeLinkInternal(LinkKey key, Timestamp timestamp) {
381 - Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions = 414 + Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions
382 - getLinkDescriptions(key); 415 + = getOrCreateLinkDescriptions(key);
416 +
383 synchronized (linkDescriptions) { 417 synchronized (linkDescriptions) {
418 + if (linkDescriptions.isEmpty()) {
419 + // never seen such link before. keeping timestamp for record
420 + removedLinks.put(key, timestamp);
421 + return null;
422 + }
384 // accept removal request if given timestamp is newer than 423 // accept removal request if given timestamp is newer than
385 // the latest Timestamp from Primary provider 424 // the latest Timestamp from Primary provider
386 - ProviderId primaryProviderId = pickPrimaryProviderId(linkDescriptions); 425 + Timestamped<LinkDescription> prim = getPrimaryDescription(linkDescriptions);
387 - if (linkDescriptions.get(primaryProviderId).isNewer(timestamp)) { 426 + if (!isMoreRecent(timestamp, prim)) {
427 + // outdated remove request, ignore
388 return null; 428 return null;
389 } 429 }
390 removedLinks.put(key, timestamp); 430 removedLinks.put(key, timestamp);
...@@ -406,12 +446,13 @@ public class GossipLinkStore ...@@ -406,12 +446,13 @@ public class GossipLinkStore
406 /** 446 /**
407 * @return primary ProviderID, or randomly chosen one if none exists 447 * @return primary ProviderID, or randomly chosen one if none exists
408 */ 448 */
409 - private ProviderId pickPrimaryProviderId( 449 + private static ProviderId pickBaseProviderId(
410 Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions) { 450 Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions) {
411 451
412 ProviderId fallBackPrimary = null; 452 ProviderId fallBackPrimary = null;
413 for (Entry<ProviderId, Timestamped<LinkDescription>> e : linkDescriptions.entrySet()) { 453 for (Entry<ProviderId, Timestamped<LinkDescription>> e : linkDescriptions.entrySet()) {
414 if (!e.getKey().isAncillary()) { 454 if (!e.getKey().isAncillary()) {
455 + // found primary
415 return e.getKey(); 456 return e.getKey();
416 } else if (fallBackPrimary == null) { 457 } else if (fallBackPrimary == null) {
417 // pick randomly as a fallback in case there is no primary 458 // pick randomly as a fallback in case there is no primary
...@@ -421,9 +462,10 @@ public class GossipLinkStore ...@@ -421,9 +462,10 @@ public class GossipLinkStore
421 return fallBackPrimary; 462 return fallBackPrimary;
422 } 463 }
423 464
465 + // Guarded by linkDescs value (=locking each Link)
424 private Link composeLink(Map<ProviderId, Timestamped<LinkDescription>> descs) { 466 private Link composeLink(Map<ProviderId, Timestamped<LinkDescription>> descs) {
425 - ProviderId primaryProviderId = pickPrimaryProviderId(descs); 467 + ProviderId baseProviderId = pickBaseProviderId(descs);
426 - Timestamped<LinkDescription> base = descs.get(primaryProviderId); 468 + Timestamped<LinkDescription> base = descs.get(baseProviderId);
427 469
428 ConnectPoint src = base.value().src(); 470 ConnectPoint src = base.value().src();
429 ConnectPoint dst = base.value().dst(); 471 ConnectPoint dst = base.value().dst();
...@@ -432,7 +474,7 @@ public class GossipLinkStore ...@@ -432,7 +474,7 @@ public class GossipLinkStore
432 annotations = merge(annotations, base.value().annotations()); 474 annotations = merge(annotations, base.value().annotations());
433 475
434 for (Entry<ProviderId, Timestamped<LinkDescription>> e : descs.entrySet()) { 476 for (Entry<ProviderId, Timestamped<LinkDescription>> e : descs.entrySet()) {
435 - if (primaryProviderId.equals(e.getKey())) { 477 + if (baseProviderId.equals(e.getKey())) {
436 continue; 478 continue;
437 } 479 }
438 480
...@@ -445,10 +487,10 @@ public class GossipLinkStore ...@@ -445,10 +487,10 @@ public class GossipLinkStore
445 annotations = merge(annotations, e.getValue().value().annotations()); 487 annotations = merge(annotations, e.getValue().value().annotations());
446 } 488 }
447 489
448 - return new DefaultLink(primaryProviderId , src, dst, type, annotations); 490 + return new DefaultLink(baseProviderId, src, dst, type, annotations);
449 } 491 }
450 492
451 - private Map<ProviderId, Timestamped<LinkDescription>> getLinkDescriptions(LinkKey key) { 493 + private Map<ProviderId, Timestamped<LinkDescription>> getOrCreateLinkDescriptions(LinkKey key) {
452 Map<ProviderId, Timestamped<LinkDescription>> r; 494 Map<ProviderId, Timestamped<LinkDescription>> r;
453 r = linkDescs.get(key); 495 r = linkDescs.get(key);
454 if (r != null) { 496 if (r != null) {
...@@ -464,11 +506,11 @@ public class GossipLinkStore ...@@ -464,11 +506,11 @@ public class GossipLinkStore
464 } 506 }
465 } 507 }
466 508
467 - private Timestamped<LinkDescription> getLinkDescription(LinkKey key, ProviderId providerId) {
468 - return getLinkDescriptions(key).get(providerId);
469 - }
470 -
471 private final Function<LinkKey, Link> lookupLink = new LookupLink(); 509 private final Function<LinkKey, Link> lookupLink = new LookupLink();
510 + /**
511 + * Returns a Function to lookup Link instance using LinkKey from cache.
512 + * @return
513 + */
472 private Function<LinkKey, Link> lookupLink() { 514 private Function<LinkKey, Link> lookupLink() {
473 return lookupLink; 515 return lookupLink;
474 } 516 }
...@@ -476,20 +518,11 @@ public class GossipLinkStore ...@@ -476,20 +518,11 @@ public class GossipLinkStore
476 private final class LookupLink implements Function<LinkKey, Link> { 518 private final class LookupLink implements Function<LinkKey, Link> {
477 @Override 519 @Override
478 public Link apply(LinkKey input) { 520 public Link apply(LinkKey input) {
479 - return links.get(input); 521 + if (input == null) {
480 - } 522 + return null;
481 - } 523 + } else {
482 - 524 + return links.get(input);
483 - private static final class IsPrimary implements Predicate<Provided> { 525 + }
484 -
485 - private static final Predicate<Provided> IS_PRIMARY = new IsPrimary();
486 - public static final Predicate<Provided> isPrimary() {
487 - return IS_PRIMARY;
488 - }
489 -
490 - @Override
491 - public boolean apply(Provided input) {
492 - return !input.providerId().isAncillary();
493 } 526 }
494 } 527 }
495 528
...@@ -499,7 +532,6 @@ public class GossipLinkStore ...@@ -499,7 +532,6 @@ public class GossipLinkStore
499 } 532 }
500 } 533 }
501 534
502 - // TODO: should we be throwing exception?
503 private void broadcastMessage(MessageSubject subject, Object event) throws IOException { 535 private void broadcastMessage(MessageSubject subject, Object event) throws IOException {
504 ClusterMessage message = new ClusterMessage( 536 ClusterMessage message = new ClusterMessage(
505 clusterService.getLocalNode().id(), 537 clusterService.getLocalNode().id(),
...@@ -508,17 +540,12 @@ public class GossipLinkStore ...@@ -508,17 +540,12 @@ public class GossipLinkStore
508 clusterCommunicator.broadcast(message); 540 clusterCommunicator.broadcast(message);
509 } 541 }
510 542
511 - // TODO: should we be throwing exception? 543 + private void unicastMessage(NodeId recipient, MessageSubject subject, Object event) throws IOException {
512 - private void unicastMessage(NodeId recipient, MessageSubject subject, Object event) { 544 + ClusterMessage message = new ClusterMessage(
513 - try { 545 + clusterService.getLocalNode().id(),
514 - ClusterMessage message = new ClusterMessage( 546 + subject,
515 - clusterService.getLocalNode().id(), 547 + SERIALIZER.encode(event));
516 - subject, 548 + clusterCommunicator.unicast(message, recipient);
517 - SERIALIZER.encode(event));
518 - clusterCommunicator.unicast(message, recipient);
519 - } catch (IOException e) {
520 - log.error("Failed to send a {} message to {}", subject.value(), recipient);
521 - }
522 } 549 }
523 550
524 private void notifyPeers(InternalLinkEvent event) throws IOException { 551 private void notifyPeers(InternalLinkEvent event) throws IOException {
...@@ -529,12 +556,22 @@ public class GossipLinkStore ...@@ -529,12 +556,22 @@ public class GossipLinkStore
529 broadcastMessage(GossipLinkStoreMessageSubjects.LINK_REMOVED, event); 556 broadcastMessage(GossipLinkStoreMessageSubjects.LINK_REMOVED, event);
530 } 557 }
531 558
559 + // notify peer, silently ignoring error
532 private void notifyPeer(NodeId peer, InternalLinkEvent event) { 560 private void notifyPeer(NodeId peer, InternalLinkEvent event) {
533 - unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_UPDATE, event); 561 + try {
562 + unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_UPDATE, event);
563 + } catch (IOException e) {
564 + log.debug("Failed to notify peer {} with message {}", peer, event);
565 + }
534 } 566 }
535 567
568 + // notify peer, silently ignoring error
536 private void notifyPeer(NodeId peer, InternalLinkRemovedEvent event) { 569 private void notifyPeer(NodeId peer, InternalLinkRemovedEvent event) {
537 - unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_REMOVED, event); 570 + try {
571 + unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_REMOVED, event);
572 + } catch (IOException e) {
573 + log.debug("Failed to notify peer {} with message {}", peer, event);
574 + }
538 } 575 }
539 576
540 private final class SendAdvertisementTask implements Runnable { 577 private final class SendAdvertisementTask implements Runnable {
...@@ -573,9 +610,9 @@ public class GossipLinkStore ...@@ -573,9 +610,9 @@ public class GossipLinkStore
573 } 610 }
574 611
575 try { 612 try {
576 - unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_ANTI_ENTROPY_ADVERTISEMENT, ad); 613 + unicastMessage(peer, LINK_ANTI_ENTROPY_ADVERTISEMENT, ad);
577 - } catch (Exception e) { 614 + } catch (IOException e) {
578 - log.error("Failed to send anti-entropy advertisement", e); 615 + log.debug("Failed to send anti-entropy advertisement to {}", peer);
579 return; 616 return;
580 } 617 }
581 } catch (Exception e) { 618 } catch (Exception e) {
...@@ -608,42 +645,75 @@ public class GossipLinkStore ...@@ -608,42 +645,75 @@ public class GossipLinkStore
608 return new LinkAntiEntropyAdvertisement(self, linkTimestamps, linkTombstones); 645 return new LinkAntiEntropyAdvertisement(self, linkTimestamps, linkTombstones);
609 } 646 }
610 647
611 - private void handleAntiEntropyAdvertisement(LinkAntiEntropyAdvertisement advertisement) { 648 + private void handleAntiEntropyAdvertisement(LinkAntiEntropyAdvertisement ad) {
612 -
613 - NodeId peer = advertisement.sender();
614 -
615 - Map<LinkFragmentId, Timestamp> linkTimestamps = advertisement.linkTimestamps();
616 - Map<LinkKey, Timestamp> linkTombstones = advertisement.linkTombstones();
617 - for (Map.Entry<LinkFragmentId, Timestamp> entry : linkTimestamps.entrySet()) {
618 - LinkFragmentId linkFragmentId = entry.getKey();
619 - Timestamp peerTimestamp = entry.getValue();
620 649
621 - LinkKey key = linkFragmentId.linkKey(); 650 + final NodeId sender = ad.sender();
622 - ProviderId providerId = linkFragmentId.providerId(); 651 + boolean localOutdated = false;
623 652
624 - Timestamped<LinkDescription> linkDescription = getLinkDescription(key, providerId); 653 + for (Entry<LinkKey, Map<ProviderId, Timestamped<LinkDescription>>>
625 - if (linkDescription.isNewer(peerTimestamp)) { 654 + l : linkDescs.entrySet()) {
626 - // I have more recent link description. update peer. 655 +
627 - notifyPeer(peer, new InternalLinkEvent(providerId, linkDescription)); 656 + final LinkKey key = l.getKey();
628 - } 657 + final Map<ProviderId, Timestamped<LinkDescription>> link = l.getValue();
629 - // else TODO: Peer has more recent link description. request it. 658 + synchronized (link) {
630 - 659 + Timestamp localLatest = removedLinks.get(key);
631 - Timestamp linkRemovedTimestamp = removedLinks.get(key); 660 +
632 - if (linkRemovedTimestamp != null && linkRemovedTimestamp.compareTo(peerTimestamp) > 0) { 661 + for (Entry<ProviderId, Timestamped<LinkDescription>> p : link.entrySet()) {
633 - // peer has a zombie link. update peer. 662 + final ProviderId providerId = p.getKey();
634 - notifyPeer(peer, new InternalLinkRemovedEvent(key, linkRemovedTimestamp)); 663 + final Timestamped<LinkDescription> pDesc = p.getValue();
664 +
665 + final LinkFragmentId fragId = new LinkFragmentId(key, providerId);
666 + // remote
667 + Timestamp remoteTimestamp = ad.linkTimestamps().get(fragId);
668 + if (remoteTimestamp == null) {
669 + remoteTimestamp = ad.linkTombstones().get(key);
670 + }
671 + if (remoteTimestamp == null ||
672 + pDesc.isNewer(remoteTimestamp)) {
673 + // I have more recent link description. update peer.
674 + notifyPeer(sender, new InternalLinkEvent(providerId, pDesc));
675 + } else {
676 + final Timestamp remoteLive = ad.linkTimestamps().get(fragId);
677 + if (remoteLive != null &&
678 + remoteLive.compareTo(pDesc.timestamp()) > 0) {
679 + // I have something outdated
680 + localOutdated = true;
681 + }
682 + }
683 +
684 + // search local latest along the way
685 + if (localLatest == null ||
686 + pDesc.isNewer(localLatest)) {
687 + localLatest = pDesc.timestamp();
688 + }
689 + }
690 + // Tests if remote remove is more recent then local latest.
691 + final Timestamp remoteRemove = ad.linkTombstones().get(key);
692 + if (remoteRemove != null) {
693 + if (localLatest != null &&
694 + localLatest.compareTo(remoteRemove) < 0) {
695 + // remote remove is more recent
696 + notifyDelegateIfNotNull(removeLinkInternal(key, remoteRemove));
697 + }
698 + }
635 } 699 }
636 } 700 }
637 701
638 - for (Map.Entry<LinkKey, Timestamp> entry : linkTombstones.entrySet()) { 702 + // populate remove info if not known locally
639 - LinkKey key = entry.getKey(); 703 + for (Entry<LinkKey, Timestamp> remoteRm : ad.linkTombstones().entrySet()) {
640 - Timestamp peerTimestamp = entry.getValue(); 704 + final LinkKey key = remoteRm.getKey();
705 + final Timestamp remoteRemove = remoteRm.getValue();
706 + // relying on removeLinkInternal to ignore stale info
707 + notifyDelegateIfNotNull(removeLinkInternal(key, remoteRemove));
708 + }
641 709
642 - ProviderId primaryProviderId = pickPrimaryProviderId(getLinkDescriptions(key)); 710 + if (localOutdated) {
643 - if (primaryProviderId != null) { 711 + // send back advertisement to speed up convergence
644 - if (!getLinkDescription(key, primaryProviderId).isNewer(peerTimestamp)) { 712 + try {
645 - notifyDelegateIfNotNull(removeLinkInternal(key, peerTimestamp)); 713 + unicastMessage(sender, LINK_ANTI_ENTROPY_ADVERTISEMENT,
646 - } 714 + createAdvertisement());
715 + } catch (IOException e) {
716 + log.debug("Failed to send back active advertisement");
647 } 717 }
648 } 718 }
649 } 719 }
......
...@@ -4,7 +4,7 @@ import com.google.common.base.MoreObjects; ...@@ -4,7 +4,7 @@ import com.google.common.base.MoreObjects;
4 4
5 import org.onlab.onos.net.link.LinkDescription; 5 import org.onlab.onos.net.link.LinkDescription;
6 import org.onlab.onos.net.provider.ProviderId; 6 import org.onlab.onos.net.provider.ProviderId;
7 -import org.onlab.onos.store.common.impl.Timestamped; 7 +import org.onlab.onos.store.impl.Timestamped;
8 8
9 /** 9 /**
10 * Information published by GossipDeviceStore to notify peers of a device 10 * Information published by GossipDeviceStore to notify peers of a device
......
1 /** 1 /**
2 - * Implementation of link store using distributed p2p synchronization protocol. 2 + * Implementation of distributed link store using p2p synchronization protocol.
3 */ 3 */
4 package org.onlab.onos.store.link.impl; 4 package org.onlab.onos.store.link.impl;
......
1 package org.onlab.onos.store.serializers; 1 package org.onlab.onos.store.serializers;
2 2
3 -import org.onlab.onos.store.common.impl.Timestamped;
4 import org.onlab.onos.store.impl.MastershipBasedTimestamp; 3 import org.onlab.onos.store.impl.MastershipBasedTimestamp;
4 +import org.onlab.onos.store.impl.Timestamped;
5 import org.onlab.onos.store.impl.WallClockTimestamp; 5 import org.onlab.onos.store.impl.WallClockTimestamp;
6 import org.onlab.util.KryoPool; 6 import org.onlab.util.KryoPool;
7 7
......
1 +/**
2 + * Implementation of distributed topology store using p2p synchronization protocol.
3 + */
4 +package org.onlab.onos.store.topology.impl;
1 -package org.onlab.onos.store.common.impl; 1 +package org.onlab.onos.store.impl;
2 2
3 import static org.junit.Assert.*; 3 import static org.junit.Assert.*;
4 4
...@@ -6,7 +6,6 @@ import java.nio.ByteBuffer; ...@@ -6,7 +6,6 @@ import java.nio.ByteBuffer;
6 6
7 import org.junit.Test; 7 import org.junit.Test;
8 import org.onlab.onos.store.Timestamp; 8 import org.onlab.onos.store.Timestamp;
9 -import org.onlab.onos.store.impl.MastershipBasedTimestamp;
10 import org.onlab.util.KryoPool; 9 import org.onlab.util.KryoPool;
11 10
12 import com.google.common.testing.EqualsTester; 11 import com.google.common.testing.EqualsTester;
......
...@@ -19,10 +19,6 @@ ...@@ -19,10 +19,6 @@
19 <dependencies> 19 <dependencies>
20 <dependency> 20 <dependency>
21 <groupId>org.onlab.onos</groupId> 21 <groupId>org.onlab.onos</groupId>
22 - <artifactId>onos-api</artifactId>
23 - </dependency>
24 - <dependency>
25 - <groupId>org.onlab.onos</groupId>
26 <artifactId>onos-core-serializers</artifactId> 22 <artifactId>onos-core-serializers</artifactId>
27 <version>${project.version}</version> 23 <version>${project.version}</version>
28 </dependency> 24 </dependency>
...@@ -38,23 +34,6 @@ ...@@ -38,23 +34,6 @@
38 <scope>test</scope> 34 <scope>test</scope>
39 <version>${project.version}</version> 35 <version>${project.version}</version>
40 </dependency> 36 </dependency>
41 - <dependency>
42 - <groupId>org.apache.felix</groupId>
43 - <artifactId>org.apache.felix.scr.annotations</artifactId>
44 - </dependency>
45 - <dependency>
46 - <groupId>com.hazelcast</groupId>
47 - <artifactId>hazelcast</artifactId>
48 - </dependency>
49 </dependencies> 37 </dependencies>
50 38
51 - <build>
52 - <plugins>
53 - <plugin>
54 - <groupId>org.apache.felix</groupId>
55 - <artifactId>maven-scr-plugin</artifactId>
56 - </plugin>
57 - </plugins>
58 - </build>
59 -
60 </project> 39 </project>
......
...@@ -19,34 +19,13 @@ ...@@ -19,34 +19,13 @@
19 <dependencies> 19 <dependencies>
20 <dependency> 20 <dependency>
21 <groupId>org.onlab.onos</groupId> 21 <groupId>org.onlab.onos</groupId>
22 - <artifactId>onos-api</artifactId>
23 - </dependency>
24 - <dependency>
25 - <groupId>org.onlab.onos</groupId>
26 <artifactId>onos-core-serializers</artifactId> 22 <artifactId>onos-core-serializers</artifactId>
27 <version>${project.version}</version> 23 <version>${project.version}</version>
28 </dependency> 24 </dependency>
29 <dependency> 25 <dependency>
30 - <groupId>org.apache.felix</groupId>
31 - <artifactId>org.apache.felix.scr.annotations</artifactId>
32 - </dependency>
33 - <dependency>
34 - <groupId>com.hazelcast</groupId>
35 - <artifactId>hazelcast</artifactId>
36 - </dependency>
37 - <dependency>
38 <groupId>org.apache.commons</groupId> 26 <groupId>org.apache.commons</groupId>
39 <artifactId>commons-lang3</artifactId> 27 <artifactId>commons-lang3</artifactId>
40 </dependency> 28 </dependency>
41 </dependencies> 29 </dependencies>
42 30
43 - <build>
44 - <plugins>
45 - <plugin>
46 - <groupId>org.apache.felix</groupId>
47 - <artifactId>maven-scr-plugin</artifactId>
48 - </plugin>
49 - </plugins>
50 - </build>
51 -
52 </project> 31 </project>
......
1 -package org.onlab.onos.store.device.impl;
2 -
3 -import org.apache.felix.scr.annotations.Component;
4 -import org.apache.felix.scr.annotations.Service;
5 -import org.onlab.onos.mastership.MastershipTerm;
6 -import org.onlab.onos.net.DeviceId;
7 -import org.onlab.onos.net.device.DeviceClockProviderService;
8 -
9 -// FIXME: Code clone in onos-core-trivial, onos-core-hz-net
10 -/**
11 - * Dummy implementation of {@link DeviceClockProviderService}.
12 - */
13 -@Component(immediate = true)
14 -@Service
15 -public class NoOpClockProviderService implements DeviceClockProviderService {
16 -
17 - @Override
18 - public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
19 - }
20 -}
1 -package org.onlab.onos.store.flow.impl;
2 -
3 -import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
4 -import static org.slf4j.LoggerFactory.getLogger;
5 -
6 -import java.util.Collection;
7 -import java.util.Collections;
8 -
9 -import org.apache.felix.scr.annotations.Activate;
10 -import org.apache.felix.scr.annotations.Component;
11 -import org.apache.felix.scr.annotations.Deactivate;
12 -import org.apache.felix.scr.annotations.Service;
13 -import org.onlab.onos.ApplicationId;
14 -import org.onlab.onos.net.DeviceId;
15 -import org.onlab.onos.net.flow.DefaultFlowEntry;
16 -import org.onlab.onos.net.flow.FlowEntry;
17 -import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
18 -import org.onlab.onos.net.flow.FlowRule;
19 -import org.onlab.onos.net.flow.FlowRuleEvent;
20 -import org.onlab.onos.net.flow.FlowRuleEvent.Type;
21 -import org.onlab.onos.net.flow.FlowRuleStore;
22 -import org.onlab.onos.net.flow.FlowRuleStoreDelegate;
23 -import org.onlab.onos.store.AbstractStore;
24 -import org.slf4j.Logger;
25 -
26 -import com.google.common.collect.ArrayListMultimap;
27 -import com.google.common.collect.ImmutableSet;
28 -import com.google.common.collect.Multimap;
29 -
30 -/**
31 - * Manages inventory of flow rules using trivial in-memory implementation.
32 - */
33 -//FIXME I LIE. I AIN'T DISTRIBUTED
34 -@Component(immediate = true)
35 -@Service
36 -public class DistributedFlowRuleStore
37 - extends AbstractStore<FlowRuleEvent, FlowRuleStoreDelegate>
38 - implements FlowRuleStore {
39 -
40 - private final Logger log = getLogger(getClass());
41 -
42 - // store entries as a pile of rules, no info about device tables
43 - private final Multimap<DeviceId, FlowEntry> flowEntries =
44 - ArrayListMultimap.<DeviceId, FlowEntry>create();
45 -
46 - private final Multimap<Short, FlowRule> flowEntriesById =
47 - ArrayListMultimap.<Short, FlowRule>create();
48 -
49 - @Activate
50 - public void activate() {
51 - log.info("Started");
52 - }
53 -
54 - @Deactivate
55 - public void deactivate() {
56 - log.info("Stopped");
57 - }
58 -
59 -
60 - @Override
61 - public int getFlowRuleCount() {
62 - return flowEntries.size();
63 - }
64 -
65 - @Override
66 - public synchronized FlowEntry getFlowEntry(FlowRule rule) {
67 - for (FlowEntry f : flowEntries.get(rule.deviceId())) {
68 - if (f.equals(rule)) {
69 - return f;
70 - }
71 - }
72 - return null;
73 - }
74 -
75 - @Override
76 - public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
77 - Collection<FlowEntry> rules = flowEntries.get(deviceId);
78 - if (rules == null) {
79 - return Collections.emptyList();
80 - }
81 - return ImmutableSet.copyOf(rules);
82 - }
83 -
84 - @Override
85 - public synchronized Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId) {
86 - Collection<FlowRule> rules = flowEntriesById.get(appId.id());
87 - if (rules == null) {
88 - return Collections.emptyList();
89 - }
90 - return ImmutableSet.copyOf(rules);
91 - }
92 -
93 - @Override
94 - public synchronized void storeFlowRule(FlowRule rule) {
95 - FlowEntry f = new DefaultFlowEntry(rule);
96 - DeviceId did = f.deviceId();
97 - if (!flowEntries.containsEntry(did, f)) {
98 - flowEntries.put(did, f);
99 - flowEntriesById.put(rule.appId(), f);
100 - }
101 - }
102 -
103 - @Override
104 - public synchronized void deleteFlowRule(FlowRule rule) {
105 - FlowEntry entry = getFlowEntry(rule);
106 - if (entry == null) {
107 - return;
108 - }
109 - entry.setState(FlowEntryState.PENDING_REMOVE);
110 - }
111 -
112 - @Override
113 - public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
114 - DeviceId did = rule.deviceId();
115 -
116 - // check if this new rule is an update to an existing entry
117 - FlowEntry stored = getFlowEntry(rule);
118 - if (stored != null) {
119 - stored.setBytes(rule.bytes());
120 - stored.setLife(rule.life());
121 - stored.setPackets(rule.packets());
122 - if (stored.state() == FlowEntryState.PENDING_ADD) {
123 - stored.setState(FlowEntryState.ADDED);
124 - return new FlowRuleEvent(Type.RULE_ADDED, rule);
125 - }
126 - return new FlowRuleEvent(Type.RULE_UPDATED, rule);
127 - }
128 -
129 - flowEntries.put(did, rule);
130 - return null;
131 - }
132 -
133 - @Override
134 - public synchronized FlowRuleEvent removeFlowRule(FlowEntry rule) {
135 - // This is where one could mark a rule as removed and still keep it in the store.
136 - if (flowEntries.remove(rule.deviceId(), rule)) {
137 - return new FlowRuleEvent(RULE_REMOVED, rule);
138 - } else {
139 - return null;
140 - }
141 - }
142 -}
1 -package org.onlab.onos.store.host.impl;
2 -
3 -import com.google.common.collect.HashMultimap;
4 -import com.google.common.collect.ImmutableSet;
5 -import com.google.common.collect.Multimap;
6 -import com.google.common.collect.Sets;
7 -import org.apache.felix.scr.annotations.Activate;
8 -import org.apache.felix.scr.annotations.Component;
9 -import org.apache.felix.scr.annotations.Deactivate;
10 -import org.apache.felix.scr.annotations.Service;
11 -import org.onlab.onos.net.Annotations;
12 -import org.onlab.onos.net.ConnectPoint;
13 -import org.onlab.onos.net.DefaultHost;
14 -import org.onlab.onos.net.DeviceId;
15 -import org.onlab.onos.net.Host;
16 -import org.onlab.onos.net.HostId;
17 -import org.onlab.onos.net.HostLocation;
18 -import org.onlab.onos.net.host.HostDescription;
19 -import org.onlab.onos.net.host.HostEvent;
20 -import org.onlab.onos.net.host.HostStore;
21 -import org.onlab.onos.net.host.HostStoreDelegate;
22 -import org.onlab.onos.net.host.PortAddresses;
23 -import org.onlab.onos.net.provider.ProviderId;
24 -import org.onlab.onos.store.AbstractStore;
25 -import org.onlab.packet.IpPrefix;
26 -import org.onlab.packet.MacAddress;
27 -import org.onlab.packet.VlanId;
28 -import org.slf4j.Logger;
29 -
30 -import java.util.HashSet;
31 -import java.util.Map;
32 -import java.util.Set;
33 -import java.util.concurrent.ConcurrentHashMap;
34 -
35 -import static org.onlab.onos.net.host.HostEvent.Type.*;
36 -import static org.slf4j.LoggerFactory.getLogger;
37 -
38 -/**
39 - * TEMPORARY: Manages inventory of end-station hosts using distributed
40 - * structures implementation.
41 - */
42 -//FIXME: I LIE I AM NOT DISTRIBUTED
43 -@Component(immediate = true)
44 -@Service
45 -public class DistributedHostStore
46 - extends AbstractStore<HostEvent, HostStoreDelegate>
47 - implements HostStore {
48 -
49 - private final Logger log = getLogger(getClass());
50 -
51 - // Host inventory
52 - private final Map<HostId, StoredHost> hosts = new ConcurrentHashMap<>(2000000, 0.75f, 16);
53 -
54 - // Hosts tracked by their location
55 - private final Multimap<ConnectPoint, Host> locations = HashMultimap.create();
56 -
57 - private final Map<ConnectPoint, PortAddresses> portAddresses =
58 - new ConcurrentHashMap<>();
59 -
60 - @Activate
61 - public void activate() {
62 - log.info("Started");
63 - }
64 -
65 - @Deactivate
66 - public void deactivate() {
67 - log.info("Stopped");
68 - }
69 -
70 - @Override
71 - public HostEvent createOrUpdateHost(ProviderId providerId, HostId hostId,
72 - HostDescription hostDescription) {
73 - StoredHost host = hosts.get(hostId);
74 - if (host == null) {
75 - return createHost(providerId, hostId, hostDescription);
76 - }
77 - return updateHost(providerId, host, hostDescription);
78 - }
79 -
80 - // creates a new host and sends HOST_ADDED
81 - private HostEvent createHost(ProviderId providerId, HostId hostId,
82 - HostDescription descr) {
83 - StoredHost newhost = new StoredHost(providerId, hostId,
84 - descr.hwAddress(),
85 - descr.vlan(),
86 - descr.location(),
87 - ImmutableSet.of(descr.ipAddress()));
88 - synchronized (this) {
89 - hosts.put(hostId, newhost);
90 - locations.put(descr.location(), newhost);
91 - }
92 - return new HostEvent(HOST_ADDED, newhost);
93 - }
94 -
95 - // checks for type of update to host, sends appropriate event
96 - private HostEvent updateHost(ProviderId providerId, StoredHost host,
97 - HostDescription descr) {
98 - HostEvent event;
99 - if (!host.location().equals(descr.location())) {
100 - host.setLocation(descr.location());
101 - return new HostEvent(HOST_MOVED, host);
102 - }
103 -
104 - if (host.ipAddresses().contains(descr.ipAddress())) {
105 - return null;
106 - }
107 -
108 - Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses());
109 - addresses.add(descr.ipAddress());
110 - StoredHost updated = new StoredHost(providerId, host.id(),
111 - host.mac(), host.vlan(),
112 - descr.location(), addresses);
113 - event = new HostEvent(HOST_UPDATED, updated);
114 - synchronized (this) {
115 - hosts.put(host.id(), updated);
116 - locations.remove(host.location(), host);
117 - locations.put(updated.location(), updated);
118 - }
119 - return event;
120 - }
121 -
122 - @Override
123 - public HostEvent removeHost(HostId hostId) {
124 - synchronized (this) {
125 - Host host = hosts.remove(hostId);
126 - if (host != null) {
127 - locations.remove((host.location()), host);
128 - return new HostEvent(HOST_REMOVED, host);
129 - }
130 - return null;
131 - }
132 - }
133 -
134 - @Override
135 - public int getHostCount() {
136 - return hosts.size();
137 - }
138 -
139 - @Override
140 - public Iterable<Host> getHosts() {
141 - return ImmutableSet.<Host>copyOf(hosts.values());
142 - }
143 -
144 - @Override
145 - public Host getHost(HostId hostId) {
146 - return hosts.get(hostId);
147 - }
148 -
149 - @Override
150 - public Set<Host> getHosts(VlanId vlanId) {
151 - Set<Host> vlanset = new HashSet<>();
152 - for (Host h : hosts.values()) {
153 - if (h.vlan().equals(vlanId)) {
154 - vlanset.add(h);
155 - }
156 - }
157 - return vlanset;
158 - }
159 -
160 - @Override
161 - public Set<Host> getHosts(MacAddress mac) {
162 - Set<Host> macset = new HashSet<>();
163 - for (Host h : hosts.values()) {
164 - if (h.mac().equals(mac)) {
165 - macset.add(h);
166 - }
167 - }
168 - return macset;
169 - }
170 -
171 - @Override
172 - public Set<Host> getHosts(IpPrefix ip) {
173 - Set<Host> ipset = new HashSet<>();
174 - for (Host h : hosts.values()) {
175 - if (h.ipAddresses().contains(ip)) {
176 - ipset.add(h);
177 - }
178 - }
179 - return ipset;
180 - }
181 -
182 - @Override
183 - public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
184 - return ImmutableSet.copyOf(locations.get(connectPoint));
185 - }
186 -
187 - @Override
188 - public Set<Host> getConnectedHosts(DeviceId deviceId) {
189 - Set<Host> hostset = new HashSet<>();
190 - for (ConnectPoint p : locations.keySet()) {
191 - if (p.deviceId().equals(deviceId)) {
192 - hostset.addAll(locations.get(p));
193 - }
194 - }
195 - return hostset;
196 - }
197 -
198 - @Override
199 - public void updateAddressBindings(PortAddresses addresses) {
200 - synchronized (portAddresses) {
201 - PortAddresses existing = portAddresses.get(addresses.connectPoint());
202 - if (existing == null) {
203 - portAddresses.put(addresses.connectPoint(), addresses);
204 - } else {
205 - Set<IpPrefix> union = Sets.union(existing.ips(), addresses.ips())
206 - .immutableCopy();
207 -
208 - MacAddress newMac = (addresses.mac() == null) ? existing.mac()
209 - : addresses.mac();
210 -
211 - PortAddresses newAddresses =
212 - new PortAddresses(addresses.connectPoint(), union, newMac);
213 -
214 - portAddresses.put(newAddresses.connectPoint(), newAddresses);
215 - }
216 - }
217 - }
218 -
219 - @Override
220 - public void removeAddressBindings(PortAddresses addresses) {
221 - synchronized (portAddresses) {
222 - PortAddresses existing = portAddresses.get(addresses.connectPoint());
223 - if (existing != null) {
224 - Set<IpPrefix> difference =
225 - Sets.difference(existing.ips(), addresses.ips()).immutableCopy();
226 -
227 - // If they removed the existing mac, set the new mac to null.
228 - // Otherwise, keep the existing mac.
229 - MacAddress newMac = existing.mac();
230 - if (addresses.mac() != null && addresses.mac().equals(existing.mac())) {
231 - newMac = null;
232 - }
233 -
234 - PortAddresses newAddresses =
235 - new PortAddresses(addresses.connectPoint(), difference, newMac);
236 -
237 - portAddresses.put(newAddresses.connectPoint(), newAddresses);
238 - }
239 - }
240 - }
241 -
242 - @Override
243 - public void clearAddressBindings(ConnectPoint connectPoint) {
244 - synchronized (portAddresses) {
245 - portAddresses.remove(connectPoint);
246 - }
247 - }
248 -
249 - @Override
250 - public Set<PortAddresses> getAddressBindings() {
251 - synchronized (portAddresses) {
252 - return new HashSet<>(portAddresses.values());
253 - }
254 - }
255 -
256 - @Override
257 - public PortAddresses getAddressBindingsForPort(ConnectPoint connectPoint) {
258 - PortAddresses addresses;
259 -
260 - synchronized (portAddresses) {
261 - addresses = portAddresses.get(connectPoint);
262 - }
263 -
264 - if (addresses == null) {
265 - addresses = new PortAddresses(connectPoint, null, null);
266 - }
267 -
268 - return addresses;
269 - }
270 -
271 - // Auxiliary extension to allow location to mutate.
272 - private class StoredHost extends DefaultHost {
273 - private HostLocation location;
274 -
275 - /**
276 - * Creates an end-station host using the supplied information.
277 - *
278 - * @param providerId provider identity
279 - * @param id host identifier
280 - * @param mac host MAC address
281 - * @param vlan host VLAN identifier
282 - * @param location host location
283 - * @param ips host IP addresses
284 - * @param annotations optional key/value annotations
285 - */
286 - public StoredHost(ProviderId providerId, HostId id,
287 - MacAddress mac, VlanId vlan, HostLocation location,
288 - Set<IpPrefix> ips, Annotations... annotations) {
289 - super(providerId, id, mac, vlan, location, ips, annotations);
290 - this.location = location;
291 - }
292 -
293 - void setLocation(HostLocation location) {
294 - this.location = location;
295 - }
296 -
297 - @Override
298 - public HostLocation location() {
299 - return location;
300 - }
301 - }
302 -}
1 -package org.onlab.onos.store.link.impl;
2 -
3 -import static com.google.common.cache.CacheBuilder.newBuilder;
4 -import static org.onlab.onos.net.Link.Type.DIRECT;
5 -import static org.onlab.onos.net.Link.Type.INDIRECT;
6 -import static org.onlab.onos.net.LinkKey.linkKey;
7 -import static org.onlab.onos.net.link.LinkEvent.Type.LINK_ADDED;
8 -import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
9 -import static org.onlab.onos.net.link.LinkEvent.Type.LINK_UPDATED;
10 -import static org.slf4j.LoggerFactory.getLogger;
11 -
12 -import java.util.HashSet;
13 -import java.util.Set;
14 -
15 -import org.apache.felix.scr.annotations.Activate;
16 -import org.apache.felix.scr.annotations.Component;
17 -import org.apache.felix.scr.annotations.Deactivate;
18 -import org.apache.felix.scr.annotations.Service;
19 -import org.onlab.onos.net.ConnectPoint;
20 -import org.onlab.onos.net.DefaultLink;
21 -import org.onlab.onos.net.DeviceId;
22 -import org.onlab.onos.net.Link;
23 -import org.onlab.onos.net.LinkKey;
24 -import org.onlab.onos.net.link.LinkDescription;
25 -import org.onlab.onos.net.link.LinkEvent;
26 -import org.onlab.onos.net.link.LinkStore;
27 -import org.onlab.onos.net.link.LinkStoreDelegate;
28 -import org.onlab.onos.net.provider.ProviderId;
29 -import org.onlab.onos.store.common.AbsentInvalidatingLoadingCache;
30 -import org.onlab.onos.store.common.AbstractHazelcastStore;
31 -import org.onlab.onos.store.common.OptionalCacheLoader;
32 -import org.slf4j.Logger;
33 -
34 -import com.google.common.base.Optional;
35 -import com.google.common.cache.LoadingCache;
36 -import com.google.common.collect.HashMultimap;
37 -import com.google.common.collect.ImmutableSet;
38 -import com.google.common.collect.Multimap;
39 -import com.google.common.collect.ImmutableSet.Builder;
40 -import com.hazelcast.core.IMap;
41 -
42 -//TODO: Add support for multiple provider and annotations
43 -/**
44 - * Manages inventory of infrastructure links using Hazelcast-backed map.
45 - */
46 -@Component(immediate = true)
47 -@Service
48 -public class DistributedLinkStore
49 - extends AbstractHazelcastStore<LinkEvent, LinkStoreDelegate>
50 - implements LinkStore {
51 -
52 - private final Logger log = getLogger(getClass());
53 -
54 - // Link inventory
55 - private IMap<byte[], byte[]> rawLinks;
56 - private LoadingCache<LinkKey, Optional<DefaultLink>> links;
57 -
58 - // TODO synchronize?
59 - // Egress and ingress link sets
60 - private final Multimap<DeviceId, Link> srcLinks = HashMultimap.create();
61 - private final Multimap<DeviceId, Link> dstLinks = HashMultimap.create();
62 -
63 - private String linksListener;
64 -
65 - @Override
66 - @Activate
67 - public void activate() {
68 - super.activate();
69 -
70 - boolean includeValue = true;
71 -
72 - // TODO decide on Map name scheme to avoid collision
73 - rawLinks = theInstance.getMap("links");
74 - final OptionalCacheLoader<LinkKey, DefaultLink> linkLoader
75 - = new OptionalCacheLoader<>(serializer, rawLinks);
76 - links = new AbsentInvalidatingLoadingCache<>(newBuilder().build(linkLoader));
77 - // refresh/populate cache based on notification from other instance
78 - linksListener = rawLinks.addEntryListener(new RemoteLinkEventHandler(links), includeValue);
79 -
80 - loadLinkCache();
81 -
82 - log.info("Started");
83 - }
84 -
85 - @Deactivate
86 - public void deactivate() {
87 - rawLinks.removeEntryListener(linksListener);
88 - log.info("Stopped");
89 - }
90 -
91 - private void loadLinkCache() {
92 - for (byte[] keyBytes : rawLinks.keySet()) {
93 - final LinkKey id = deserialize(keyBytes);
94 - links.refresh(id);
95 - }
96 - }
97 -
98 - @Override
99 - public int getLinkCount() {
100 - return links.asMap().size();
101 - }
102 -
103 - @Override
104 - public Iterable<Link> getLinks() {
105 - Builder<Link> builder = ImmutableSet.builder();
106 - for (Optional<DefaultLink> e : links.asMap().values()) {
107 - if (e.isPresent()) {
108 - builder.add(e.get());
109 - }
110 - }
111 - return builder.build();
112 - }
113 -
114 - @Override
115 - public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
116 - return ImmutableSet.copyOf(srcLinks.get(deviceId));
117 - }
118 -
119 - @Override
120 - public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
121 - return ImmutableSet.copyOf(dstLinks.get(deviceId));
122 - }
123 -
124 - @Override
125 - public Link getLink(ConnectPoint src, ConnectPoint dst) {
126 - return links.getUnchecked(linkKey(src, dst)).orNull();
127 - }
128 -
129 - @Override
130 - public Set<Link> getEgressLinks(ConnectPoint src) {
131 - Set<Link> egress = new HashSet<>();
132 - for (Link link : srcLinks.get(src.deviceId())) {
133 - if (link.src().equals(src)) {
134 - egress.add(link);
135 - }
136 - }
137 - return egress;
138 - }
139 -
140 - @Override
141 - public Set<Link> getIngressLinks(ConnectPoint dst) {
142 - Set<Link> ingress = new HashSet<>();
143 - for (Link link : dstLinks.get(dst.deviceId())) {
144 - if (link.dst().equals(dst)) {
145 - ingress.add(link);
146 - }
147 - }
148 - return ingress;
149 - }
150 -
151 - @Override
152 - public LinkEvent createOrUpdateLink(ProviderId providerId,
153 - LinkDescription linkDescription) {
154 - LinkKey key = linkKey(linkDescription.src(), linkDescription.dst());
155 - Optional<DefaultLink> link = links.getUnchecked(key);
156 - if (!link.isPresent()) {
157 - return createLink(providerId, key, linkDescription);
158 - }
159 - return updateLink(providerId, link.get(), key, linkDescription);
160 - }
161 -
162 - // Creates and stores the link and returns the appropriate event.
163 - private LinkEvent createLink(ProviderId providerId, LinkKey key,
164 - LinkDescription linkDescription) {
165 - DefaultLink link = new DefaultLink(providerId, key.src(), key.dst(),
166 - linkDescription.type());
167 - synchronized (this) {
168 - final byte[] keyBytes = serialize(key);
169 - rawLinks.put(keyBytes, serialize(link));
170 - links.asMap().putIfAbsent(key, Optional.of(link));
171 -
172 - addNewLink(link);
173 - }
174 - return new LinkEvent(LINK_ADDED, link);
175 - }
176 -
177 - // update Egress and ingress link sets
178 - private void addNewLink(DefaultLink link) {
179 - synchronized (this) {
180 - srcLinks.put(link.src().deviceId(), link);
181 - dstLinks.put(link.dst().deviceId(), link);
182 - }
183 - }
184 -
185 - // Updates, if necessary the specified link and returns the appropriate event.
186 - private LinkEvent updateLink(ProviderId providerId, DefaultLink link,
187 - LinkKey key, LinkDescription linkDescription) {
188 - // FIXME confirm Link update condition is OK
189 - if (link.type() == INDIRECT && linkDescription.type() == DIRECT) {
190 - synchronized (this) {
191 -
192 - DefaultLink updated =
193 - new DefaultLink(providerId, link.src(), link.dst(),
194 - linkDescription.type());
195 - final byte[] keyBytes = serialize(key);
196 - rawLinks.put(keyBytes, serialize(updated));
197 - links.asMap().replace(key, Optional.of(link), Optional.of(updated));
198 -
199 - replaceLink(link, updated);
200 - return new LinkEvent(LINK_UPDATED, updated);
201 - }
202 - }
203 - return null;
204 - }
205 -
206 - // update Egress and ingress link sets
207 - private void replaceLink(DefaultLink link, DefaultLink updated) {
208 - synchronized (this) {
209 - srcLinks.remove(link.src().deviceId(), link);
210 - dstLinks.remove(link.dst().deviceId(), link);
211 -
212 - srcLinks.put(link.src().deviceId(), updated);
213 - dstLinks.put(link.dst().deviceId(), updated);
214 - }
215 - }
216 -
217 - @Override
218 - public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
219 - synchronized (this) {
220 - LinkKey key = linkKey(src, dst);
221 - byte[] keyBytes = serialize(key);
222 - Link link = deserialize(rawLinks.remove(keyBytes));
223 - links.invalidate(key);
224 - if (link != null) {
225 - removeLink(link);
226 - return new LinkEvent(LINK_REMOVED, link);
227 - }
228 - return null;
229 - }
230 - }
231 -
232 - // update Egress and ingress link sets
233 - private void removeLink(Link link) {
234 - synchronized (this) {
235 - srcLinks.remove(link.src().deviceId(), link);
236 - dstLinks.remove(link.dst().deviceId(), link);
237 - }
238 - }
239 -
240 - private class RemoteLinkEventHandler extends RemoteCacheEventHandler<LinkKey, DefaultLink> {
241 - public RemoteLinkEventHandler(LoadingCache<LinkKey, Optional<DefaultLink>> cache) {
242 - super(cache);
243 - }
244 -
245 - @Override
246 - protected void onAdd(LinkKey key, DefaultLink newVal) {
247 - addNewLink(newVal);
248 - notifyDelegate(new LinkEvent(LINK_ADDED, newVal));
249 - }
250 -
251 - @Override
252 - protected void onUpdate(LinkKey key, DefaultLink oldVal, DefaultLink newVal) {
253 - replaceLink(oldVal, newVal);
254 - notifyDelegate(new LinkEvent(LINK_UPDATED, newVal));
255 - }
256 -
257 - @Override
258 - protected void onRemove(LinkKey key, DefaultLink val) {
259 - removeLink(val);
260 - notifyDelegate(new LinkEvent(LINK_REMOVED, val));
261 - }
262 - }
263 -}
1 -package org.onlab.onos.store.topology.impl;
2 -
3 -import com.google.common.collect.ImmutableMap;
4 -import com.google.common.collect.ImmutableSet;
5 -import com.google.common.collect.ImmutableSetMultimap;
6 -import org.onlab.graph.DijkstraGraphSearch;
7 -import org.onlab.graph.GraphPathSearch;
8 -import org.onlab.graph.TarjanGraphSearch;
9 -import org.onlab.onos.net.AbstractModel;
10 -import org.onlab.onos.net.ConnectPoint;
11 -import org.onlab.onos.net.DefaultPath;
12 -import org.onlab.onos.net.DeviceId;
13 -import org.onlab.onos.net.Link;
14 -import org.onlab.onos.net.Path;
15 -import org.onlab.onos.net.provider.ProviderId;
16 -import org.onlab.onos.net.topology.ClusterId;
17 -import org.onlab.onos.net.topology.DefaultTopologyCluster;
18 -import org.onlab.onos.net.topology.DefaultTopologyVertex;
19 -import org.onlab.onos.net.topology.GraphDescription;
20 -import org.onlab.onos.net.topology.LinkWeight;
21 -import org.onlab.onos.net.topology.Topology;
22 -import org.onlab.onos.net.topology.TopologyCluster;
23 -import org.onlab.onos.net.topology.TopologyEdge;
24 -import org.onlab.onos.net.topology.TopologyGraph;
25 -import org.onlab.onos.net.topology.TopologyVertex;
26 -
27 -import java.util.ArrayList;
28 -import java.util.List;
29 -import java.util.Map;
30 -import java.util.Set;
31 -
32 -import static com.google.common.base.MoreObjects.toStringHelper;
33 -import static com.google.common.collect.ImmutableSetMultimap.Builder;
34 -import static org.onlab.graph.GraphPathSearch.Result;
35 -import static org.onlab.graph.TarjanGraphSearch.SCCResult;
36 -import static org.onlab.onos.net.Link.Type.INDIRECT;
37 -
38 -/**
39 - * Default implementation of the topology descriptor. This carries the
40 - * backing topology data.
41 - */
42 -public class DefaultTopology extends AbstractModel implements Topology {
43 -
44 - private static final DijkstraGraphSearch<TopologyVertex, TopologyEdge> DIJKSTRA =
45 - new DijkstraGraphSearch<>();
46 - private static final TarjanGraphSearch<TopologyVertex, TopologyEdge> TARJAN =
47 - new TarjanGraphSearch<>();
48 -
49 - private static final ProviderId PID = new ProviderId("core", "org.onlab.onos.net");
50 -
51 - private final long time;
52 - private final TopologyGraph graph;
53 -
54 - private final SCCResult<TopologyVertex, TopologyEdge> clusterResults;
55 - private final ImmutableMap<DeviceId, Result<TopologyVertex, TopologyEdge>> results;
56 - private final ImmutableSetMultimap<PathKey, Path> paths;
57 -
58 - private final ImmutableMap<ClusterId, TopologyCluster> clusters;
59 - private final ImmutableSet<ConnectPoint> infrastructurePoints;
60 - private final ImmutableSetMultimap<ClusterId, ConnectPoint> broadcastSets;
61 -
62 - private ImmutableMap<DeviceId, TopologyCluster> clustersByDevice;
63 - private ImmutableSetMultimap<TopologyCluster, DeviceId> devicesByCluster;
64 - private ImmutableSetMultimap<TopologyCluster, Link> linksByCluster;
65 -
66 -
67 - /**
68 - * Creates a topology descriptor attributed to the specified provider.
69 - *
70 - * @param providerId identity of the provider
71 - * @param description data describing the new topology
72 - */
73 - DefaultTopology(ProviderId providerId, GraphDescription description) {
74 - super(providerId);
75 - this.time = description.timestamp();
76 -
77 - // Build the graph
78 - this.graph = new DefaultTopologyGraph(description.vertexes(),
79 - description.edges());
80 -
81 - this.results = searchForShortestPaths();
82 - this.paths = buildPaths();
83 -
84 - this.clusterResults = searchForClusters();
85 - this.clusters = buildTopologyClusters();
86 -
87 - buildIndexes();
88 -
89 - this.broadcastSets = buildBroadcastSets();
90 - this.infrastructurePoints = findInfrastructurePoints();
91 - }
92 -
93 - @Override
94 - public long time() {
95 - return time;
96 - }
97 -
98 - @Override
99 - public int clusterCount() {
100 - return clusters.size();
101 - }
102 -
103 - @Override
104 - public int deviceCount() {
105 - return graph.getVertexes().size();
106 - }
107 -
108 - @Override
109 - public int linkCount() {
110 - return graph.getEdges().size();
111 - }
112 -
113 - @Override
114 - public int pathCount() {
115 - return paths.size();
116 - }
117 -
118 - /**
119 - * Returns the backing topology graph.
120 - *
121 - * @return topology graph
122 - */
123 - TopologyGraph getGraph() {
124 - return graph;
125 - }
126 -
127 - /**
128 - * Returns the set of topology clusters.
129 - *
130 - * @return set of clusters
131 - */
132 - Set<TopologyCluster> getClusters() {
133 - return ImmutableSet.copyOf(clusters.values());
134 - }
135 -
136 - /**
137 - * Returns the specified topology cluster.
138 - *
139 - * @param clusterId cluster identifier
140 - * @return topology cluster
141 - */
142 - TopologyCluster getCluster(ClusterId clusterId) {
143 - return clusters.get(clusterId);
144 - }
145 -
146 - /**
147 - * Returns the topology cluster that contains the given device.
148 - *
149 - * @param deviceId device identifier
150 - * @return topology cluster
151 - */
152 - TopologyCluster getCluster(DeviceId deviceId) {
153 - return clustersByDevice.get(deviceId);
154 - }
155 -
156 - /**
157 - * Returns the set of cluster devices.
158 - *
159 - * @param cluster topology cluster
160 - * @return cluster devices
161 - */
162 - Set<DeviceId> getClusterDevices(TopologyCluster cluster) {
163 - return devicesByCluster.get(cluster);
164 - }
165 -
166 - /**
167 - * Returns the set of cluster links.
168 - *
169 - * @param cluster topology cluster
170 - * @return cluster links
171 - */
172 - Set<Link> getClusterLinks(TopologyCluster cluster) {
173 - return linksByCluster.get(cluster);
174 - }
175 -
176 - /**
177 - * Indicates whether the given point is an infrastructure link end-point.
178 - *
179 - * @param connectPoint connection point
180 - * @return true if infrastructure
181 - */
182 - boolean isInfrastructure(ConnectPoint connectPoint) {
183 - return infrastructurePoints.contains(connectPoint);
184 - }
185 -
186 - /**
187 - * Indicates whether the given point is part of a broadcast set.
188 - *
189 - * @param connectPoint connection point
190 - * @return true if in broadcast set
191 - */
192 - boolean isBroadcastPoint(ConnectPoint connectPoint) {
193 - // Any non-infrastructure, i.e. edge points are assumed to be OK.
194 - if (!isInfrastructure(connectPoint)) {
195 - return true;
196 - }
197 -
198 - // Find the cluster to which the device belongs.
199 - TopologyCluster cluster = clustersByDevice.get(connectPoint.deviceId());
200 - if (cluster == null) {
201 - throw new IllegalArgumentException("No cluster found for device " + connectPoint.deviceId());
202 - }
203 -
204 - // If the broadcast set is null or empty, or if the point explicitly
205 - // belongs to it, return true;
206 - Set<ConnectPoint> points = broadcastSets.get(cluster.id());
207 - return points == null || points.isEmpty() || points.contains(connectPoint);
208 - }
209 -
210 - /**
211 - * Returns the size of the cluster broadcast set.
212 - *
213 - * @param clusterId cluster identifier
214 - * @return size of the cluster broadcast set
215 - */
216 - int broadcastSetSize(ClusterId clusterId) {
217 - return broadcastSets.get(clusterId).size();
218 - }
219 -
220 - /**
221 - * Returns the set of pre-computed shortest paths between source and
222 - * destination devices.
223 - *
224 - * @param src source device
225 - * @param dst destination device
226 - * @return set of shortest paths
227 - */
228 - Set<Path> getPaths(DeviceId src, DeviceId dst) {
229 - return paths.get(new PathKey(src, dst));
230 - }
231 -
232 - /**
233 - * Computes on-demand the set of shortest paths between source and
234 - * destination devices.
235 - *
236 - * @param src source device
237 - * @param dst destination device
238 - * @return set of shortest paths
239 - */
240 - Set<Path> getPaths(DeviceId src, DeviceId dst, LinkWeight weight) {
241 - GraphPathSearch.Result<TopologyVertex, TopologyEdge> result =
242 - DIJKSTRA.search(graph, new DefaultTopologyVertex(src),
243 - new DefaultTopologyVertex(dst), weight);
244 - ImmutableSet.Builder<Path> builder = ImmutableSet.builder();
245 - for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
246 - builder.add(networkPath(path));
247 - }
248 - return builder.build();
249 - }
250 -
251 -
252 - // Searches the graph for all shortest paths and returns the search results.
253 - private ImmutableMap<DeviceId, Result<TopologyVertex, TopologyEdge>> searchForShortestPaths() {
254 - ImmutableMap.Builder<DeviceId, Result<TopologyVertex, TopologyEdge>> builder = ImmutableMap.builder();
255 -
256 - // Search graph paths for each source to all destinations.
257 - LinkWeight weight = new HopCountLinkWeight(graph.getVertexes().size());
258 - for (TopologyVertex src : graph.getVertexes()) {
259 - builder.put(src.deviceId(), DIJKSTRA.search(graph, src, null, weight));
260 - }
261 - return builder.build();
262 - }
263 -
264 - // Builds network paths from the graph path search results
265 - private ImmutableSetMultimap<PathKey, Path> buildPaths() {
266 - Builder<PathKey, Path> builder = ImmutableSetMultimap.builder();
267 - for (DeviceId deviceId : results.keySet()) {
268 - Result<TopologyVertex, TopologyEdge> result = results.get(deviceId);
269 - for (org.onlab.graph.Path<TopologyVertex, TopologyEdge> path : result.paths()) {
270 - builder.put(new PathKey(path.src().deviceId(), path.dst().deviceId()),
271 - networkPath(path));
272 - }
273 - }
274 - return builder.build();
275 - }
276 -
277 - // Converts graph path to a network path with the same cost.
278 - private Path networkPath(org.onlab.graph.Path<TopologyVertex, TopologyEdge> path) {
279 - List<Link> links = new ArrayList<>();
280 - for (TopologyEdge edge : path.edges()) {
281 - links.add(edge.link());
282 - }
283 - return new DefaultPath(PID, links, path.cost());
284 - }
285 -
286 -
287 - // Searches for SCC clusters in the network topology graph using Tarjan
288 - // algorithm.
289 - private SCCResult<TopologyVertex, TopologyEdge> searchForClusters() {
290 - return TARJAN.search(graph, new NoIndirectLinksWeight());
291 - }
292 -
293 - // Builds the topology clusters and returns the id-cluster bindings.
294 - private ImmutableMap<ClusterId, TopologyCluster> buildTopologyClusters() {
295 - ImmutableMap.Builder<ClusterId, TopologyCluster> clusterBuilder = ImmutableMap.builder();
296 - SCCResult<TopologyVertex, TopologyEdge> result =
297 - TARJAN.search(graph, new NoIndirectLinksWeight());
298 -
299 - // Extract both vertexes and edges from the results; the lists form
300 - // pairs along the same index.
301 - List<Set<TopologyVertex>> clusterVertexes = result.clusterVertexes();
302 - List<Set<TopologyEdge>> clusterEdges = result.clusterEdges();
303 -
304 - // Scan over the lists and create a cluster from the results.
305 - for (int i = 0, n = result.clusterCount(); i < n; i++) {
306 - Set<TopologyVertex> vertexSet = clusterVertexes.get(i);
307 - Set<TopologyEdge> edgeSet = clusterEdges.get(i);
308 -
309 - ClusterId cid = ClusterId.clusterId(i);
310 - DefaultTopologyCluster cluster =
311 - new DefaultTopologyCluster(cid, vertexSet.size(), edgeSet.size(),
312 - findRoot(vertexSet).deviceId());
313 - clusterBuilder.put(cid, cluster);
314 - }
315 - return clusterBuilder.build();
316 - }
317 -
318 - // Finds the vertex whose device id is the lexicographical minimum in the
319 - // specified set.
320 - private TopologyVertex findRoot(Set<TopologyVertex> vertexSet) {
321 - TopologyVertex minVertex = null;
322 - for (TopologyVertex vertex : vertexSet) {
323 - if (minVertex == null ||
324 - minVertex.deviceId().toString()
325 - .compareTo(minVertex.deviceId().toString()) < 0) {
326 - minVertex = vertex;
327 - }
328 - }
329 - return minVertex;
330 - }
331 -
332 - // Processes a map of broadcast sets for each cluster.
333 - private ImmutableSetMultimap<ClusterId, ConnectPoint> buildBroadcastSets() {
334 - Builder<ClusterId, ConnectPoint> builder = ImmutableSetMultimap.builder();
335 - for (TopologyCluster cluster : clusters.values()) {
336 - addClusterBroadcastSet(cluster, builder);
337 - }
338 - return builder.build();
339 - }
340 -
341 - // Finds all broadcast points for the cluster. These are those connection
342 - // points which lie along the shortest paths between the cluster root and
343 - // all other devices within the cluster.
344 - private void addClusterBroadcastSet(TopologyCluster cluster,
345 - Builder<ClusterId, ConnectPoint> builder) {
346 - // Use the graph root search results to build the broadcast set.
347 - Result<TopologyVertex, TopologyEdge> result = results.get(cluster.root());
348 - for (Map.Entry<TopologyVertex, Set<TopologyEdge>> entry : result.parents().entrySet()) {
349 - TopologyVertex vertex = entry.getKey();
350 -
351 - // Ignore any parents that lead outside the cluster.
352 - if (clustersByDevice.get(vertex.deviceId()) != cluster) {
353 - continue;
354 - }
355 -
356 - // Ignore any back-link sets that are empty.
357 - Set<TopologyEdge> parents = entry.getValue();
358 - if (parents.isEmpty()) {
359 - continue;
360 - }
361 -
362 - // Use the first back-link source and destinations to add to the
363 - // broadcast set.
364 - Link link = parents.iterator().next().link();
365 - builder.put(cluster.id(), link.src());
366 - builder.put(cluster.id(), link.dst());
367 - }
368 - }
369 -
370 - // Collects and returns an set of all infrastructure link end-points.
371 - private ImmutableSet<ConnectPoint> findInfrastructurePoints() {
372 - ImmutableSet.Builder<ConnectPoint> builder = ImmutableSet.builder();
373 - for (TopologyEdge edge : graph.getEdges()) {
374 - builder.add(edge.link().src());
375 - builder.add(edge.link().dst());
376 - }
377 - return builder.build();
378 - }
379 -
380 - // Builds cluster-devices, cluster-links and device-cluster indexes.
381 - private void buildIndexes() {
382 - // Prepare the index builders
383 - ImmutableMap.Builder<DeviceId, TopologyCluster> clusterBuilder = ImmutableMap.builder();
384 - ImmutableSetMultimap.Builder<TopologyCluster, DeviceId> devicesBuilder = ImmutableSetMultimap.builder();
385 - ImmutableSetMultimap.Builder<TopologyCluster, Link> linksBuilder = ImmutableSetMultimap.builder();
386 -
387 - // Now scan through all the clusters
388 - for (TopologyCluster cluster : clusters.values()) {
389 - int i = cluster.id().index();
390 -
391 - // Scan through all the cluster vertexes.
392 - for (TopologyVertex vertex : clusterResults.clusterVertexes().get(i)) {
393 - devicesBuilder.put(cluster, vertex.deviceId());
394 - clusterBuilder.put(vertex.deviceId(), cluster);
395 - }
396 -
397 - // Scan through all the cluster edges.
398 - for (TopologyEdge edge : clusterResults.clusterEdges().get(i)) {
399 - linksBuilder.put(cluster, edge.link());
400 - }
401 - }
402 -
403 - // Finalize all indexes.
404 - clustersByDevice = clusterBuilder.build();
405 - devicesByCluster = devicesBuilder.build();
406 - linksByCluster = linksBuilder.build();
407 - }
408 -
409 - // Link weight for measuring link cost as hop count with indirect links
410 - // being as expensive as traversing the entire graph to assume the worst.
411 - private static class HopCountLinkWeight implements LinkWeight {
412 - private final int indirectLinkCost;
413 -
414 - HopCountLinkWeight(int indirectLinkCost) {
415 - this.indirectLinkCost = indirectLinkCost;
416 - }
417 -
418 - @Override
419 - public double weight(TopologyEdge edge) {
420 - // To force preference to use direct paths first, make indirect
421 - // links as expensive as the linear vertex traversal.
422 - return edge.link().type() == INDIRECT ? indirectLinkCost : 1;
423 - }
424 - }
425 -
426 - // Link weight for preventing traversal over indirect links.
427 - private static class NoIndirectLinksWeight implements LinkWeight {
428 - @Override
429 - public double weight(TopologyEdge edge) {
430 - return edge.link().type() == INDIRECT ? -1 : 1;
431 - }
432 - }
433 -
434 - @Override
435 - public String toString() {
436 - return toStringHelper(this)
437 - .add("time", time)
438 - .add("clusters", clusterCount())
439 - .add("devices", deviceCount())
440 - .add("links", linkCount())
441 - .add("pathCount", pathCount())
442 - .toString();
443 - }
444 -}
1 -package org.onlab.onos.store.topology.impl;
2 -
3 -import org.onlab.graph.AdjacencyListsGraph;
4 -import org.onlab.onos.net.topology.TopologyEdge;
5 -import org.onlab.onos.net.topology.TopologyGraph;
6 -import org.onlab.onos.net.topology.TopologyVertex;
7 -
8 -import java.util.Set;
9 -
10 -/**
11 - * Default implementation of an immutable topology graph based on a generic
12 - * implementation of adjacency lists graph.
13 - */
14 -public class DefaultTopologyGraph
15 - extends AdjacencyListsGraph<TopologyVertex, TopologyEdge>
16 - implements TopologyGraph {
17 -
18 - /**
19 - * Creates a topology graph comprising of the specified vertexes and edges.
20 - *
21 - * @param vertexes set of graph vertexes
22 - * @param edges set of graph edges
23 - */
24 - public DefaultTopologyGraph(Set<TopologyVertex> vertexes, Set<TopologyEdge> edges) {
25 - super(vertexes, edges);
26 - }
27 -
28 -}
1 -package org.onlab.onos.store.topology.impl;
2 -
3 -import static org.slf4j.LoggerFactory.getLogger;
4 -
5 -import java.util.List;
6 -import java.util.Set;
7 -
8 -import org.apache.felix.scr.annotations.Activate;
9 -import org.apache.felix.scr.annotations.Component;
10 -import org.apache.felix.scr.annotations.Deactivate;
11 -import org.apache.felix.scr.annotations.Service;
12 -import org.onlab.onos.event.Event;
13 -import org.onlab.onos.net.ConnectPoint;
14 -import org.onlab.onos.net.DeviceId;
15 -import org.onlab.onos.net.Link;
16 -import org.onlab.onos.net.Path;
17 -import org.onlab.onos.net.provider.ProviderId;
18 -import org.onlab.onos.net.topology.ClusterId;
19 -import org.onlab.onos.net.topology.GraphDescription;
20 -import org.onlab.onos.net.topology.LinkWeight;
21 -import org.onlab.onos.net.topology.Topology;
22 -import org.onlab.onos.net.topology.TopologyCluster;
23 -import org.onlab.onos.net.topology.TopologyEvent;
24 -import org.onlab.onos.net.topology.TopologyGraph;
25 -import org.onlab.onos.net.topology.TopologyStore;
26 -import org.onlab.onos.net.topology.TopologyStoreDelegate;
27 -import org.onlab.onos.store.AbstractStore;
28 -import org.slf4j.Logger;
29 -
30 -/**
31 - * TEMPORARY: Manages inventory of topology snapshots using distributed
32 - * structures implementation.
33 - */
34 -//FIXME: I LIE I AM NOT DISTRIBUTED
35 -@Component(immediate = true)
36 -@Service
37 -public class DistributedTopologyStore
38 -extends AbstractStore<TopologyEvent, TopologyStoreDelegate>
39 -implements TopologyStore {
40 -
41 - private final Logger log = getLogger(getClass());
42 -
43 - private volatile DefaultTopology current;
44 -
45 - @Activate
46 - public void activate() {
47 - log.info("Started");
48 - }
49 -
50 - @Deactivate
51 - public void deactivate() {
52 - log.info("Stopped");
53 - }
54 - @Override
55 - public Topology currentTopology() {
56 - return current;
57 - }
58 -
59 - @Override
60 - public boolean isLatest(Topology topology) {
61 - // Topology is current only if it is the same as our current topology
62 - return topology == current;
63 - }
64 -
65 - @Override
66 - public TopologyGraph getGraph(Topology topology) {
67 - return defaultTopology(topology).getGraph();
68 - }
69 -
70 - @Override
71 - public Set<TopologyCluster> getClusters(Topology topology) {
72 - return defaultTopology(topology).getClusters();
73 - }
74 -
75 - @Override
76 - public TopologyCluster getCluster(Topology topology, ClusterId clusterId) {
77 - return defaultTopology(topology).getCluster(clusterId);
78 - }
79 -
80 - @Override
81 - public Set<DeviceId> getClusterDevices(Topology topology, TopologyCluster cluster) {
82 - return defaultTopology(topology).getClusterDevices(cluster);
83 - }
84 -
85 - @Override
86 - public Set<Link> getClusterLinks(Topology topology, TopologyCluster cluster) {
87 - return defaultTopology(topology).getClusterLinks(cluster);
88 - }
89 -
90 - @Override
91 - public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst) {
92 - return defaultTopology(topology).getPaths(src, dst);
93 - }
94 -
95 - @Override
96 - public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst,
97 - LinkWeight weight) {
98 - return defaultTopology(topology).getPaths(src, dst, weight);
99 - }
100 -
101 - @Override
102 - public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
103 - return defaultTopology(topology).isInfrastructure(connectPoint);
104 - }
105 -
106 - @Override
107 - public boolean isBroadcastPoint(Topology topology, ConnectPoint connectPoint) {
108 - return defaultTopology(topology).isBroadcastPoint(connectPoint);
109 - }
110 -
111 - @Override
112 - public TopologyEvent updateTopology(ProviderId providerId,
113 - GraphDescription graphDescription,
114 - List<Event> reasons) {
115 - // First off, make sure that what we're given is indeed newer than
116 - // what we already have.
117 - if (current != null && graphDescription.timestamp() < current.time()) {
118 - return null;
119 - }
120 -
121 - // Have the default topology construct self from the description data.
122 - DefaultTopology newTopology =
123 - new DefaultTopology(providerId, graphDescription);
124 -
125 - // Promote the new topology to current and return a ready-to-send event.
126 - synchronized (this) {
127 - current = newTopology;
128 - return new TopologyEvent(TopologyEvent.Type.TOPOLOGY_CHANGED,
129 - current, reasons);
130 - }
131 - }
132 -
133 - // Validates the specified topology and returns it as a default
134 - private DefaultTopology defaultTopology(Topology topology) {
135 - if (topology instanceof DefaultTopology) {
136 - return (DefaultTopology) topology;
137 - }
138 - throw new IllegalArgumentException("Topology class " + topology.getClass() +
139 - " not supported");
140 - }
141 -
142 -}
1 -package org.onlab.onos.store.topology.impl;
2 -
3 -import org.onlab.onos.net.DeviceId;
4 -
5 -import java.util.Objects;
6 -
7 -/**
8 - * Key for filing pre-computed paths between source and destination devices.
9 - */
10 -class PathKey {
11 - private final DeviceId src;
12 - private final DeviceId dst;
13 -
14 - /**
15 - * Creates a path key from the given source/dest pair.
16 - * @param src source device
17 - * @param dst destination device
18 - */
19 - PathKey(DeviceId src, DeviceId dst) {
20 - this.src = src;
21 - this.dst = dst;
22 - }
23 -
24 - @Override
25 - public int hashCode() {
26 - return Objects.hash(src, dst);
27 - }
28 -
29 - @Override
30 - public boolean equals(Object obj) {
31 - if (this == obj) {
32 - return true;
33 - }
34 - if (obj instanceof PathKey) {
35 - final PathKey other = (PathKey) obj;
36 - return Objects.equals(this.src, other.src) && Objects.equals(this.dst, other.dst);
37 - }
38 - return false;
39 - }
40 -}
1 -package org.onlab.onos.store.link.impl;
2 -
3 -import static org.junit.Assert.*;
4 -import static org.onlab.onos.net.DeviceId.deviceId;
5 -import static org.onlab.onos.net.Link.Type.*;
6 -import static org.onlab.onos.net.LinkKey.linkKey;
7 -import static org.onlab.onos.net.link.LinkEvent.Type.*;
8 -
9 -import java.util.HashMap;
10 -import java.util.Map;
11 -import java.util.Set;
12 -import java.util.concurrent.CountDownLatch;
13 -import java.util.concurrent.TimeUnit;
14 -
15 -import org.junit.After;
16 -import org.junit.AfterClass;
17 -import org.junit.Before;
18 -import org.junit.BeforeClass;
19 -import org.junit.Ignore;
20 -import org.junit.Test;
21 -import org.onlab.onos.net.ConnectPoint;
22 -import org.onlab.onos.net.DeviceId;
23 -import org.onlab.onos.net.Link;
24 -import org.onlab.onos.net.LinkKey;
25 -import org.onlab.onos.net.PortNumber;
26 -import org.onlab.onos.net.Link.Type;
27 -import org.onlab.onos.net.link.DefaultLinkDescription;
28 -import org.onlab.onos.net.link.LinkEvent;
29 -import org.onlab.onos.net.link.LinkStoreDelegate;
30 -import org.onlab.onos.net.provider.ProviderId;
31 -import org.onlab.onos.store.common.StoreManager;
32 -import org.onlab.onos.store.common.StoreService;
33 -import org.onlab.onos.store.common.TestStoreManager;
34 -import com.google.common.collect.Iterables;
35 -import com.hazelcast.config.Config;
36 -import com.hazelcast.core.Hazelcast;
37 -
38 -/**
39 - * Test of the Hazelcast based distributed LinkStore implementation.
40 - */
41 -public class DistributedLinkStoreTest {
42 -
43 - private static final ProviderId PID = new ProviderId("of", "foo");
44 - private static final DeviceId DID1 = deviceId("of:foo");
45 - private static final DeviceId DID2 = deviceId("of:bar");
46 -
47 - private static final PortNumber P1 = PortNumber.portNumber(1);
48 - private static final PortNumber P2 = PortNumber.portNumber(2);
49 - private static final PortNumber P3 = PortNumber.portNumber(3);
50 -
51 - private StoreManager storeManager;
52 -
53 - private DistributedLinkStore linkStore;
54 -
55 - @BeforeClass
56 - public static void setUpBeforeClass() throws Exception {
57 - }
58 -
59 - @AfterClass
60 - public static void tearDownAfterClass() throws Exception {
61 - }
62 -
63 - @Before
64 - public void setUp() throws Exception {
65 - // TODO should find a way to clean Hazelcast instance without shutdown.
66 - Config config = TestStoreManager.getTestConfig();
67 -
68 - storeManager = new TestStoreManager(Hazelcast.newHazelcastInstance(config));
69 - storeManager.activate();
70 -
71 - linkStore = new TestDistributedLinkStore(storeManager);
72 - linkStore.activate();
73 - }
74 -
75 - @After
76 - public void tearDown() throws Exception {
77 - linkStore.deactivate();
78 - storeManager.deactivate();
79 - }
80 -
81 - private void putLink(DeviceId srcId, PortNumber srcNum,
82 - DeviceId dstId, PortNumber dstNum, Type type) {
83 - ConnectPoint src = new ConnectPoint(srcId, srcNum);
84 - ConnectPoint dst = new ConnectPoint(dstId, dstNum);
85 - linkStore.createOrUpdateLink(PID, new DefaultLinkDescription(src, dst, type));
86 - }
87 -
88 - private void putLink(LinkKey key, Type type) {
89 - putLink(key.src().deviceId(), key.src().port(),
90 - key.dst().deviceId(), key.dst().port(),
91 - type);
92 - }
93 -
94 - private static void assertLink(DeviceId srcId, PortNumber srcNum,
95 - DeviceId dstId, PortNumber dstNum, Type type,
96 - Link link) {
97 - assertEquals(srcId, link.src().deviceId());
98 - assertEquals(srcNum, link.src().port());
99 - assertEquals(dstId, link.dst().deviceId());
100 - assertEquals(dstNum, link.dst().port());
101 - assertEquals(type, link.type());
102 - }
103 -
104 - private static void assertLink(LinkKey key, Type type, Link link) {
105 - assertLink(key.src().deviceId(), key.src().port(),
106 - key.dst().deviceId(), key.dst().port(),
107 - type, link);
108 - }
109 -
110 - @Test
111 - public final void testGetLinkCount() {
112 - assertEquals("initialy empty", 0, linkStore.getLinkCount());
113 -
114 - putLink(DID1, P1, DID2, P2, DIRECT);
115 - putLink(DID2, P2, DID1, P1, DIRECT);
116 - putLink(DID1, P1, DID2, P2, DIRECT);
117 -
118 - assertEquals("expecting 2 unique link", 2, linkStore.getLinkCount());
119 - }
120 -
121 - @Test
122 - public final void testGetLinks() {
123 - assertEquals("initialy empty", 0,
124 - Iterables.size(linkStore.getLinks()));
125 -
126 - LinkKey linkId1 = linkKey(new ConnectPoint(DID1, P1), new ConnectPoint(DID2, P2));
127 - LinkKey linkId2 = linkKey(new ConnectPoint(DID2, P2), new ConnectPoint(DID1, P1));
128 -
129 - putLink(linkId1, DIRECT);
130 - putLink(linkId2, DIRECT);
131 - putLink(linkId1, DIRECT);
132 -
133 - assertEquals("expecting 2 unique link", 2,
134 - Iterables.size(linkStore.getLinks()));
135 -
136 - Map<LinkKey, Link> links = new HashMap<>();
137 - for (Link link : linkStore.getLinks()) {
138 - links.put(linkKey(link), link);
139 - }
140 -
141 - assertLink(linkId1, DIRECT, links.get(linkId1));
142 - assertLink(linkId2, DIRECT, links.get(linkId2));
143 - }
144 -
145 - @Test
146 - public final void testGetDeviceEgressLinks() {
147 - LinkKey linkId1 = linkKey(new ConnectPoint(DID1, P1), new ConnectPoint(DID2, P2));
148 - LinkKey linkId2 = linkKey(new ConnectPoint(DID2, P2), new ConnectPoint(DID1, P1));
149 - LinkKey linkId3 = linkKey(new ConnectPoint(DID1, P2), new ConnectPoint(DID2, P3));
150 -
151 - putLink(linkId1, DIRECT);
152 - putLink(linkId2, DIRECT);
153 - putLink(linkId3, DIRECT);
154 -
155 - // DID1,P1 => DID2,P2
156 - // DID2,P2 => DID1,P1
157 - // DID1,P2 => DID2,P3
158 -
159 - Set<Link> links1 = linkStore.getDeviceEgressLinks(DID1);
160 - assertEquals(2, links1.size());
161 - // check
162 -
163 - Set<Link> links2 = linkStore.getDeviceEgressLinks(DID2);
164 - assertEquals(1, links2.size());
165 - assertLink(linkId2, DIRECT, links2.iterator().next());
166 - }
167 -
168 - @Test
169 - public final void testGetDeviceIngressLinks() {
170 - LinkKey linkId1 = linkKey(new ConnectPoint(DID1, P1), new ConnectPoint(DID2, P2));
171 - LinkKey linkId2 = linkKey(new ConnectPoint(DID2, P2), new ConnectPoint(DID1, P1));
172 - LinkKey linkId3 = linkKey(new ConnectPoint(DID1, P2), new ConnectPoint(DID2, P3));
173 -
174 - putLink(linkId1, DIRECT);
175 - putLink(linkId2, DIRECT);
176 - putLink(linkId3, DIRECT);
177 -
178 - // DID1,P1 => DID2,P2
179 - // DID2,P2 => DID1,P1
180 - // DID1,P2 => DID2,P3
181 -
182 - Set<Link> links1 = linkStore.getDeviceIngressLinks(DID2);
183 - assertEquals(2, links1.size());
184 - // check
185 -
186 - Set<Link> links2 = linkStore.getDeviceIngressLinks(DID1);
187 - assertEquals(1, links2.size());
188 - assertLink(linkId2, DIRECT, links2.iterator().next());
189 - }
190 -
191 - @Test
192 - public final void testGetLink() {
193 - ConnectPoint src = new ConnectPoint(DID1, P1);
194 - ConnectPoint dst = new ConnectPoint(DID2, P2);
195 - LinkKey linkId1 = linkKey(src, dst);
196 -
197 - putLink(linkId1, DIRECT);
198 -
199 - Link link = linkStore.getLink(src, dst);
200 - assertLink(linkId1, DIRECT, link);
201 -
202 - assertNull("There shouldn't be reverese link",
203 - linkStore.getLink(dst, src));
204 - }
205 -
206 - @Test
207 - public final void testGetEgressLinks() {
208 - final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
209 - final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
210 - LinkKey linkId1 = linkKey(d1P1, d2P2);
211 - LinkKey linkId2 = linkKey(d2P2, d1P1);
212 - LinkKey linkId3 = linkKey(new ConnectPoint(DID1, P2), new ConnectPoint(DID2, P3));
213 -
214 - putLink(linkId1, DIRECT);
215 - putLink(linkId2, DIRECT);
216 - putLink(linkId3, DIRECT);
217 -
218 - // DID1,P1 => DID2,P2
219 - // DID2,P2 => DID1,P1
220 - // DID1,P2 => DID2,P3
221 -
222 - Set<Link> links1 = linkStore.getEgressLinks(d1P1);
223 - assertEquals(1, links1.size());
224 - assertLink(linkId1, DIRECT, links1.iterator().next());
225 -
226 - Set<Link> links2 = linkStore.getEgressLinks(d2P2);
227 - assertEquals(1, links2.size());
228 - assertLink(linkId2, DIRECT, links2.iterator().next());
229 - }
230 -
231 - @Test
232 - public final void testGetIngressLinks() {
233 - final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
234 - final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
235 - LinkKey linkId1 = linkKey(d1P1, d2P2);
236 - LinkKey linkId2 = linkKey(d2P2, d1P1);
237 - LinkKey linkId3 = linkKey(new ConnectPoint(DID1, P2), new ConnectPoint(DID2, P3));
238 -
239 - putLink(linkId1, DIRECT);
240 - putLink(linkId2, DIRECT);
241 - putLink(linkId3, DIRECT);
242 -
243 - // DID1,P1 => DID2,P2
244 - // DID2,P2 => DID1,P1
245 - // DID1,P2 => DID2,P3
246 -
247 - Set<Link> links1 = linkStore.getIngressLinks(d2P2);
248 - assertEquals(1, links1.size());
249 - assertLink(linkId1, DIRECT, links1.iterator().next());
250 -
251 - Set<Link> links2 = linkStore.getIngressLinks(d1P1);
252 - assertEquals(1, links2.size());
253 - assertLink(linkId2, DIRECT, links2.iterator().next());
254 - }
255 -
256 - @Test
257 - public final void testCreateOrUpdateLink() {
258 - ConnectPoint src = new ConnectPoint(DID1, P1);
259 - ConnectPoint dst = new ConnectPoint(DID2, P2);
260 -
261 - // add link
262 - LinkEvent event = linkStore.createOrUpdateLink(PID,
263 - new DefaultLinkDescription(src, dst, INDIRECT));
264 -
265 - assertLink(DID1, P1, DID2, P2, INDIRECT, event.subject());
266 - assertEquals(LINK_ADDED, event.type());
267 -
268 - // update link type
269 - LinkEvent event2 = linkStore.createOrUpdateLink(PID,
270 - new DefaultLinkDescription(src, dst, DIRECT));
271 -
272 - assertLink(DID1, P1, DID2, P2, DIRECT, event2.subject());
273 - assertEquals(LINK_UPDATED, event2.type());
274 -
275 - // no change
276 - LinkEvent event3 = linkStore.createOrUpdateLink(PID,
277 - new DefaultLinkDescription(src, dst, DIRECT));
278 -
279 - assertNull("No change event expected", event3);
280 - }
281 -
282 - @Test
283 - public final void testRemoveLink() {
284 - final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
285 - final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
286 - LinkKey linkId1 = linkKey(d1P1, d2P2);
287 - LinkKey linkId2 = linkKey(d2P2, d1P1);
288 -
289 - putLink(linkId1, DIRECT);
290 - putLink(linkId2, DIRECT);
291 -
292 - // DID1,P1 => DID2,P2
293 - // DID2,P2 => DID1,P1
294 - // DID1,P2 => DID2,P3
295 -
296 - LinkEvent event = linkStore.removeLink(d1P1, d2P2);
297 - assertEquals(LINK_REMOVED, event.type());
298 - LinkEvent event2 = linkStore.removeLink(d1P1, d2P2);
299 - assertNull(event2);
300 -
301 - assertLink(linkId2, DIRECT, linkStore.getLink(d2P2, d1P1));
302 - }
303 -
304 - @Ignore("Ignore until Delegate spec. is clear.")
305 - @Test
306 - public final void testEvents() throws InterruptedException {
307 -
308 - final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
309 - final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
310 - final LinkKey linkId1 = linkKey(d1P1, d2P2);
311 -
312 - final CountDownLatch addLatch = new CountDownLatch(1);
313 - LinkStoreDelegate checkAdd = new LinkStoreDelegate() {
314 - @Override
315 - public void notify(LinkEvent event) {
316 - assertEquals(LINK_ADDED, event.type());
317 - assertLink(linkId1, INDIRECT, event.subject());
318 - addLatch.countDown();
319 - }
320 - };
321 - final CountDownLatch updateLatch = new CountDownLatch(1);
322 - LinkStoreDelegate checkUpdate = new LinkStoreDelegate() {
323 - @Override
324 - public void notify(LinkEvent event) {
325 - assertEquals(LINK_UPDATED, event.type());
326 - assertLink(linkId1, DIRECT, event.subject());
327 - updateLatch.countDown();
328 - }
329 - };
330 - final CountDownLatch removeLatch = new CountDownLatch(1);
331 - LinkStoreDelegate checkRemove = new LinkStoreDelegate() {
332 - @Override
333 - public void notify(LinkEvent event) {
334 - assertEquals(LINK_REMOVED, event.type());
335 - assertLink(linkId1, DIRECT, event.subject());
336 - removeLatch.countDown();
337 - }
338 - };
339 -
340 - linkStore.setDelegate(checkAdd);
341 - putLink(linkId1, INDIRECT);
342 - assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
343 -
344 - linkStore.unsetDelegate(checkAdd);
345 - linkStore.setDelegate(checkUpdate);
346 - putLink(linkId1, DIRECT);
347 - assertTrue("Update event fired", updateLatch.await(1, TimeUnit.SECONDS));
348 -
349 - linkStore.unsetDelegate(checkUpdate);
350 - linkStore.setDelegate(checkRemove);
351 - linkStore.removeLink(d1P1, d2P2);
352 - assertTrue("Remove event fired", removeLatch.await(1, TimeUnit.SECONDS));
353 - }
354 -
355 -
356 - class TestDistributedLinkStore extends DistributedLinkStore {
357 - TestDistributedLinkStore(StoreService storeService) {
358 - this.storeService = storeService;
359 - }
360 - }
361 -}
...@@ -17,35 +17,13 @@ ...@@ -17,35 +17,13 @@
17 <modules> 17 <modules>
18 <module>common</module> 18 <module>common</module>
19 <module>cluster</module> 19 <module>cluster</module>
20 - <module>net</module>
21 </modules> 20 </modules>
22 21
23 <dependencies> 22 <dependencies>
24 <dependency> 23 <dependency>
25 - <groupId>com.google.guava</groupId>
26 - <artifactId>guava</artifactId>
27 - </dependency>
28 - <dependency>
29 - <groupId>org.onlab.onos</groupId>
30 - <artifactId>onlab-misc</artifactId>
31 - </dependency>
32 - <dependency>
33 - <groupId>org.onlab.onos</groupId>
34 - <artifactId>onlab-junit</artifactId>
35 - </dependency>
36 - <dependency>
37 <groupId>com.hazelcast</groupId> 24 <groupId>com.hazelcast</groupId>
38 <artifactId>hazelcast</artifactId> 25 <artifactId>hazelcast</artifactId>
39 </dependency> 26 </dependency>
40 </dependencies> 27 </dependencies>
41 28
42 - <build>
43 - <plugins>
44 - <plugin>
45 - <groupId>org.apache.felix</groupId>
46 - <artifactId>maven-bundle-plugin</artifactId>
47 - </plugin>
48 - </plugins>
49 - </build>
50 -
51 </project> 29 </project>
......
1 <?xml version="1.0" encoding="UTF-8"?> 1 <?xml version="1.0" encoding="UTF-8"?>
2 -<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"> 2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
3 <modelVersion>4.0.0</modelVersion> 5 <modelVersion>4.0.0</modelVersion>
4 6
5 <parent> 7 <parent>
...@@ -19,24 +21,17 @@ ...@@ -19,24 +21,17 @@
19 <module>dist</module> 21 <module>dist</module>
20 <module>hz</module> 22 <module>hz</module>
21 <module>serializers</module> 23 <module>serializers</module>
22 - </modules> 24 + </modules>
23 25
24 <dependencies> 26 <dependencies>
25 <dependency> 27 <dependency>
26 - <groupId>com.google.guava</groupId>
27 - <artifactId>guava</artifactId>
28 - </dependency>
29 - <dependency>
30 - <groupId>org.onlab.onos</groupId>
31 - <artifactId>onlab-misc</artifactId>
32 - </dependency>
33 - <dependency>
34 <groupId>org.onlab.onos</groupId> 28 <groupId>org.onlab.onos</groupId>
35 - <artifactId>onlab-junit</artifactId> 29 + <artifactId>onos-api</artifactId>
36 </dependency> 30 </dependency>
31 +
37 <dependency> 32 <dependency>
38 - <groupId>com.hazelcast</groupId> 33 + <groupId>org.apache.felix</groupId>
39 - <artifactId>hazelcast</artifactId> 34 + <artifactId>org.apache.felix.scr.annotations</artifactId>
40 </dependency> 35 </dependency>
41 </dependencies> 36 </dependencies>
42 37
...@@ -44,7 +39,7 @@ ...@@ -44,7 +39,7 @@
44 <plugins> 39 <plugins>
45 <plugin> 40 <plugin>
46 <groupId>org.apache.felix</groupId> 41 <groupId>org.apache.felix</groupId>
47 - <artifactId>maven-bundle-plugin</artifactId> 42 + <artifactId>maven-scr-plugin</artifactId>
48 </plugin> 43 </plugin>
49 </plugins> 44 </plugins>
50 </build> 45 </build>
......
...@@ -18,14 +18,6 @@ ...@@ -18,14 +18,6 @@
18 18
19 <dependencies> 19 <dependencies>
20 <dependency> 20 <dependency>
21 - <groupId>org.onlab.onos</groupId>
22 - <artifactId>onos-api</artifactId>
23 - </dependency>
24 - <dependency>
25 - <groupId>org.apache.felix</groupId>
26 - <artifactId>org.apache.felix.scr.annotations</artifactId>
27 - </dependency>
28 - <dependency>
29 <groupId>com.esotericsoftware</groupId> 21 <groupId>com.esotericsoftware</groupId>
30 <artifactId>kryo</artifactId> 22 <artifactId>kryo</artifactId>
31 </dependency> 23 </dependency>
...@@ -36,13 +28,4 @@ ...@@ -36,13 +28,4 @@
36 </dependency> 28 </dependency>
37 </dependencies> 29 </dependencies>
38 30
39 - <build>
40 - <plugins>
41 - <plugin>
42 - <groupId>org.apache.felix</groupId>
43 - <artifactId>maven-scr-plugin</artifactId>
44 - </plugin>
45 - </plugins>
46 - </build>
47 -
48 </project> 31 </project>
......
1 +package org.onlab.onos.store.serializers;
2 +
3 +import org.onlab.onos.net.DeviceId;
4 +import org.onlab.onos.net.HostLocation;
5 +import org.onlab.onos.net.PortNumber;
6 +
7 +import com.esotericsoftware.kryo.Kryo;
8 +import com.esotericsoftware.kryo.Serializer;
9 +import com.esotericsoftware.kryo.io.Input;
10 +import com.esotericsoftware.kryo.io.Output;
11 +
12 +/**
13 +* Kryo Serializer for {@link HostLocation}.
14 +*/
15 +public class HostLocationSerializer extends Serializer<HostLocation> {
16 +
17 + /**
18 + * Creates {@link HostLocation} serializer instance.
19 + */
20 + public HostLocationSerializer() {
21 + // non-null, immutable
22 + super(false, true);
23 + }
24 +
25 + @Override
26 + public void write(Kryo kryo, Output output, HostLocation object) {
27 + kryo.writeClassAndObject(output, object.deviceId());
28 + kryo.writeClassAndObject(output, object.port());
29 + output.writeLong(object.time());
30 + }
31 +
32 + @Override
33 + public HostLocation read(Kryo kryo, Input input, Class<HostLocation> type) {
34 + DeviceId deviceId = (DeviceId) kryo.readClassAndObject(input);
35 + PortNumber portNumber = (PortNumber) kryo.readClassAndObject(input);
36 + long time = input.readLong();
37 + return new HostLocation(deviceId, portNumber, time);
38 + }
39 +
40 +}
...@@ -20,8 +20,7 @@ public class IpAddressSerializer extends Serializer<IpAddress> { ...@@ -20,8 +20,7 @@ public class IpAddressSerializer extends Serializer<IpAddress> {
20 } 20 }
21 21
22 @Override 22 @Override
23 - public void write(Kryo kryo, Output output, 23 + public void write(Kryo kryo, Output output, IpAddress object) {
24 - IpAddress object) {
25 byte[] octs = object.toOctets(); 24 byte[] octs = object.toOctets();
26 output.writeInt(octs.length); 25 output.writeInt(octs.length);
27 output.writeBytes(octs); 26 output.writeBytes(octs);
...@@ -29,11 +28,10 @@ public class IpAddressSerializer extends Serializer<IpAddress> { ...@@ -29,11 +28,10 @@ public class IpAddressSerializer extends Serializer<IpAddress> {
29 } 28 }
30 29
31 @Override 30 @Override
32 - public IpAddress read(Kryo kryo, Input input, 31 + public IpAddress read(Kryo kryo, Input input, Class<IpAddress> type) {
33 - Class<IpAddress> type) { 32 + final int octLen = input.readInt();
34 - int octLen = input.readInt();
35 byte[] octs = new byte[octLen]; 33 byte[] octs = new byte[octLen];
36 - input.read(octs); 34 + input.readBytes(octs);
37 int prefLen = input.readInt(); 35 int prefLen = input.readInt();
38 return IpAddress.valueOf(octs, prefLen); 36 return IpAddress.valueOf(octs, prefLen);
39 } 37 }
......
...@@ -34,7 +34,7 @@ public final class IpPrefixSerializer extends Serializer<IpPrefix> { ...@@ -34,7 +34,7 @@ public final class IpPrefixSerializer extends Serializer<IpPrefix> {
34 Class<IpPrefix> type) { 34 Class<IpPrefix> type) {
35 int octLen = input.readInt(); 35 int octLen = input.readInt();
36 byte[] octs = new byte[octLen]; 36 byte[] octs = new byte[octLen];
37 - input.read(octs); 37 + input.readBytes(octs);
38 int prefLen = input.readInt(); 38 int prefLen = input.readInt();
39 return IpPrefix.valueOf(octs, prefLen); 39 return IpPrefix.valueOf(octs, prefLen);
40 } 40 }
......
...@@ -17,6 +17,8 @@ import org.onlab.onos.net.DefaultPort; ...@@ -17,6 +17,8 @@ import org.onlab.onos.net.DefaultPort;
17 import org.onlab.onos.net.Device; 17 import org.onlab.onos.net.Device;
18 import org.onlab.onos.net.DeviceId; 18 import org.onlab.onos.net.DeviceId;
19 import org.onlab.onos.net.Element; 19 import org.onlab.onos.net.Element;
20 +import org.onlab.onos.net.HostId;
21 +import org.onlab.onos.net.HostLocation;
20 import org.onlab.onos.net.Link; 22 import org.onlab.onos.net.Link;
21 import org.onlab.onos.net.LinkKey; 23 import org.onlab.onos.net.LinkKey;
22 import org.onlab.onos.net.MastershipRole; 24 import org.onlab.onos.net.MastershipRole;
...@@ -24,16 +26,21 @@ import org.onlab.onos.net.Port; ...@@ -24,16 +26,21 @@ import org.onlab.onos.net.Port;
24 import org.onlab.onos.net.PortNumber; 26 import org.onlab.onos.net.PortNumber;
25 import org.onlab.onos.net.device.DefaultDeviceDescription; 27 import org.onlab.onos.net.device.DefaultDeviceDescription;
26 import org.onlab.onos.net.device.DefaultPortDescription; 28 import org.onlab.onos.net.device.DefaultPortDescription;
29 +import org.onlab.onos.net.host.DefaultHostDescription;
30 +import org.onlab.onos.net.host.HostDescription;
27 import org.onlab.onos.net.link.DefaultLinkDescription; 31 import org.onlab.onos.net.link.DefaultLinkDescription;
28 import org.onlab.onos.net.provider.ProviderId; 32 import org.onlab.onos.net.provider.ProviderId;
29 import org.onlab.onos.store.Timestamp; 33 import org.onlab.onos.store.Timestamp;
30 import org.onlab.packet.ChassisId; 34 import org.onlab.packet.ChassisId;
31 import org.onlab.packet.IpAddress; 35 import org.onlab.packet.IpAddress;
32 import org.onlab.packet.IpPrefix; 36 import org.onlab.packet.IpPrefix;
37 +import org.onlab.packet.MacAddress;
38 +import org.onlab.packet.VlanId;
33 import org.onlab.util.KryoPool; 39 import org.onlab.util.KryoPool;
34 40
35 import com.google.common.collect.ImmutableList; 41 import com.google.common.collect.ImmutableList;
36 import com.google.common.collect.ImmutableMap; 42 import com.google.common.collect.ImmutableMap;
43 +import com.google.common.collect.ImmutableSet;
37 44
38 public final class KryoPoolUtil { 45 public final class KryoPoolUtil {
39 46
...@@ -43,6 +50,8 @@ public final class KryoPoolUtil { ...@@ -43,6 +50,8 @@ public final class KryoPoolUtil {
43 public static final KryoPool MISC = KryoPool.newBuilder() 50 public static final KryoPool MISC = KryoPool.newBuilder()
44 .register(IpPrefix.class, new IpPrefixSerializer()) 51 .register(IpPrefix.class, new IpPrefixSerializer())
45 .register(IpAddress.class, new IpAddressSerializer()) 52 .register(IpAddress.class, new IpAddressSerializer())
53 + .register(MacAddress.class, new MacAddressSerializer())
54 + .register(VlanId.class)
46 .build(); 55 .build();
47 56
48 // TODO: Populate other classes 57 // TODO: Populate other classes
...@@ -53,6 +62,7 @@ public final class KryoPoolUtil { ...@@ -53,6 +62,7 @@ public final class KryoPoolUtil {
53 .register(MISC) 62 .register(MISC)
54 .register(ImmutableMap.class, new ImmutableMapSerializer()) 63 .register(ImmutableMap.class, new ImmutableMapSerializer())
55 .register(ImmutableList.class, new ImmutableListSerializer()) 64 .register(ImmutableList.class, new ImmutableListSerializer())
65 + .register(ImmutableSet.class, new ImmutableSetSerializer())
56 .register( 66 .register(
57 // 67 //
58 ArrayList.class, 68 ArrayList.class,
...@@ -73,8 +83,10 @@ public final class KryoPoolUtil { ...@@ -73,8 +83,10 @@ public final class KryoPoolUtil {
73 DefaultPortDescription.class, 83 DefaultPortDescription.class,
74 Element.class, 84 Element.class,
75 Link.Type.class, 85 Link.Type.class,
76 - Timestamp.class 86 + Timestamp.class,
77 - 87 + HostId.class,
88 + HostDescription.class,
89 + DefaultHostDescription.class
78 ) 90 )
79 .register(URI.class, new URISerializer()) 91 .register(URI.class, new URISerializer())
80 .register(NodeId.class, new NodeIdSerializer()) 92 .register(NodeId.class, new NodeIdSerializer())
...@@ -87,6 +99,7 @@ public final class KryoPoolUtil { ...@@ -87,6 +99,7 @@ public final class KryoPoolUtil {
87 .register(DefaultLink.class, new DefaultLinkSerializer()) 99 .register(DefaultLink.class, new DefaultLinkSerializer())
88 .register(MastershipTerm.class, new MastershipTermSerializer()) 100 .register(MastershipTerm.class, new MastershipTermSerializer())
89 .register(MastershipRole.class, new MastershipRoleSerializer()) 101 .register(MastershipRole.class, new MastershipRoleSerializer())
102 + .register(HostLocation.class, new HostLocationSerializer())
90 103
91 .build(); 104 .build();
92 105
......
1 +package org.onlab.onos.store.serializers;
2 +
3 +import org.onlab.packet.MacAddress;
4 +
5 +import com.esotericsoftware.kryo.Kryo;
6 +import com.esotericsoftware.kryo.Serializer;
7 +import com.esotericsoftware.kryo.io.Input;
8 +import com.esotericsoftware.kryo.io.Output;
9 +
10 +/**
11 + * Kryo Serializer for {@link MacAddress}.
12 + */
13 +public class MacAddressSerializer extends Serializer<MacAddress> {
14 +
15 + /**
16 + * Creates {@link MacAddress} serializer instance.
17 + */
18 + public MacAddressSerializer() {
19 + super(false, true);
20 + }
21 +
22 + @Override
23 + public void write(Kryo kryo, Output output, MacAddress object) {
24 + output.writeBytes(object.getAddress());
25 + }
26 +
27 + @Override
28 + public MacAddress read(Kryo kryo, Input input, Class<MacAddress> type) {
29 + return MacAddress.valueOf(input.readBytes(MacAddress.MAC_ADDRESS_LENGTH));
30 + }
31 +
32 +}
...@@ -18,26 +18,9 @@ ...@@ -18,26 +18,9 @@
18 18
19 <dependencies> 19 <dependencies>
20 <dependency> 20 <dependency>
21 - <groupId>org.onlab.onos</groupId>
22 - <artifactId>onos-api</artifactId>
23 - </dependency>
24 - <dependency>
25 - <groupId>org.apache.felix</groupId>
26 - <artifactId>org.apache.felix.scr.annotations</artifactId>
27 - </dependency>
28 - <dependency>
29 <groupId>org.apache.commons</groupId> 21 <groupId>org.apache.commons</groupId>
30 <artifactId>commons-lang3</artifactId> 22 <artifactId>commons-lang3</artifactId>
31 </dependency> 23 </dependency>
32 </dependencies> 24 </dependencies>
33 25
34 - <build>
35 - <plugins>
36 - <plugin>
37 - <groupId>org.apache.felix</groupId>
38 - <artifactId>maven-scr-plugin</artifactId>
39 - </plugin>
40 - </plugins>
41 - </build>
42 -
43 </project> 26 </project>
......
...@@ -5,8 +5,6 @@ import com.google.common.collect.ImmutableList; ...@@ -5,8 +5,6 @@ import com.google.common.collect.ImmutableList;
5 import com.google.common.collect.Maps; 5 import com.google.common.collect.Maps;
6 import com.google.common.collect.Sets; 6 import com.google.common.collect.Sets;
7 7
8 -import org.apache.commons.lang3.concurrent.ConcurrentException;
9 -import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
10 import org.apache.felix.scr.annotations.Activate; 8 import org.apache.felix.scr.annotations.Activate;
11 import org.apache.felix.scr.annotations.Component; 9 import org.apache.felix.scr.annotations.Component;
12 import org.apache.felix.scr.annotations.Deactivate; 10 import org.apache.felix.scr.annotations.Deactivate;
...@@ -36,6 +34,7 @@ import org.slf4j.Logger; ...@@ -36,6 +34,7 @@ import org.slf4j.Logger;
36 34
37 import java.util.ArrayList; 35 import java.util.ArrayList;
38 import java.util.Collections; 36 import java.util.Collections;
37 +import java.util.HashMap;
39 import java.util.HashSet; 38 import java.util.HashSet;
40 import java.util.Iterator; 39 import java.util.Iterator;
41 import java.util.List; 40 import java.util.List;
...@@ -72,8 +71,7 @@ public class SimpleDeviceStore ...@@ -72,8 +71,7 @@ public class SimpleDeviceStore
72 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found"; 71 public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
73 72
74 // collection of Description given from various providers 73 // collection of Description given from various providers
75 - private final ConcurrentMap<DeviceId, 74 + private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>>
76 - ConcurrentMap<ProviderId, DeviceDescriptions>>
77 deviceDescs = Maps.newConcurrentMap(); 75 deviceDescs = Maps.newConcurrentMap();
78 76
79 // cache of Device and Ports generated by compositing descriptions from providers 77 // cache of Device and Ports generated by compositing descriptions from providers
...@@ -118,15 +116,16 @@ public class SimpleDeviceStore ...@@ -118,15 +116,16 @@ public class SimpleDeviceStore
118 DeviceId deviceId, 116 DeviceId deviceId,
119 DeviceDescription deviceDescription) { 117 DeviceDescription deviceDescription) {
120 118
121 - ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs 119 + Map<ProviderId, DeviceDescriptions> providerDescs
122 - = getDeviceDescriptions(deviceId); 120 + = getOrCreateDeviceDescriptions(deviceId);
123 121
124 synchronized (providerDescs) { 122 synchronized (providerDescs) {
125 // locking per device 123 // locking per device
126 124
127 DeviceDescriptions descs 125 DeviceDescriptions descs
128 - = createIfAbsentUnchecked(providerDescs, providerId, 126 + = getOrCreateProviderDeviceDescriptions(providerDescs,
129 - new InitDeviceDescs(deviceDescription)); 127 + providerId,
128 + deviceDescription);
130 129
131 Device oldDevice = devices.get(deviceId); 130 Device oldDevice = devices.get(deviceId);
132 // update description 131 // update description
...@@ -193,8 +192,8 @@ public class SimpleDeviceStore ...@@ -193,8 +192,8 @@ public class SimpleDeviceStore
193 192
194 @Override 193 @Override
195 public DeviceEvent markOffline(DeviceId deviceId) { 194 public DeviceEvent markOffline(DeviceId deviceId) {
196 - ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs 195 + Map<ProviderId, DeviceDescriptions> providerDescs
197 - = getDeviceDescriptions(deviceId); 196 + = getOrCreateDeviceDescriptions(deviceId);
198 197
199 // locking device 198 // locking device
200 synchronized (providerDescs) { 199 synchronized (providerDescs) {
...@@ -219,7 +218,7 @@ public class SimpleDeviceStore ...@@ -219,7 +218,7 @@ public class SimpleDeviceStore
219 Device device = devices.get(deviceId); 218 Device device = devices.get(deviceId);
220 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); 219 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
221 220
222 - ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); 221 + Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
223 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); 222 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
224 223
225 List<DeviceEvent> events = new ArrayList<>(); 224 List<DeviceEvent> events = new ArrayList<>();
...@@ -288,12 +287,12 @@ public class SimpleDeviceStore ...@@ -288,12 +287,12 @@ public class SimpleDeviceStore
288 Map<PortNumber, Port> ports, 287 Map<PortNumber, Port> ports,
289 Set<PortNumber> processed) { 288 Set<PortNumber> processed) {
290 List<DeviceEvent> events = new ArrayList<>(); 289 List<DeviceEvent> events = new ArrayList<>();
291 - Iterator<PortNumber> iterator = ports.keySet().iterator(); 290 + Iterator<Entry<PortNumber, Port>> iterator = ports.entrySet().iterator();
292 while (iterator.hasNext()) { 291 while (iterator.hasNext()) {
293 - PortNumber portNumber = iterator.next(); 292 + Entry<PortNumber, Port> e = iterator.next();
293 + PortNumber portNumber = e.getKey();
294 if (!processed.contains(portNumber)) { 294 if (!processed.contains(portNumber)) {
295 - events.add(new DeviceEvent(PORT_REMOVED, device, 295 + events.add(new DeviceEvent(PORT_REMOVED, device, e.getValue()));
296 - ports.get(portNumber)));
297 iterator.remove(); 296 iterator.remove();
298 } 297 }
299 } 298 }
...@@ -307,10 +306,36 @@ public class SimpleDeviceStore ...@@ -307,10 +306,36 @@ public class SimpleDeviceStore
307 NewConcurrentHashMap.<PortNumber, Port>ifNeeded()); 306 NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
308 } 307 }
309 308
310 - private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions( 309 + private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions(
311 DeviceId deviceId) { 310 DeviceId deviceId) {
312 - return createIfAbsentUnchecked(deviceDescs, deviceId, 311 + Map<ProviderId, DeviceDescriptions> r;
313 - NewConcurrentHashMap.<ProviderId, DeviceDescriptions>ifNeeded()); 312 + r = deviceDescs.get(deviceId);
313 + if (r != null) {
314 + return r;
315 + }
316 + r = new HashMap<>();
317 + final Map<ProviderId, DeviceDescriptions> concurrentlyAdded;
318 + concurrentlyAdded = deviceDescs.putIfAbsent(deviceId, r);
319 + if (concurrentlyAdded != null) {
320 + return concurrentlyAdded;
321 + } else {
322 + return r;
323 + }
324 + }
325 +
326 + // Guarded by deviceDescs value (=Device lock)
327 + private DeviceDescriptions getOrCreateProviderDeviceDescriptions(
328 + Map<ProviderId, DeviceDescriptions> device,
329 + ProviderId providerId, DeviceDescription deltaDesc) {
330 +
331 + synchronized (device) {
332 + DeviceDescriptions r = device.get(providerId);
333 + if (r == null) {
334 + r = new DeviceDescriptions(deltaDesc);
335 + device.put(providerId, r);
336 + }
337 + return r;
338 + }
314 } 339 }
315 340
316 @Override 341 @Override
...@@ -319,12 +344,12 @@ public class SimpleDeviceStore ...@@ -319,12 +344,12 @@ public class SimpleDeviceStore
319 Device device = devices.get(deviceId); 344 Device device = devices.get(deviceId);
320 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); 345 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
321 346
322 - ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); 347 + Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
323 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); 348 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
324 349
325 synchronized (descsMap) { 350 synchronized (descsMap) {
326 DeviceDescriptions descs = descsMap.get(providerId); 351 DeviceDescriptions descs = descsMap.get(providerId);
327 - // assuming all providers must to give DeviceDescription 352 + // assuming all providers must give DeviceDescription first
328 checkArgument(descs != null, 353 checkArgument(descs != null,
329 "Device description for Device ID %s from Provider %s was not found", 354 "Device description for Device ID %s from Provider %s was not found",
330 deviceId, providerId); 355 deviceId, providerId);
...@@ -368,7 +393,7 @@ public class SimpleDeviceStore ...@@ -368,7 +393,7 @@ public class SimpleDeviceStore
368 393
369 @Override 394 @Override
370 public DeviceEvent removeDevice(DeviceId deviceId) { 395 public DeviceEvent removeDevice(DeviceId deviceId) {
371 - ConcurrentMap<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId); 396 + Map<ProviderId, DeviceDescriptions> descs = getOrCreateDeviceDescriptions(deviceId);
372 synchronized (descs) { 397 synchronized (descs) {
373 Device device = devices.remove(deviceId); 398 Device device = devices.remove(deviceId);
374 // should DEVICE_REMOVED carry removed ports? 399 // should DEVICE_REMOVED carry removed ports?
...@@ -391,7 +416,7 @@ public class SimpleDeviceStore ...@@ -391,7 +416,7 @@ public class SimpleDeviceStore
391 * @return Device instance 416 * @return Device instance
392 */ 417 */
393 private Device composeDevice(DeviceId deviceId, 418 private Device composeDevice(DeviceId deviceId,
394 - ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) { 419 + Map<ProviderId, DeviceDescriptions> providerDescs) {
395 420
396 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied"); 421 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
397 422
...@@ -432,14 +457,14 @@ public class SimpleDeviceStore ...@@ -432,14 +457,14 @@ public class SimpleDeviceStore
432 * 457 *
433 * @param device device the port is on 458 * @param device device the port is on
434 * @param number port number 459 * @param number port number
435 - * @param providerDescs Collection of Descriptions from multiple providers 460 + * @param descsMap Collection of Descriptions from multiple providers
436 * @return Port instance 461 * @return Port instance
437 */ 462 */
438 private Port composePort(Device device, PortNumber number, 463 private Port composePort(Device device, PortNumber number,
439 - ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) { 464 + Map<ProviderId, DeviceDescriptions> descsMap) {
440 465
441 - ProviderId primary = pickPrimaryPID(providerDescs); 466 + ProviderId primary = pickPrimaryPID(descsMap);
442 - DeviceDescriptions primDescs = providerDescs.get(primary); 467 + DeviceDescriptions primDescs = descsMap.get(primary);
443 // if no primary, assume not enabled 468 // if no primary, assume not enabled
444 // TODO: revisit this default port enabled/disabled behavior 469 // TODO: revisit this default port enabled/disabled behavior
445 boolean isEnabled = false; 470 boolean isEnabled = false;
...@@ -451,7 +476,7 @@ public class SimpleDeviceStore ...@@ -451,7 +476,7 @@ public class SimpleDeviceStore
451 annotations = merge(annotations, portDesc.annotations()); 476 annotations = merge(annotations, portDesc.annotations());
452 } 477 }
453 478
454 - for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) { 479 + for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
455 if (e.getKey().equals(primary)) { 480 if (e.getKey().equals(primary)) {
456 continue; 481 continue;
457 } 482 }
...@@ -473,10 +498,9 @@ public class SimpleDeviceStore ...@@ -473,10 +498,9 @@ public class SimpleDeviceStore
473 /** 498 /**
474 * @return primary ProviderID, or randomly chosen one if none exists 499 * @return primary ProviderID, or randomly chosen one if none exists
475 */ 500 */
476 - private ProviderId pickPrimaryPID( 501 + private ProviderId pickPrimaryPID(Map<ProviderId, DeviceDescriptions> descsMap) {
477 - ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
478 ProviderId fallBackPrimary = null; 502 ProviderId fallBackPrimary = null;
479 - for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) { 503 + for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
480 if (!e.getKey().isAncillary()) { 504 if (!e.getKey().isAncillary()) {
481 return e.getKey(); 505 return e.getKey();
482 } else if (fallBackPrimary == null) { 506 } else if (fallBackPrimary == null) {
...@@ -487,21 +511,6 @@ public class SimpleDeviceStore ...@@ -487,21 +511,6 @@ public class SimpleDeviceStore
487 return fallBackPrimary; 511 return fallBackPrimary;
488 } 512 }
489 513
490 - public static final class InitDeviceDescs
491 - implements ConcurrentInitializer<DeviceDescriptions> {
492 -
493 - private final DeviceDescription deviceDesc;
494 -
495 - public InitDeviceDescs(DeviceDescription deviceDesc) {
496 - this.deviceDesc = checkNotNull(deviceDesc);
497 - }
498 - @Override
499 - public DeviceDescriptions get() throws ConcurrentException {
500 - return new DeviceDescriptions(deviceDesc);
501 - }
502 - }
503 -
504 -
505 /** 514 /**
506 * Collection of Description of a Device and it's Ports given from a Provider. 515 * Collection of Description of a Device and it's Ports given from a Provider.
507 */ 516 */
......
...@@ -84,7 +84,7 @@ public class SimpleHostStore ...@@ -84,7 +84,7 @@ public class SimpleHostStore
84 descr.hwAddress(), 84 descr.hwAddress(),
85 descr.vlan(), 85 descr.vlan(),
86 descr.location(), 86 descr.location(),
87 - ImmutableSet.of(descr.ipAddress())); 87 + ImmutableSet.copyOf(descr.ipAddress()));
88 synchronized (this) { 88 synchronized (this) {
89 hosts.put(hostId, newhost); 89 hosts.put(hostId, newhost);
90 locations.put(descr.location(), newhost); 90 locations.put(descr.location(), newhost);
...@@ -101,12 +101,12 @@ public class SimpleHostStore ...@@ -101,12 +101,12 @@ public class SimpleHostStore
101 return new HostEvent(HOST_MOVED, host); 101 return new HostEvent(HOST_MOVED, host);
102 } 102 }
103 103
104 - if (host.ipAddresses().contains(descr.ipAddress())) { 104 + if (host.ipAddresses().containsAll(descr.ipAddress())) {
105 return null; 105 return null;
106 } 106 }
107 107
108 Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses()); 108 Set<IpPrefix> addresses = new HashSet<>(host.ipAddresses());
109 - addresses.add(descr.ipAddress()); 109 + addresses.addAll(descr.ipAddress());
110 StoredHost updated = new StoredHost(providerId, host.id(), 110 StoredHost updated = new StoredHost(providerId, host.id(),
111 host.mac(), host.vlan(), 111 host.mac(), host.vlan(),
112 descr.location(), addresses); 112 descr.location(), addresses);
......
1 package org.onlab.onos.store.trivial.impl; 1 package org.onlab.onos.store.trivial.impl;
2 2
3 import com.google.common.base.Function; 3 import com.google.common.base.Function;
4 -import com.google.common.base.Predicate;
5 import com.google.common.collect.FluentIterable; 4 import com.google.common.collect.FluentIterable;
6 import com.google.common.collect.HashMultimap; 5 import com.google.common.collect.HashMultimap;
7 import com.google.common.collect.SetMultimap; 6 import com.google.common.collect.SetMultimap;
8 7
9 -import org.apache.commons.lang3.concurrent.ConcurrentUtils;
10 import org.apache.felix.scr.annotations.Activate; 8 import org.apache.felix.scr.annotations.Activate;
11 import org.apache.felix.scr.annotations.Component; 9 import org.apache.felix.scr.annotations.Component;
12 import org.apache.felix.scr.annotations.Deactivate; 10 import org.apache.felix.scr.annotations.Deactivate;
...@@ -20,7 +18,6 @@ import org.onlab.onos.net.Link; ...@@ -20,7 +18,6 @@ import org.onlab.onos.net.Link;
20 import org.onlab.onos.net.SparseAnnotations; 18 import org.onlab.onos.net.SparseAnnotations;
21 import org.onlab.onos.net.Link.Type; 19 import org.onlab.onos.net.Link.Type;
22 import org.onlab.onos.net.LinkKey; 20 import org.onlab.onos.net.LinkKey;
23 -import org.onlab.onos.net.Provided;
24 import org.onlab.onos.net.link.DefaultLinkDescription; 21 import org.onlab.onos.net.link.DefaultLinkDescription;
25 import org.onlab.onos.net.link.LinkDescription; 22 import org.onlab.onos.net.link.LinkDescription;
26 import org.onlab.onos.net.link.LinkEvent; 23 import org.onlab.onos.net.link.LinkEvent;
...@@ -28,11 +25,12 @@ import org.onlab.onos.net.link.LinkStore; ...@@ -28,11 +25,12 @@ import org.onlab.onos.net.link.LinkStore;
28 import org.onlab.onos.net.link.LinkStoreDelegate; 25 import org.onlab.onos.net.link.LinkStoreDelegate;
29 import org.onlab.onos.net.provider.ProviderId; 26 import org.onlab.onos.net.provider.ProviderId;
30 import org.onlab.onos.store.AbstractStore; 27 import org.onlab.onos.store.AbstractStore;
31 -import org.onlab.util.NewConcurrentHashMap;
32 import org.slf4j.Logger; 28 import org.slf4j.Logger;
33 29
34 import java.util.Collections; 30 import java.util.Collections;
31 +import java.util.HashMap;
35 import java.util.HashSet; 32 import java.util.HashSet;
33 +import java.util.Map;
36 import java.util.Set; 34 import java.util.Set;
37 import java.util.Map.Entry; 35 import java.util.Map.Entry;
38 import java.util.concurrent.ConcurrentHashMap; 36 import java.util.concurrent.ConcurrentHashMap;
...@@ -47,6 +45,7 @@ import static org.onlab.onos.net.link.LinkEvent.Type.*; ...@@ -47,6 +45,7 @@ import static org.onlab.onos.net.link.LinkEvent.Type.*;
47 import static org.slf4j.LoggerFactory.getLogger; 45 import static org.slf4j.LoggerFactory.getLogger;
48 import static com.google.common.collect.Multimaps.synchronizedSetMultimap; 46 import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
49 import static com.google.common.base.Predicates.notNull; 47 import static com.google.common.base.Predicates.notNull;
48 +import static com.google.common.base.Verify.verifyNotNull;
50 49
51 /** 50 /**
52 * Manages inventory of infrastructure links using trivial in-memory structures 51 * Manages inventory of infrastructure links using trivial in-memory structures
...@@ -61,8 +60,7 @@ public class SimpleLinkStore ...@@ -61,8 +60,7 @@ public class SimpleLinkStore
61 private final Logger log = getLogger(getClass()); 60 private final Logger log = getLogger(getClass());
62 61
63 // Link inventory 62 // Link inventory
64 - private final ConcurrentMap<LinkKey, 63 + private final ConcurrentMap<LinkKey, Map<ProviderId, LinkDescription>>
65 - ConcurrentMap<ProviderId, LinkDescription>>
66 linkDescs = new ConcurrentHashMap<>(); 64 linkDescs = new ConcurrentHashMap<>();
67 65
68 // Link instance cache 66 // Link instance cache
...@@ -151,7 +149,7 @@ public class SimpleLinkStore ...@@ -151,7 +149,7 @@ public class SimpleLinkStore
151 LinkDescription linkDescription) { 149 LinkDescription linkDescription) {
152 LinkKey key = linkKey(linkDescription.src(), linkDescription.dst()); 150 LinkKey key = linkKey(linkDescription.src(), linkDescription.dst());
153 151
154 - ConcurrentMap<ProviderId, LinkDescription> descs = getLinkDescriptions(key); 152 + Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
155 synchronized (descs) { 153 synchronized (descs) {
156 final Link oldLink = links.get(key); 154 final Link oldLink = links.get(key);
157 // update description 155 // update description
...@@ -166,7 +164,7 @@ public class SimpleLinkStore ...@@ -166,7 +164,7 @@ public class SimpleLinkStore
166 164
167 // Guarded by linkDescs value (=locking each Link) 165 // Guarded by linkDescs value (=locking each Link)
168 private LinkDescription createOrUpdateLinkDescription( 166 private LinkDescription createOrUpdateLinkDescription(
169 - ConcurrentMap<ProviderId, LinkDescription> descs, 167 + Map<ProviderId, LinkDescription> descs,
170 ProviderId providerId, 168 ProviderId providerId,
171 LinkDescription linkDescription) { 169 LinkDescription linkDescription) {
172 170
...@@ -227,7 +225,7 @@ public class SimpleLinkStore ...@@ -227,7 +225,7 @@ public class SimpleLinkStore
227 @Override 225 @Override
228 public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) { 226 public LinkEvent removeLink(ConnectPoint src, ConnectPoint dst) {
229 final LinkKey key = linkKey(src, dst); 227 final LinkKey key = linkKey(src, dst);
230 - ConcurrentMap<ProviderId, LinkDescription> descs = getLinkDescriptions(key); 228 + Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
231 synchronized (descs) { 229 synchronized (descs) {
232 Link link = links.remove(key); 230 Link link = links.remove(key);
233 descs.clear(); 231 descs.clear();
...@@ -247,8 +245,8 @@ public class SimpleLinkStore ...@@ -247,8 +245,8 @@ public class SimpleLinkStore
247 /** 245 /**
248 * @return primary ProviderID, or randomly chosen one if none exists 246 * @return primary ProviderID, or randomly chosen one if none exists
249 */ 247 */
250 - private ProviderId pickPrimaryPID( 248 + // Guarded by linkDescs value (=locking each Link)
251 - ConcurrentMap<ProviderId, LinkDescription> providerDescs) { 249 + private ProviderId getBaseProviderId(Map<ProviderId, LinkDescription> providerDescs) {
252 250
253 ProviderId fallBackPrimary = null; 251 ProviderId fallBackPrimary = null;
254 for (Entry<ProviderId, LinkDescription> e : providerDescs.entrySet()) { 252 for (Entry<ProviderId, LinkDescription> e : providerDescs.entrySet()) {
...@@ -262,9 +260,10 @@ public class SimpleLinkStore ...@@ -262,9 +260,10 @@ public class SimpleLinkStore
262 return fallBackPrimary; 260 return fallBackPrimary;
263 } 261 }
264 262
265 - private Link composeLink(ConcurrentMap<ProviderId, LinkDescription> descs) { 263 + // Guarded by linkDescs value (=locking each Link)
266 - ProviderId primary = pickPrimaryPID(descs); 264 + private Link composeLink(Map<ProviderId, LinkDescription> descs) {
267 - LinkDescription base = descs.get(primary); 265 + ProviderId primary = getBaseProviderId(descs);
266 + LinkDescription base = descs.get(verifyNotNull(primary));
268 267
269 ConnectPoint src = base.src(); 268 ConnectPoint src = base.src();
270 ConnectPoint dst = base.dst(); 269 ConnectPoint dst = base.dst();
...@@ -289,9 +288,20 @@ public class SimpleLinkStore ...@@ -289,9 +288,20 @@ public class SimpleLinkStore
289 return new DefaultLink(primary , src, dst, type, annotations); 288 return new DefaultLink(primary , src, dst, type, annotations);
290 } 289 }
291 290
292 - private ConcurrentMap<ProviderId, LinkDescription> getLinkDescriptions(LinkKey key) { 291 + private Map<ProviderId, LinkDescription> getOrCreateLinkDescriptions(LinkKey key) {
293 - return ConcurrentUtils.createIfAbsentUnchecked(linkDescs, key, 292 + Map<ProviderId, LinkDescription> r;
294 - NewConcurrentHashMap.<ProviderId, LinkDescription>ifNeeded()); 293 + r = linkDescs.get(key);
294 + if (r != null) {
295 + return r;
296 + }
297 + r = new HashMap<>();
298 + final Map<ProviderId, LinkDescription> concurrentlyAdded;
299 + concurrentlyAdded = linkDescs.putIfAbsent(key, r);
300 + if (concurrentlyAdded == null) {
301 + return r;
302 + } else {
303 + return concurrentlyAdded;
304 + }
295 } 305 }
296 306
297 private final Function<LinkKey, Link> lookupLink = new LookupLink(); 307 private final Function<LinkKey, Link> lookupLink = new LookupLink();
...@@ -302,20 +312,11 @@ public class SimpleLinkStore ...@@ -302,20 +312,11 @@ public class SimpleLinkStore
302 private final class LookupLink implements Function<LinkKey, Link> { 312 private final class LookupLink implements Function<LinkKey, Link> {
303 @Override 313 @Override
304 public Link apply(LinkKey input) { 314 public Link apply(LinkKey input) {
305 - return links.get(input); 315 + if (input == null) {
306 - } 316 + return null;
307 - } 317 + } else {
308 - 318 + return links.get(input);
309 - private static final Predicate<Provided> IS_PRIMARY = new IsPrimary(); 319 + }
310 - private static final Predicate<Provided> isPrimary() {
311 - return IS_PRIMARY;
312 - }
313 -
314 - private static final class IsPrimary implements Predicate<Provided> {
315 -
316 - @Override
317 - public boolean apply(Provided input) {
318 - return !input.providerId().isAncillary();
319 } 320 }
320 } 321 }
321 } 322 }
......
1 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 1 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2 <features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" 2 <features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0"
3 name="onos-1.0.0"> 3 name="onos-1.0.0">
4 - <repository>mvn:org.onlab.onos/onos-features/1.0.0-SNAPSHOT/xml/features</repository> 4 + <repository>mvn:org.onlab.onos/onos-features/1.0.0-SNAPSHOT/xml/features
5 + </repository>
5 6
6 <feature name="onos-thirdparty-base" version="1.0.0" 7 <feature name="onos-thirdparty-base" version="1.0.0"
7 description="ONOS 3rd party dependencies"> 8 description="ONOS 3rd party dependencies">
...@@ -28,20 +29,22 @@ ...@@ -28,20 +29,22 @@
28 29
29 <bundle>mvn:org.onlab.onos/onlab-nio/1.0.0-SNAPSHOT</bundle> 30 <bundle>mvn:org.onlab.onos/onlab-nio/1.0.0-SNAPSHOT</bundle>
30 31
31 - <bundle>mvn:org.codehaus.jackson/jackson-core-asl/1.9.13</bundle> 32 + <bundle>mvn:org.codehaus.jackson/jackson-core-asl/1.9.13</bundle>
32 - <bundle>mvn:org.codehaus.jackson/jackson-mapper-asl/1.9.13</bundle> 33 + <bundle>mvn:org.codehaus.jackson/jackson-mapper-asl/1.9.13</bundle>
34 + <bundle>mvn:org.onlab.onos/onlab-thirdparty/1.0.0-SNAPSHOT</bundle>
33 </feature> 35 </feature>
34 36
35 <feature name="onos-thirdparty-web" version="1.0.0" 37 <feature name="onos-thirdparty-web" version="1.0.0"
36 description="ONOS 3rd party dependencies"> 38 description="ONOS 3rd party dependencies">
37 <feature>war</feature> 39 <feature>war</feature>
38 <bundle>mvn:com.fasterxml.jackson.core/jackson-core/2.4.2</bundle> 40 <bundle>mvn:com.fasterxml.jackson.core/jackson-core/2.4.2</bundle>
39 - <bundle>mvn:com.fasterxml.jackson.core/jackson-annotations/2.4.2</bundle> 41 + <bundle>mvn:com.fasterxml.jackson.core/jackson-annotations/2.4.2
42 + </bundle>
40 <bundle>mvn:com.fasterxml.jackson.core/jackson-databind/2.4.2</bundle> 43 <bundle>mvn:com.fasterxml.jackson.core/jackson-databind/2.4.2</bundle>
41 <bundle>mvn:com.sun.jersey/jersey-core/1.18.1</bundle> 44 <bundle>mvn:com.sun.jersey/jersey-core/1.18.1</bundle>
42 <bundle>mvn:com.sun.jersey/jersey-server/1.18.1</bundle> 45 <bundle>mvn:com.sun.jersey/jersey-server/1.18.1</bundle>
43 <bundle>mvn:com.sun.jersey/jersey-servlet/1.18.1</bundle> 46 <bundle>mvn:com.sun.jersey/jersey-servlet/1.18.1</bundle>
44 - 47 +
45 </feature> 48 </feature>
46 49
47 <feature name="onos-api" version="1.0.0" 50 <feature name="onos-api" version="1.0.0"
...@@ -95,7 +98,7 @@ ...@@ -95,7 +98,7 @@
95 </feature> 98 </feature>
96 99
97 <feature name="onos-openflow" version="1.0.0" 100 <feature name="onos-openflow" version="1.0.0"
98 - description="ONOS OpenFlow API, Controller &amp; Providers"> 101 + description="ONOS OpenFlow API, Controller &amp; Providers">
99 <feature>onos-api</feature> 102 <feature>onos-api</feature>
100 <bundle>mvn:io.netty/netty/3.9.2.Final</bundle> 103 <bundle>mvn:io.netty/netty/3.9.2.Final</bundle>
101 <bundle>mvn:org.onlab.onos/onos-of-api/1.0.0-SNAPSHOT</bundle> 104 <bundle>mvn:org.onlab.onos/onos-of-api/1.0.0-SNAPSHOT</bundle>
...@@ -159,4 +162,11 @@ ...@@ -159,4 +162,11 @@
159 <bundle>mvn:org.onlab.onos/onos-app-sdnip/1.0.0-SNAPSHOT</bundle> 162 <bundle>mvn:org.onlab.onos/onos-app-sdnip/1.0.0-SNAPSHOT</bundle>
160 </feature> 163 </feature>
161 164
165 + <feature name="onos-app-calendar" version="1.0.0"
166 + description="REST interface for scheduling intents from an external calendar">
167 + <feature>onos-api</feature>
168 + <feature>onos-thirdparty-web</feature>
169 + <bundle>mvn:org.onlab.onos/onos-app-calendar/1.0.0-SNAPSHOT</bundle>
170 + </feature>
171 +
162 </features> 172 </features>
......
...@@ -5,6 +5,7 @@ import org.onlab.onos.openflow.controller.driver.SwitchDriverSubHandshakeComplet ...@@ -5,6 +5,7 @@ import org.onlab.onos.openflow.controller.driver.SwitchDriverSubHandshakeComplet
5 import org.onlab.onos.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted; 5 import org.onlab.onos.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted;
6 import org.onlab.onos.openflow.controller.Dpid; 6 import org.onlab.onos.openflow.controller.Dpid;
7 import org.onlab.onos.openflow.controller.driver.AbstractOpenFlowSwitch; 7 import org.onlab.onos.openflow.controller.driver.AbstractOpenFlowSwitch;
8 +import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
8 import org.projectfloodlight.openflow.protocol.OFCircuitPortsReply; 9 import org.projectfloodlight.openflow.protocol.OFCircuitPortsReply;
9 import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest; 10 import org.projectfloodlight.openflow.protocol.OFCircuitPortsRequest;
10 import org.projectfloodlight.openflow.protocol.OFDescStatsReply; 11 import org.projectfloodlight.openflow.protocol.OFDescStatsReply;
...@@ -21,7 +22,6 @@ import org.projectfloodlight.openflow.protocol.oxm.OFOxmInPort; ...@@ -21,7 +22,6 @@ import org.projectfloodlight.openflow.protocol.oxm.OFOxmInPort;
21 import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigid; 22 import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigid;
22 import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigidBasic; 23 import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigidBasic;
23 import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigtype; 24 import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigtype;
24 -import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigtypeBasic;
25 import org.projectfloodlight.openflow.types.CircuitSignalID; 25 import org.projectfloodlight.openflow.types.CircuitSignalID;
26 import org.projectfloodlight.openflow.types.OFPort; 26 import org.projectfloodlight.openflow.types.OFPort;
27 import org.projectfloodlight.openflow.types.U8; 27 import org.projectfloodlight.openflow.types.U8;
...@@ -119,11 +119,12 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { ...@@ -119,11 +119,12 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch {
119 processHandshakeOFExperimenterPortDescRequest( 119 processHandshakeOFExperimenterPortDescRequest(
120 (OFCircuitPortsReply) m); 120 (OFCircuitPortsReply) m);
121 driverHandshakeComplete.set(true); 121 driverHandshakeComplete.set(true);
122 - /* try { 122 + try {
123 testMA(); 123 testMA();
124 + testReverseMA();
124 } catch (IOException e) { 125 } catch (IOException e) {
125 e.printStackTrace(); 126 e.printStackTrace();
126 - }*/ 127 + }
127 break; 128 break;
128 default: 129 default:
129 log.debug("Received message {} during switch-driver " + 130 log.debug("Received message {} during switch-driver " +
...@@ -163,22 +164,71 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { ...@@ -163,22 +164,71 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch {
163 "message " + 164 "message " +
164 "{}", 165 "{}",
165 circuitPortsRequest.toString()); 166 circuitPortsRequest.toString());
166 - channel.write(Collections.singletonList(circuitPortsRequest)); 167 + sendMsg(Collections.<OFMessage>singletonList(circuitPortsRequest));
167 } 168 }
168 169
169 170
170 - 171 + public static final U8 SIGNAL_TYPE = U8.of((short) 10);
171 - //todo for testing
172 - public static final U8 SIGNAL_TYPE = U8.of((short) 1);
173 private void testMA() throws IOException { 172 private void testMA() throws IOException {
174 log.debug("LINC OE *** Testing MA "); 173 log.debug("LINC OE *** Testing MA ");
175 - short lambda = 100; 174 + short lambda = 1;
176 - if (getId() == 0x0000ffffffffff02L) { 175 + if (getId() == 0x0000ffffffffff01L) {
177 final int inport = 10; 176 final int inport = 10;
178 final int outport = 20; 177 final int outport = 20;
179 //Circuit signal id 178 //Circuit signal id
180 CircuitSignalID sigID = getSignalID(lambda); 179 CircuitSignalID sigID = getSignalID(lambda);
181 180
181 + OFOxmOchSigidBasic ofOxmOchSigidBasic =
182 + factory().oxms().ochSigidBasic(sigID);
183 +
184 +
185 + //Match Port
186 + OFOxmInPort fieldPort = factory().oxms()
187 + .inPort(OFPort.of(inport));
188 + OFMatchV3 matchPort =
189 + factory()
190 + .buildMatchV3().
191 + setOxmList(OFOxmList.of(fieldPort)).build();
192 +
193 +
194 + // Set Action outport ,sigType and sigID
195 + List<OFAction> actionList = new ArrayList<>();
196 + OFAction actionOutPort =
197 + factory().actions().output(OFPort.of(outport),
198 + 0xffff);
199 +
200 + OFActionCircuit actionCircuit = factory()
201 + .actions()
202 + .circuit(ofOxmOchSigidBasic);
203 +
204 + actionList.add(actionCircuit);
205 + actionList.add(actionOutPort);
206 +
207 + OFInstruction instructionAction =
208 + factory().instructions().buildApplyActions()
209 + .setActions(actionList)
210 + .build();
211 + List<OFInstruction> instructions =
212 + Collections.singletonList(instructionAction);
213 +
214 + OFMessage opticalFlowEntry =
215 + factory().buildFlowAdd()
216 + .setMatch(matchPort)
217 + .setPriority(100)
218 + .setInstructions(instructions)
219 + .setXid(getNextTransactionId())
220 + .build();
221 + log.debug("Adding optical flow in sw {}", getStringId());
222 + List<OFMessage> msglist = new ArrayList<>(1);
223 + msglist.add(opticalFlowEntry);
224 + write(msglist);
225 + sendBarrier(true);
226 + } else if (getId() == 0x0000ffffffffff03L) {
227 + final int inport = 30;
228 + final int outport = 31;
229 + //Circuit signal id
230 + CircuitSignalID sigID = getSignalID(lambda);
231 +
182 OFOxmOchSigid fieldSigIDMatch = factory().oxms().ochSigid(sigID); 232 OFOxmOchSigid fieldSigIDMatch = factory().oxms().ochSigid(sigID);
183 OFOxmOchSigtype fieldSigType = factory() 233 OFOxmOchSigtype fieldSigType = factory()
184 .oxms() 234 .oxms()
...@@ -187,8 +237,119 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { ...@@ -187,8 +237,119 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch {
187 OFOxmOchSigidBasic ofOxmOchSigidBasic = 237 OFOxmOchSigidBasic ofOxmOchSigidBasic =
188 factory().oxms().ochSigidBasic(sigID); 238 factory().oxms().ochSigidBasic(sigID);
189 239
190 - OFOxmOchSigtypeBasic ofOxmOchSigtypeBasic = 240 + //Match Port,SigType,SigID
191 - factory().oxms().ochSigtypeBasic(SIGNAL_TYPE); 241 + OFOxmInPort fieldPort = factory()
242 + .oxms()
243 + .inPort(OFPort.of(inport));
244 + OFMatchV3 matchPort = factory()
245 + .buildMatchV3()
246 + .setOxmList(OFOxmList.of(fieldPort,
247 + fieldSigIDMatch,
248 + fieldSigType
249 + ))
250 + .build();
251 +
252 + // Set Action outport ,SigType, sigID
253 + List<OFAction> actionList = new ArrayList<>();
254 + OFAction actionOutPort =
255 + factory().actions().output(OFPort.of(outport),
256 + 0xffff);
257 +
258 + OFActionCircuit actionCircuit = factory()
259 + .actions()
260 + .circuit(ofOxmOchSigidBasic);
261 +
262 +
263 +
264 + //actionList.add(setActionSigType);
265 + actionList.add(actionCircuit);
266 + actionList.add(actionOutPort);
267 +
268 + OFInstruction instructionAction =
269 + factory().instructions().buildApplyActions()
270 + .setActions(actionList)
271 + .build();
272 + List<OFInstruction> instructions =
273 + Collections.singletonList(instructionAction);
274 +
275 + OFMessage opticalFlowEntry =
276 + factory().buildFlowAdd()
277 + .setMatch(matchPort)
278 + .setPriority(100)
279 + .setInstructions(instructions)
280 + .setXid(getNextTransactionId())
281 + .build();
282 + log.debug("Adding optical flow in sw {}", getStringId());
283 + List<OFMessage> msglist = new ArrayList<>(1);
284 + msglist.add(opticalFlowEntry);
285 + write(msglist);
286 + sendBarrier(true);
287 +
288 + } else if (getId() == 0x0000ffffffffff02L) {
289 + final int inport = 21;
290 + final int outport = 11;
291 + //Circuit signal id
292 + CircuitSignalID sigID = getSignalID(lambda);
293 +
294 + OFOxmOchSigid fieldSigIDMatch = factory().oxms().ochSigid(sigID);
295 + OFOxmOchSigtype fieldSigType = factory()
296 + .oxms()
297 + .ochSigtype(SIGNAL_TYPE);
298 +
299 +
300 + //Match Port, sig type and sig id
301 + OFOxmInPort fieldPort = factory()
302 + .oxms()
303 + .inPort(OFPort.of(inport));
304 + OFMatchV3 matchPort =
305 + factory().buildMatchV3()
306 + .setOxmList(OFOxmList.of(fieldPort,
307 + fieldSigIDMatch,
308 + fieldSigType
309 + ))
310 + .build();
311 +
312 + // Set Action outport
313 + List<OFAction> actionList = new ArrayList<>();
314 + OFAction actionOutPort =
315 + factory().actions().output(OFPort.of(outport),
316 + 0xffff);
317 +
318 + actionList.add(actionOutPort);
319 +
320 + OFInstruction instructionAction =
321 + factory().instructions().buildApplyActions()
322 + .setActions(actionList)
323 + .build();
324 + List<OFInstruction> instructions =
325 + Collections.singletonList(instructionAction);
326 +
327 + OFMessage opticalFlowEntry =
328 + factory().buildFlowAdd()
329 + .setMatch(matchPort)
330 + .setPriority(100)
331 + .setInstructions(instructions)
332 + .setXid(getNextTransactionId())
333 + .build();
334 + log.debug("Adding optical flow in sw {}", getStringId());
335 + List<OFMessage> msglist = new ArrayList<>(1);
336 + msglist.add(opticalFlowEntry);
337 + write(msglist);
338 + sendBarrier(true);
339 + }
340 +
341 + }
342 + private void testReverseMA() throws IOException {
343 + log.debug("LINC OE *** Testing MA ");
344 + short lambda = 1;
345 + if (getId() == 0x0000ffffffffff02L) {
346 + final int inport = 11;
347 + final int outport = 21;
348 + //Circuit signal id
349 + CircuitSignalID sigID = getSignalID(lambda);
350 +
351 + OFOxmOchSigidBasic ofOxmOchSigidBasic =
352 + factory().oxms().ochSigidBasic(sigID);
192 353
193 //Match Port 354 //Match Port
194 OFOxmInPort fieldPort = factory().oxms() 355 OFOxmInPort fieldPort = factory().oxms()
...@@ -196,27 +357,20 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { ...@@ -196,27 +357,20 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch {
196 OFMatchV3 matchPort = 357 OFMatchV3 matchPort =
197 factory() 358 factory()
198 .buildMatchV3(). 359 .buildMatchV3().
199 - setOxmList(OFOxmList.of(fieldPort, 360 + setOxmList(OFOxmList.of(fieldPort)).build();
200 - fieldSigType,
201 - fieldSigIDMatch)).build();
202 361
203 362
204 // Set Action outport ,sigType and sigID 363 // Set Action outport ,sigType and sigID
205 List<OFAction> actionList = new ArrayList<>(); 364 List<OFAction> actionList = new ArrayList<>();
206 OFAction actionOutPort = 365 OFAction actionOutPort =
207 factory().actions().output(OFPort.of(outport), 366 factory().actions().output(OFPort.of(outport),
208 - Short.MAX_VALUE); 367 + 0xffff);
209 368
210 OFActionCircuit actionCircuit = factory() 369 OFActionCircuit actionCircuit = factory()
211 .actions() 370 .actions()
212 .circuit(ofOxmOchSigidBasic); 371 .circuit(ofOxmOchSigidBasic);
213 - OFActionCircuit setActionSigType = factory()
214 - .actions()
215 - .circuit(ofOxmOchSigtypeBasic);
216 -
217 - actionList.add(actionOutPort);
218 - actionList.add(setActionSigType);
219 actionList.add(actionCircuit); 372 actionList.add(actionCircuit);
373 + actionList.add(actionOutPort);
220 374
221 OFInstruction instructionAction = 375 OFInstruction instructionAction =
222 factory().instructions().buildApplyActions() 376 factory().instructions().buildApplyActions()
...@@ -228,6 +382,7 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { ...@@ -228,6 +382,7 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch {
228 OFMessage opticalFlowEntry = 382 OFMessage opticalFlowEntry =
229 factory().buildFlowAdd() 383 factory().buildFlowAdd()
230 .setMatch(matchPort) 384 .setMatch(matchPort)
385 + .setPriority(100)
231 .setInstructions(instructions) 386 .setInstructions(instructions)
232 .setXid(getNextTransactionId()) 387 .setXid(getNextTransactionId())
233 .build(); 388 .build();
...@@ -235,9 +390,10 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { ...@@ -235,9 +390,10 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch {
235 List<OFMessage> msglist = new ArrayList<>(1); 390 List<OFMessage> msglist = new ArrayList<>(1);
236 msglist.add(opticalFlowEntry); 391 msglist.add(opticalFlowEntry);
237 write(msglist); 392 write(msglist);
393 + sendBarrier(true);
238 } else if (getId() == 0x0000ffffffffff03L) { 394 } else if (getId() == 0x0000ffffffffff03L) {
239 - final int inport = 21; 395 + final int inport = 31;
240 - final int outport = 22; 396 + final int outport = 30;
241 //Circuit signal id 397 //Circuit signal id
242 CircuitSignalID sigID = getSignalID(lambda); 398 CircuitSignalID sigID = getSignalID(lambda);
243 399
...@@ -249,9 +405,6 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { ...@@ -249,9 +405,6 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch {
249 OFOxmOchSigidBasic ofOxmOchSigidBasic = 405 OFOxmOchSigidBasic ofOxmOchSigidBasic =
250 factory().oxms().ochSigidBasic(sigID); 406 factory().oxms().ochSigidBasic(sigID);
251 407
252 - OFOxmOchSigtypeBasic ofOxmOchSigtypeBasic =
253 - factory().oxms().ochSigtypeBasic(SIGNAL_TYPE);
254 -
255 //Match Port,SigType,SigID 408 //Match Port,SigType,SigID
256 OFOxmInPort fieldPort = factory() 409 OFOxmInPort fieldPort = factory()
257 .oxms() 410 .oxms()
...@@ -259,27 +412,22 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { ...@@ -259,27 +412,22 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch {
259 OFMatchV3 matchPort = factory() 412 OFMatchV3 matchPort = factory()
260 .buildMatchV3() 413 .buildMatchV3()
261 .setOxmList(OFOxmList.of(fieldPort, 414 .setOxmList(OFOxmList.of(fieldPort,
262 - fieldSigType, 415 + fieldSigIDMatch,
263 - fieldSigIDMatch)) 416 + fieldSigType
417 + ))
264 .build(); 418 .build();
265 419
266 // Set Action outport ,SigType, sigID 420 // Set Action outport ,SigType, sigID
267 List<OFAction> actionList = new ArrayList<>(); 421 List<OFAction> actionList = new ArrayList<>();
268 OFAction actionOutPort = 422 OFAction actionOutPort =
269 factory().actions().output(OFPort.of(outport), 423 factory().actions().output(OFPort.of(outport),
270 - Short.MAX_VALUE); 424 + 0xffff);
271 -
272 - OFActionCircuit setActionSigType = factory()
273 - .actions()
274 - .circuit(ofOxmOchSigtypeBasic);
275 OFActionCircuit actionCircuit = factory() 425 OFActionCircuit actionCircuit = factory()
276 .actions() 426 .actions()
277 .circuit(ofOxmOchSigidBasic); 427 .circuit(ofOxmOchSigidBasic);
278 428
279 -
280 - actionList.add(actionOutPort);
281 - actionList.add(setActionSigType);
282 actionList.add(actionCircuit); 429 actionList.add(actionCircuit);
430 + actionList.add(actionOutPort);
283 431
284 OFInstruction instructionAction = 432 OFInstruction instructionAction =
285 factory().instructions().buildApplyActions() 433 factory().instructions().buildApplyActions()
...@@ -290,18 +438,20 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { ...@@ -290,18 +438,20 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch {
290 438
291 OFMessage opticalFlowEntry = 439 OFMessage opticalFlowEntry =
292 factory().buildFlowAdd() 440 factory().buildFlowAdd()
293 - .setMatch(matchPort) 441 + .setMatch(matchPort)
294 - .setInstructions(instructions) 442 + .setPriority(100)
295 - .setXid(getNextTransactionId()) 443 + .setInstructions(instructions)
296 - .build(); 444 + .setXid(getNextTransactionId())
445 + .build();
297 log.debug("Adding optical flow in sw {}", getStringId()); 446 log.debug("Adding optical flow in sw {}", getStringId());
298 List<OFMessage> msglist = new ArrayList<>(1); 447 List<OFMessage> msglist = new ArrayList<>(1);
299 msglist.add(opticalFlowEntry); 448 msglist.add(opticalFlowEntry);
300 write(msglist); 449 write(msglist);
450 + sendBarrier(true);
301 451
302 - } else if (getId() == 0x0000ffffffffff04L) { 452 + } else if (getId() == 0x0000ffffffffff01L) {
303 - final int inport = 23; 453 + final int inport = 20;
304 - final int outport = 11; 454 + final int outport = 10;
305 //Circuit signal id 455 //Circuit signal id
306 CircuitSignalID sigID = getSignalID(lambda); 456 CircuitSignalID sigID = getSignalID(lambda);
307 457
...@@ -318,15 +468,16 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { ...@@ -318,15 +468,16 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch {
318 OFMatchV3 matchPort = 468 OFMatchV3 matchPort =
319 factory().buildMatchV3() 469 factory().buildMatchV3()
320 .setOxmList(OFOxmList.of(fieldPort, 470 .setOxmList(OFOxmList.of(fieldPort,
321 - fieldSigType, 471 + fieldSigIDMatch,
322 - fieldSigIDMatch)) 472 + fieldSigType
473 + ))
323 .build(); 474 .build();
324 475
325 // Set Action outport 476 // Set Action outport
326 List<OFAction> actionList = new ArrayList<>(); 477 List<OFAction> actionList = new ArrayList<>();
327 OFAction actionOutPort = 478 OFAction actionOutPort =
328 factory().actions().output(OFPort.of(outport), 479 factory().actions().output(OFPort.of(outport),
329 - Short.MAX_VALUE); 480 + 0xffff);
330 481
331 actionList.add(actionOutPort); 482 actionList.add(actionOutPort);
332 483
...@@ -339,18 +490,21 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { ...@@ -339,18 +490,21 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch {
339 490
340 OFMessage opticalFlowEntry = 491 OFMessage opticalFlowEntry =
341 factory().buildFlowAdd() 492 factory().buildFlowAdd()
342 - .setMatch(matchPort) 493 + .setMatch(matchPort)
343 - .setInstructions(instructions) 494 + .setPriority(100)
344 - .setXid(getNextTransactionId()) 495 + .setInstructions(instructions)
345 - .build(); 496 + .setXid(getNextTransactionId())
497 + .build();
346 log.debug("Adding optical flow in sw {}", getStringId()); 498 log.debug("Adding optical flow in sw {}", getStringId());
347 List<OFMessage> msglist = new ArrayList<>(1); 499 List<OFMessage> msglist = new ArrayList<>(1);
348 msglist.add(opticalFlowEntry); 500 msglist.add(opticalFlowEntry);
349 write(msglist); 501 write(msglist);
502 + sendBarrier(true);
350 } 503 }
351 504
352 } 505 }
353 506
507 +
354 // Todo remove - for testing purpose only 508 // Todo remove - for testing purpose only
355 private static CircuitSignalID getSignalID(short lambda) { 509 private static CircuitSignalID getSignalID(short lambda) {
356 byte myGrid = 1; 510 byte myGrid = 1;
...@@ -365,9 +519,21 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch { ...@@ -365,9 +519,21 @@ public class OFOpticalSwitchImplLINC13 extends AbstractOpenFlowSwitch {
365 return signalID; 519 return signalID;
366 } 520 }
367 521
522 + private void sendBarrier(boolean finalBarrier) throws IOException {
523 + int xid = getNextTransactionId();
524 + if (finalBarrier) {
525 + barrierXidToWaitFor = xid;
526 + }
527 + OFBarrierRequest br = factory()
528 + .buildBarrierRequest()
529 + .setXid(xid)
530 + .build();
531 + sendMsg(br);
532 + }
533 +
368 @Override 534 @Override
369 public void write(OFMessage msg) { 535 public void write(OFMessage msg) {
370 - this.channel.write(msg); 536 + this.sendMsg(msg);
371 } 537 }
372 538
373 @Override 539 @Override
......
...@@ -107,7 +107,13 @@ ...@@ -107,7 +107,13 @@
107 </dependency> 107 </dependency>
108 108
109 <dependency> 109 <dependency>
110 - <groupId>commons-lang</groupId> 110 + <groupId>com.googlecode.concurrent-trees</groupId>
111 + <artifactId>concurrent-trees</artifactId>
112 + <version>2.4.0</version>
113 + </dependency>
114 +
115 + <dependency>
116 + <groupId>commons-lang</groupId>
111 <artifactId>commons-lang</artifactId> 117 <artifactId>commons-lang</artifactId>
112 <version>2.6</version> 118 <version>2.6</version>
113 </dependency> 119 </dependency>
...@@ -164,6 +170,12 @@ ...@@ -164,6 +170,12 @@
164 <scope>provided</scope> 170 <scope>provided</scope>
165 </dependency> 171 </dependency>
166 <dependency> 172 <dependency>
173 + <groupId>org.osgi</groupId>
174 + <artifactId>org.osgi.compendium</artifactId>
175 + <version>4.3.1</version>
176 + <scope>provided</scope>
177 + </dependency>
178 + <dependency>
167 <groupId>org.apache.felix</groupId> 179 <groupId>org.apache.felix</groupId>
168 <artifactId>org.apache.felix.scr.annotations</artifactId> 180 <artifactId>org.apache.felix.scr.annotations</artifactId>
169 <version>1.9.8</version> 181 <version>1.9.8</version>
...@@ -260,6 +272,13 @@ ...@@ -260,6 +272,13 @@
260 <artifactId>onos-of-api</artifactId> 272 <artifactId>onos-of-api</artifactId>
261 <version>${project.version}</version> 273 <version>${project.version}</version>
262 </dependency> 274 </dependency>
275 +
276 + <dependency>
277 + <groupId>org.onlab.onos</groupId>
278 + <artifactId>onlab-thirdparty</artifactId>
279 + <version>${project.version}</version>
280 + </dependency>
281 +
263 <dependency> 282 <dependency>
264 <groupId>org.onlab.onos</groupId> 283 <groupId>org.onlab.onos</groupId>
265 <artifactId>onos-of-api</artifactId> 284 <artifactId>onos-of-api</artifactId>
...@@ -502,7 +521,7 @@ ...@@ -502,7 +521,7 @@
502 <group> 521 <group>
503 <title>Core Subsystems</title> 522 <title>Core Subsystems</title>
504 <packages> 523 <packages>
505 - org.onlab.onos.impl:org.onlab.onos.cluster.impl:org.onlab.onos.net.device.impl:org.onlab.onos.net.link.impl:org.onlab.onos.net.host.impl:org.onlab.onos.net.topology.impl:org.onlab.onos.net.packet.impl:org.onlab.onos.net.flow.impl:org.onlab.onos.store.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.*:org.onlab.onos.net.intent.impl:org.onlab.onos.net.proxyarp.impl:org.onlab.onos.mastership.impl 524 + org.onlab.onos.impl:org.onlab.onos.cluster.impl:org.onlab.onos.net.device.impl:org.onlab.onos.net.link.impl:org.onlab.onos.net.host.impl:org.onlab.onos.net.topology.impl:org.onlab.onos.net.packet.impl:org.onlab.onos.net.flow.impl:org.onlab.onos.store.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.*:org.onlab.onos.net.intent.impl:org.onlab.onos.net.proxyarp.impl:org.onlab.onos.mastership.impl:org.onlab.onos.json:org.onlab.onos.json.*
506 </packages> 525 </packages>
507 </group> 526 </group>
508 <group> 527 <group>
...@@ -527,10 +546,11 @@ ...@@ -527,10 +546,11 @@
527 <group> 546 <group>
528 <title>Sample Applications</title> 547 <title>Sample Applications</title>
529 <packages> 548 <packages>
530 - org.onlab.onos.tvue:org.onlab.onos.fwd:org.onlab.onos.ifwd:org.onlab.onos.mobility:org.onlab.onos.proxyarp:org.onlab.onos.foo 549 + org.onlab.onos.tvue:org.onlab.onos.fwd:org.onlab.onos.ifwd:org.onlab.onos.mobility:org.onlab.onos.proxyarp:org.onlab.onos.foo:org.onlab.onos.calendar
531 </packages> 550 </packages>
532 </group> 551 </group>
533 </groups> 552 </groups>
553 + <excludePackageNames>org.onlab.thirdparty</excludePackageNames>
534 </configuration> 554 </configuration>
535 </plugin> 555 </plugin>
536 556
......
...@@ -161,10 +161,10 @@ public class FlowModBuilder { ...@@ -161,10 +161,10 @@ public class FlowModBuilder {
161 switch (l3m.subtype()) { 161 switch (l3m.subtype()) {
162 case IP_DST: 162 case IP_DST:
163 ip = (ModIPInstruction) i; 163 ip = (ModIPInstruction) i;
164 - return factory.actions().setNwDst(IPv4Address.of(ip.ip().toRealInt())); 164 + return factory.actions().setNwDst(IPv4Address.of(ip.ip().toInt()));
165 case IP_SRC: 165 case IP_SRC:
166 ip = (ModIPInstruction) i; 166 ip = (ModIPInstruction) i;
167 - return factory.actions().setNwSrc(IPv4Address.of(ip.ip().toRealInt())); 167 + return factory.actions().setNwSrc(IPv4Address.of(ip.ip().toInt()));
168 default: 168 default:
169 log.warn("Unimplemented action type {}.", l3m.subtype()); 169 log.warn("Unimplemented action type {}.", l3m.subtype());
170 break; 170 break;
...@@ -220,21 +220,21 @@ public class FlowModBuilder { ...@@ -220,21 +220,21 @@ public class FlowModBuilder {
220 case IPV4_DST: 220 case IPV4_DST:
221 ip = (IPCriterion) c; 221 ip = (IPCriterion) c;
222 if (ip.ip().isMasked()) { 222 if (ip.ip().isMasked()) {
223 - Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toRealInt()), 223 + Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toInt()),
224 - IPv4Address.of(ip.ip().netmask().toRealInt())); 224 + IPv4Address.of(ip.ip().netmask().toInt()));
225 mBuilder.setMasked(MatchField.IPV4_DST, maskedIp); 225 mBuilder.setMasked(MatchField.IPV4_DST, maskedIp);
226 } else { 226 } else {
227 - mBuilder.setExact(MatchField.IPV4_DST, IPv4Address.of(ip.ip().toRealInt())); 227 + mBuilder.setExact(MatchField.IPV4_DST, IPv4Address.of(ip.ip().toInt()));
228 } 228 }
229 break; 229 break;
230 case IPV4_SRC: 230 case IPV4_SRC:
231 ip = (IPCriterion) c; 231 ip = (IPCriterion) c;
232 if (ip.ip().isMasked()) { 232 if (ip.ip().isMasked()) {
233 - Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toRealInt()), 233 + Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toInt()),
234 - IPv4Address.of(ip.ip().netmask().toRealInt())); 234 + IPv4Address.of(ip.ip().netmask().toInt()));
235 mBuilder.setMasked(MatchField.IPV4_SRC, maskedIp); 235 mBuilder.setMasked(MatchField.IPV4_SRC, maskedIp);
236 } else { 236 } else {
237 - mBuilder.setExact(MatchField.IPV4_SRC, IPv4Address.of(ip.ip().toRealInt())); 237 + mBuilder.setExact(MatchField.IPV4_SRC, IPv4Address.of(ip.ip().toInt()));
238 } 238 }
239 break; 239 break;
240 case IP_PROTO: 240 case IP_PROTO:
......
...@@ -66,6 +66,7 @@ import org.slf4j.Logger; ...@@ -66,6 +66,7 @@ import org.slf4j.Logger;
66 * TODO: add 'fast discovery' mode: drop LLDPs in destination switch but listen 66 * TODO: add 'fast discovery' mode: drop LLDPs in destination switch but listen
67 * for flow_removed messages 67 * for flow_removed messages
68 */ 68 */
69 +@Deprecated
69 public class LinkDiscovery implements TimerTask { 70 public class LinkDiscovery implements TimerTask {
70 71
71 private final OpenFlowSwitch sw; 72 private final OpenFlowSwitch sw;
...@@ -339,9 +340,14 @@ public class LinkDiscovery implements TimerTask { ...@@ -339,9 +340,14 @@ public class LinkDiscovery implements TimerTask {
339 final Iterator<Integer> fastIterator = this.fastPorts.iterator(); 340 final Iterator<Integer> fastIterator = this.fastPorts.iterator();
340 while (fastIterator.hasNext()) { 341 while (fastIterator.hasNext()) {
341 final Integer portNumber = fastIterator.next(); 342 final Integer portNumber = fastIterator.next();
343 + OFPortDesc port = findPort(portNumber);
344 + if (port == null) {
345 + // port can be null
346 + // #removePort modifies `ports` outside synchronized block
347 + continue;
348 + }
342 final int probeCount = this.portProbeCount.get(portNumber) 349 final int probeCount = this.portProbeCount.get(portNumber)
343 .getAndIncrement(); 350 .getAndIncrement();
344 - OFPortDesc port = findPort(portNumber);
345 if (probeCount < LinkDiscovery.MAX_PROBE_COUNT) { 351 if (probeCount < LinkDiscovery.MAX_PROBE_COUNT) {
346 this.log.debug("sending fast probe to port"); 352 this.log.debug("sending fast probe to port");
347 353
......
...@@ -35,6 +35,7 @@ import org.slf4j.Logger; ...@@ -35,6 +35,7 @@ import org.slf4j.Logger;
35 * infrastructure links. 35 * infrastructure links.
36 */ 36 */
37 @Component(immediate = true) 37 @Component(immediate = true)
38 +@Deprecated
38 public class OpenFlowLinkProvider extends AbstractProvider implements LinkProvider { 39 public class OpenFlowLinkProvider extends AbstractProvider implements LinkProvider {
39 40
40 private final Logger log = getLogger(getClass()); 41 private final Logger log = getLogger(getClass());
......
...@@ -9,6 +9,10 @@ export KARAF_ZIP=${KARAF_ZIP:-~/Downloads/apache-karaf-3.0.1.zip} ...@@ -9,6 +9,10 @@ export KARAF_ZIP=${KARAF_ZIP:-~/Downloads/apache-karaf-3.0.1.zip}
9 export KARAF_TAR=${KARAF_TAR:-~/Downloads/apache-karaf-3.0.1.tar.gz} 9 export KARAF_TAR=${KARAF_TAR:-~/Downloads/apache-karaf-3.0.1.tar.gz}
10 export KARAF_DIST=$(basename $KARAF_ZIP .zip) 10 export KARAF_DIST=$(basename $KARAF_ZIP .zip)
11 11
12 +# Add ONOS-specific directories to the exectable PATH
13 +export PATH="$PATH:$ONOS_ROOT/tools/dev/bin:$ONOS_ROOT/tools/test/bin"
14 +export PATH="$PATH:$ONOS_ROOT/tools/build"
15 +
12 # Fallback build number us derived from from the user name & time 16 # Fallback build number us derived from from the user name & time
13 export BUILD_NUMBER=${BUILD_NUMBER:-$(id -un)~$(date +'%Y/%m/%d@%H:%M')} 17 export BUILD_NUMBER=${BUILD_NUMBER:-$(id -un)~$(date +'%Y/%m/%d@%H:%M')}
14 18
...@@ -21,6 +25,9 @@ export ONOS_STAGE=$ONOS_STAGE_ROOT/$ONOS_BITS ...@@ -21,6 +25,9 @@ export ONOS_STAGE=$ONOS_STAGE_ROOT/$ONOS_BITS
21 export ONOS_TAR=$ONOS_STAGE.tar.gz 25 export ONOS_TAR=$ONOS_STAGE.tar.gz
22 26
23 # Defaults for ONOS testing using remote machines. 27 # Defaults for ONOS testing using remote machines.
28 +if [ -n "${ONOS_CELL}" -a -f $ONOS_ROOT/tools/test/cells/${ONOS_CELL} ]; then
29 + . $ONOS_ROOT/tools/test/cells/${ONOS_CELL}
30 +fi
24 export ONOS_INSTALL_DIR="/opt/onos" # Installation directory on remote 31 export ONOS_INSTALL_DIR="/opt/onos" # Installation directory on remote
25 export OCI="${OCI:-192.168.56.101}" # ONOS Controller Instance 32 export OCI="${OCI:-192.168.56.101}" # ONOS Controller Instance
26 export ONOS_USER="sdn" # ONOS user on remote system 33 export ONOS_USER="sdn" # ONOS user on remote system
......
...@@ -6,7 +6,13 @@ ...@@ -6,7 +6,13 @@
6 export ONOS_ROOT=${ONOS_ROOT:-~/onos-next} 6 export ONOS_ROOT=${ONOS_ROOT:-~/onos-next}
7 7
8 # Setup some environmental context for developers 8 # Setup some environmental context for developers
9 -export JAVA_HOME=${JAVA_HOME:-$(/usr/libexec/java_home -v 1.7)} 9 +if [ -z "${JAVA_HOME}" ]; then
10 + if [ -x /usr/libexec/java_home ]; then
11 + export JAVA_HOME=$(/usr/libexec/java_home -v 1.7)
12 + elif [ -d /usr/lib/jvm/java-7-openjdk-amd64 ]; then
13 + export JAVA_HOME="/usr/lib/jvm/java-7-openjdk-amd64"
14 + fi
15 +fi
10 export MAVEN=${MAVEN:-~/Applications/apache-maven-3.2.2} 16 export MAVEN=${MAVEN:-~/Applications/apache-maven-3.2.2}
11 export KARAF=${KARAF:-~/Applications/apache-karaf-3.0.1} 17 export KARAF=${KARAF:-~/Applications/apache-karaf-3.0.1}
12 export KARAF_LOG=$KARAF/data/log/karaf.log 18 export KARAF_LOG=$KARAF/data/log/karaf.log
...@@ -15,7 +21,6 @@ export KARAF_LOG=$KARAF/data/log/karaf.log ...@@ -15,7 +21,6 @@ export KARAF_LOG=$KARAF/data/log/karaf.log
15 export PATH="$PATH:$ONOS_ROOT/tools/dev/bin:$ONOS_ROOT/tools/test/bin" 21 export PATH="$PATH:$ONOS_ROOT/tools/dev/bin:$ONOS_ROOT/tools/test/bin"
16 export PATH="$PATH:$ONOS_ROOT/tools/build" 22 export PATH="$PATH:$ONOS_ROOT/tools/build"
17 export PATH="$PATH:$MAVEN/bin:$KARAF/bin" 23 export PATH="$PATH:$MAVEN/bin:$KARAF/bin"
18 -export PATH="$PATH:."
19 24
20 # Convenience utility to warp to various ONOS source projects 25 # Convenience utility to warp to various ONOS source projects
21 # e.g. 'o api', 'o dev', 'o' 26 # e.g. 'o api', 'o dev', 'o'
...@@ -61,10 +66,10 @@ function cell { ...@@ -61,10 +66,10 @@ function cell {
61 if [ -n "$1" ]; then 66 if [ -n "$1" ]; then
62 [ ! -f $ONOS_ROOT/tools/test/cells/$1 ] && \ 67 [ ! -f $ONOS_ROOT/tools/test/cells/$1 ] && \
63 echo "No such cell: $1" >&2 && return 1 68 echo "No such cell: $1" >&2 && return 1
64 - unset OC1 OC2 OC3 OC4 OC5 OC6 OC7 OC8 OC9 OCN OCI 69 + unset ONOS_CELL ONOS_NIC ONOS_FEATURES
65 - . $ONOS_ROOT/tools/test/cells/$1 70 + unset OC0 OC1 OC2 OC3 OC4 OC5 OC6 OC7 OC8 OC9 OCN OCI
66 - export OCI=$OC1
67 export ONOS_CELL=$1 71 export ONOS_CELL=$1
72 + . $ONOS_ROOT/tools/test/cells/$1
68 cell 73 cell
69 else 74 else
70 env | egrep "ONOS_CELL" 75 env | egrep "ONOS_CELL"
......
1 +#!/bin/tcsh
2 +# ONOS developer csh/tcsh profile conveniences
3 +# Simply include in your own $HOME/.cshrc file. E.g.:
4 +#
5 +# setenv ONOS_ROOT $HOME/onos
6 +# if ( -f $ONOS_ROOT/tools/dev/onos.cshrc ) then
7 +# source $ONOS_ROOT/tools/dev/onos.cshrc
8 +# endif
9 +#
10 +
11 +# Root of the ONOS source tree
12 +if ( ! $?ONOS_ROOT ) then
13 + setenv ONOS_ROOT $HOME/onos
14 +endif
15 +
16 +# Setup some environmental context for developers
17 +if ( ! $?JAVA_HOME ) then
18 + if ( -x /usr/libexec/java_home ) then
19 + setenv JAVA_HOME `/usr/libexec/java_home -v 1.7`
20 + else if ( -d /usr/lib/jvm/java-7-openjdk-amd64 ) then
21 + setenv JAVA_HOME /usr/lib/jvm/java-7-openjdk-amd64
22 + endif
23 +endif
24 +if ( ! $?MAVEN ) then
25 + setenv MAVEN $HOME/Applications/apache-maven-3.2.2
26 +endif
27 +if ( ! $?KARAF ) then
28 + setenv KARAF $HOME/Applications/apache-karaf-3.0.1
29 +endif
30 +setenv KARAF_LOG $KARAF/data/log/karaf.log
31 +
32 +alias onos-setup-cell ' ( $ONOS_ROOT/tools/test/bin/onos-show-cell \!^ ) && setenv ONOS_CELL \!^'
33 +
34 +set path=( $path $ONOS_ROOT/tools/dev/bin $ONOS_ROOT/tools/test/bin )
35 +set path=( $path $ONOS_ROOT/tools/build )
36 +set path=( $path $KARAF/bin )
1 +#!/bin/bash
2 +# -----------------------------------------------------------------------------
3 +# List available ONOS cells configuration.
4 +# -----------------------------------------------------------------------------
5 +
6 +[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
7 +. $ONOS_ROOT/tools/build/envDefaults
8 +
9 +# Lists available cells
10 +for cell in $(ls -1 $ONOS_ROOT/tools/test/cells); do
11 + if [ ${cell} = "${ONOS_CELL}" ]; then
12 + cell_id="${cell} *"
13 + else
14 + cell_id="${cell}"
15 + fi
16 + cell_descr="$(grep '^#' $ONOS_ROOT/tools/test/cells/$cell | head -n 1)"
17 + printf "%-12s %s\n" "${cell_id}" "${cell_descr}"
18 +done
...@@ -6,4 +6,39 @@ ...@@ -6,4 +6,39 @@
6 [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1 6 [ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
7 . $ONOS_ROOT/tools/build/envDefaults 7 . $ONOS_ROOT/tools/build/envDefaults
8 8
9 -ssh $ONOS_USER@${1:-$OCI} "sudo service onos ${2:-status}" 9 +function print_usage {
10 + command_name=`basename $0`
11 + echo "Remotely administer the ONOS service on a single node or the current ONOS cell."
12 + echo
13 + echo "Usage: $command_name <TARGET> [COMMAND]"
14 + echo " $command_name [-h | --help]"
15 + echo "Options:"
16 + echo " TARGET The target of the command"
17 + echo " COMMAND The command to execute. Default value is 'status'"
18 + echo " [-h | --help] Print this help"
19 + echo ""
20 + echo "TARGET: <hostname | --cell>"
21 + echo " hostname Execute on the specified host name"
22 + echo " --cell Execute on the current ONOS cell"
23 + echo ""
24 + echo "COMMAND: [start|stop|restart|status]"
25 + echo ""
26 +}
27 +
28 +# Print usage
29 +if [ "${1}" = "-h" -o "${1}" = "--help" ]; then
30 + print_usage
31 + exit 0
32 +fi
33 +
34 +# Select the target
35 +if [ "${1}" = "--cell" ]; then
36 + nodes=$(env | sort | egrep "OC[0-9]+" | cut -d= -f2)
37 +else
38 + nodes=${1:-$OCI}
39 +fi
40 +
41 +# Execute the remote commands
42 +for node in $nodes; do
43 + ssh $ONOS_USER@${node} "sudo service onos ${2:-status}"
44 +done
......
1 +#!/bin/bash
2 +# -----------------------------------------------------------------------------
3 +# Print the configuration of an ONOS cell.
4 +# -----------------------------------------------------------------------------
5 +
6 +[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
7 +. $ONOS_ROOT/tools/build/envDefaults
8 +
9 +function print_usage {
10 + echo "Print the configuration of an ONOS cell."
11 + echo "If no arguments are specified, it will print the configuration for the default"
12 + echo "ONOS cell as specified in the 'ONOS_CELL' environmental variable."
13 + echo
14 + echo "Optional arguments:"
15 + echo " [cell-name] Print the configuration of 'cell-name'"
16 + echo " [-h | --help] Print this help"
17 +}
18 +
19 +if [ "${1}" = "-h" -o "${1}" = "--help" ]; then
20 + print_usage
21 + exit 0
22 +fi
23 +
24 +if [ -n "${1}" ]; then
25 + cell="${1}"
26 +else
27 + if [ -z "${ONOS_CELL}" ]; then
28 + echo "Environmental variable 'ONOS_CELL' is not defiled"
29 + exit 1
30 + else
31 + cell="${ONOS_CELL}"
32 + fi
33 +fi
34 +
35 +if [ ! -f $ONOS_ROOT/tools/test/cells/${cell} ]; then
36 + echo "No such cell: ${cell}"
37 + exit 1
38 +fi
39 +
40 +# Load the cell setup
41 +. $ONOS_ROOT/tools/test/cells/${cell}
42 +
43 +echo "ONOS_CELL=${ONOS_CELL}"
44 +echo "ONOS_NIC=${ONOS_NIC}"
45 +for n in {0..9}; do
46 + ocn="OC${n}"
47 + if [ -n "${!ocn}" ]; then
48 + echo "$ocn=${!ocn}"
49 + fi
50 +done
51 +echo "OCN=${OCN}"
52 +echo "OCI=${OCI}"
53 +echo "ONOS_FEATURES=${ONOS_FEATURES}"
1 # Local VirtualBox-based single ONOS instance & ONOS mininet box 1 # Local VirtualBox-based single ONOS instance & ONOS mininet box
2 2
3 +export ONOS_CELL="cbench"
4 +
3 export ONOS_NIC=192.168.56.* 5 export ONOS_NIC=192.168.56.*
4 export OC1="192.168.56.103" 6 export OC1="192.168.56.103"
5 export OCN="192.168.56.103" 7 export OCN="192.168.56.103"
8 +export OCI="${OC1}"
6 9
7 export ONOS_FEATURES="webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd" 10 export ONOS_FEATURES="webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd"
......
1 # Local VirtualBox-based ONOS instances 1,2 & ONOS mininet box 1 # Local VirtualBox-based ONOS instances 1,2 & ONOS mininet box
2 2
3 +export ONOS_CELL="local"
4 +
3 export ONOS_NIC=192.168.56.* 5 export ONOS_NIC=192.168.56.*
4 export OC1="192.168.56.101" 6 export OC1="192.168.56.101"
5 export OC2="192.168.56.102" 7 export OC2="192.168.56.102"
6 -
7 export OCN="192.168.56.103" 8 export OCN="192.168.56.103"
9 +export OCI="${OC1}"
10 +
11 +export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core,onos-cli,onos-openflow,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}"
......
1 # ProxMox-based cell of ONOS instance; no mininet-box 1 # ProxMox-based cell of ONOS instance; no mininet-box
2 2
3 -export ONOS_FEATURES="webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-mobility,onos-app-tvue,onos-app-proxyarp" 3 +export ONOS_CELL="office"
4 4
5 export ONOS_NIC="10.1.10.*" 5 export ONOS_NIC="10.1.10.*"
6 export OC1="10.1.10.223" 6 export OC1="10.1.10.223"
7 +export OCI="${OC1}"
8 +
9 +export ONOS_FEATURES="webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-mobility,onos-app-tvue,onos-app-proxyarp"
......
1 # ProxMox-based cell of ONOS instances 1,2 & ONOS mininet box 1 # ProxMox-based cell of ONOS instances 1,2 & ONOS mininet box
2 2
3 +export ONOS_CELL="prox"
4 +
3 export ONOS_NIC="10.1.9.*" 5 export ONOS_NIC="10.1.9.*"
4 export OC1="10.1.9.94" 6 export OC1="10.1.9.94"
5 export OC2="10.1.9.82" 7 export OC2="10.1.9.82"
6 -
7 export OCN="10.1.9.93" 8 export OCN="10.1.9.93"
9 +export OCI="${OC1}"
10 +
11 +export ONOS_FEATURES=""
......
1 # Local VirtualBox-based single ONOS instance & ONOS mininet box 1 # Local VirtualBox-based single ONOS instance & ONOS mininet box
2 2
3 +export ONOS_CELL="single"
4 +
3 export ONOS_NIC=192.168.56.* 5 export ONOS_NIC=192.168.56.*
4 export OC1="192.168.56.101" 6 export OC1="192.168.56.101"
5 export OCN="192.168.56.103" 7 export OCN="192.168.56.103"
8 +export OCI="${OC1}"
9 +
10 +export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}"
......
1 # Local VirtualBox-based ONOS instances 1,2,3 & ONOS mininet box 1 # Local VirtualBox-based ONOS instances 1,2,3 & ONOS mininet box
2 2
3 +export ONOS_CELL="triple"
4 +
3 export ONOS_NIC=192.168.56.* 5 export ONOS_NIC=192.168.56.*
4 export OC1="192.168.56.101" 6 export OC1="192.168.56.101"
5 export OC2="192.168.56.102" 7 export OC2="192.168.56.102"
6 export OC3="192.168.56.104" 8 export OC3="192.168.56.104"
7 -
8 export OCN="192.168.56.103" 9 export OCN="192.168.56.103"
10 +export OCI="${OC1}"
11 +
12 +export ONOS_FEATURES=""
......
...@@ -2,13 +2,15 @@ package org.onlab.packet; ...@@ -2,13 +2,15 @@ package org.onlab.packet;
2 2
3 import java.util.Arrays; 3 import java.util.Arrays;
4 4
5 +
6 +
5 /** 7 /**
6 * A class representing an IPv4 address. 8 * A class representing an IPv4 address.
7 * <p/> 9 * <p/>
8 * TODO this class is a clone of IpPrefix and still needs to be modified to 10 * TODO this class is a clone of IpPrefix and still needs to be modified to
9 * look more like an IpAddress. 11 * look more like an IpAddress.
10 */ 12 */
11 -public final class IpAddress { 13 +public final class IpAddress implements Comparable<IpAddress> {
12 14
13 // TODO a comparator for netmasks? E.g. for sorting by prefix match order. 15 // TODO a comparator for netmasks? E.g. for sorting by prefix match order.
14 16
...@@ -121,7 +123,7 @@ public final class IpAddress { ...@@ -121,7 +123,7 @@ public final class IpAddress {
121 123
122 int mask = DEFAULT_MASK; 124 int mask = DEFAULT_MASK;
123 if (parts.length == 2) { 125 if (parts.length == 2) {
124 - mask = Integer.valueOf(parts[1]); 126 + mask = Integer.parseInt(parts[1]);
125 if (mask > MAX_INET_MASK) { 127 if (mask > MAX_INET_MASK) {
126 throw new IllegalArgumentException( 128 throw new IllegalArgumentException(
127 "Value of subnet mask cannot exceed " 129 "Value of subnet mask cannot exceed "
...@@ -174,14 +176,6 @@ public final class IpAddress { ...@@ -174,14 +176,6 @@ public final class IpAddress {
174 * @return the IP address's value as an integer 176 * @return the IP address's value as an integer
175 */ 177 */
176 public int toInt() { 178 public int toInt() {
177 - int address = 0;
178 - for (int i = 0; i < INET_LEN; i++) {
179 - address |= octets[i] << ((INET_LEN - (i + 1)) * 8);
180 - }
181 - return address;
182 - }
183 -
184 - public int toRealInt() {
185 int val = 0; 179 int val = 0;
186 for (int i = 0; i < octets.length; i++) { 180 for (int i = 0; i < octets.length; i++) {
187 val <<= 8; 181 val <<= 8;
...@@ -191,6 +185,15 @@ public final class IpAddress { ...@@ -191,6 +185,15 @@ public final class IpAddress {
191 } 185 }
192 186
193 /** 187 /**
188 + * Converts the IP address to a /32 IP prefix.
189 + *
190 + * @return the new IP prefix
191 + */
192 + public IpPrefix toPrefix() {
193 + return IpPrefix.valueOf(octets, MAX_INET_MASK);
194 + }
195 +
196 + /**
194 * Helper for computing the mask value from CIDR. 197 * Helper for computing the mask value from CIDR.
195 * 198 *
196 * @return an integer bitmask 199 * @return an integer bitmask
...@@ -280,6 +283,13 @@ public final class IpAddress { ...@@ -280,6 +283,13 @@ public final class IpAddress {
280 } 283 }
281 284
282 @Override 285 @Override
286 + public int compareTo(IpAddress o) {
287 + Long lv = ((long) this.toInt()) & 0xffffffffL;
288 + Long rv = ((long) o.toInt()) & 0xffffffffL;
289 + return lv.compareTo(rv);
290 + }
291 +
292 + @Override
283 public int hashCode() { 293 public int hashCode() {
284 final int prime = 31; 294 final int prime = 31;
285 int result = 1; 295 int result = 1;
......
...@@ -120,7 +120,7 @@ public final class IpPrefix { ...@@ -120,7 +120,7 @@ public final class IpPrefix {
120 120
121 int mask = DEFAULT_MASK; 121 int mask = DEFAULT_MASK;
122 if (parts.length == 2) { 122 if (parts.length == 2) {
123 - mask = Integer.valueOf(parts[1]); 123 + mask = Integer.parseInt(parts[1]);
124 if (mask > MAX_INET_MASK) { 124 if (mask > MAX_INET_MASK) {
125 throw new IllegalArgumentException( 125 throw new IllegalArgumentException(
126 "Value of subnet mask cannot exceed " 126 "Value of subnet mask cannot exceed "
...@@ -173,14 +173,6 @@ public final class IpPrefix { ...@@ -173,14 +173,6 @@ public final class IpPrefix {
173 * @return the IP address's value as an integer 173 * @return the IP address's value as an integer
174 */ 174 */
175 public int toInt() { 175 public int toInt() {
176 - int address = 0;
177 - for (int i = 0; i < INET_LEN; i++) {
178 - address |= octets[i] << ((INET_LEN - (i + 1)) * 8);
179 - }
180 - return address;
181 - }
182 -
183 - public int toRealInt() {
184 int val = 0; 176 int val = 0;
185 for (int i = 0; i < octets.length; i++) { 177 for (int i = 0; i < octets.length; i++) {
186 val <<= 8; 178 val <<= 8;
......
1 +package org.onlab.util;
2 +
3 +import java.lang.reflect.Constructor;
4 +import java.lang.reflect.Field;
5 +import java.lang.reflect.InvocationTargetException;
6 +import java.lang.reflect.Method;
7 +
8 +
9 +/**
10 + * Utilities for testing.
11 + */
12 +public final class TestUtils {
13 +
14 + /**
15 + * Sets the field, bypassing scope restriction.
16 + *
17 + * @param subject Object where the field belongs
18 + * @param fieldName name of the field to set
19 + * @param value value to set to the field.
20 + * @param <T> subject type
21 + * @param <U> value type
22 + * @throws TestUtilsException if there are reflection errors while setting
23 + * the field
24 + */
25 + public static <T, U> void setField(T subject, String fieldName, U value)
26 + throws TestUtilsException {
27 + @SuppressWarnings("unchecked")
28 + Class<T> clazz = (Class<T>) subject.getClass();
29 + try {
30 + Field field = clazz.getDeclaredField(fieldName);
31 + field.setAccessible(true);
32 + field.set(subject, value);
33 + } catch (NoSuchFieldException | SecurityException |
34 + IllegalArgumentException | IllegalAccessException e) {
35 + throw new TestUtilsException("setField failed", e);
36 + }
37 + }
38 +
39 + /**
40 + * Gets the field, bypassing scope restriction.
41 + *
42 + * @param subject Object where the field belongs
43 + * @param fieldName name of the field to get
44 + * @return value of the field.
45 + * @param <T> subject type
46 + * @param <U> field value type
47 + * @throws TestUtilsException if there are reflection errors while getting
48 + * the field
49 + */
50 + public static <T, U> U getField(T subject, String fieldName)
51 + throws TestUtilsException {
52 + try {
53 + @SuppressWarnings("unchecked")
54 + Class<T> clazz = (Class<T>) subject.getClass();
55 + Field field = clazz.getDeclaredField(fieldName);
56 + field.setAccessible(true);
57 +
58 + @SuppressWarnings("unchecked")
59 + U result = (U) field.get(subject);
60 + return result;
61 + } catch (NoSuchFieldException | SecurityException |
62 + IllegalArgumentException | IllegalAccessException e) {
63 + throw new TestUtilsException("getField failed", e);
64 + }
65 + }
66 +
67 + /**
68 + * Calls the method, bypassing scope restriction.
69 + *
70 + * @param subject Object where the method belongs
71 + * @param methodName name of the method to call
72 + * @param paramTypes formal parameter type array
73 + * @param args arguments
74 + * @return return value or null if void
75 + * @param <T> subject type
76 + * @param <U> return value type
77 + * @throws TestUtilsException if there are reflection errors while calling
78 + * the method
79 + */
80 + public static <T, U> U callMethod(T subject, String methodName,
81 + Class<?>[] paramTypes, Object...args) throws TestUtilsException {
82 +
83 + try {
84 + @SuppressWarnings("unchecked")
85 + Class<T> clazz = (Class<T>) subject.getClass();
86 + final Method method;
87 + if (paramTypes == null || paramTypes.length == 0) {
88 + method = clazz.getDeclaredMethod(methodName);
89 + } else {
90 + method = clazz.getDeclaredMethod(methodName, paramTypes);
91 + }
92 + method.setAccessible(true);
93 +
94 + @SuppressWarnings("unchecked")
95 + U result = (U) method.invoke(subject, args);
96 + return result;
97 + } catch (NoSuchMethodException | SecurityException |
98 + IllegalAccessException | IllegalArgumentException |
99 + InvocationTargetException e) {
100 + throw new TestUtilsException("callMethod failed", e);
101 + }
102 + }
103 +
104 + /**
105 + * Calls the method, bypassing scope restriction.
106 + *
107 + * @param subject Object where the method belongs
108 + * @param methodName name of the method to call
109 + * @param paramType formal parameter type
110 + * @param arg argument
111 + * @return return value or null if void
112 + * @param <T> subject type
113 + * @param <U> return value type
114 + * @throws TestUtilsException if there are reflection errors while calling
115 + * the method
116 + */
117 + public static <T, U> U callMethod(T subject, String methodName,
118 + Class<?> paramType, Object arg) throws TestUtilsException {
119 + return callMethod(subject, methodName, new Class<?>[]{paramType}, arg);
120 + }
121 +
122 + /**
123 + * Triggers an allocation of an object of type <T> and forces a call to
124 + * the private constructor.
125 + *
126 + * @param constructor Constructor to call
127 + * @param <T> type of the object to create
128 + * @return created object of type <T>
129 + * @throws TestUtilsException if there are reflection errors while calling
130 + * the constructor
131 + */
132 + public static <T> T callConstructor(Constructor<T> constructor)
133 + throws TestUtilsException {
134 + try {
135 + constructor.setAccessible(true);
136 + return constructor.newInstance();
137 + } catch (InstantiationException | IllegalAccessException |
138 + InvocationTargetException error) {
139 + throw new TestUtilsException("callConstructor failed", error);
140 + }
141 + }
142 +
143 + /**
144 + * Avoid instantiation.
145 + */
146 + private TestUtils() {}
147 +
148 + /**
149 + * Exception that can be thrown if problems are encountered while executing
150 + * the utility method. These are usually problems accessing fields/methods
151 + * through reflection. The original exception can be found by examining the
152 + * cause.
153 + */
154 + public static class TestUtilsException extends Exception {
155 +
156 + private static final long serialVersionUID = 1L;
157 +
158 + /**
159 + * Constructs a new exception with the specified detail message and
160 + * cause.
161 + *
162 + * @param message the detail message
163 + * @param cause the original cause of this exception
164 + */
165 + public TestUtilsException(String message, Throwable cause) {
166 + super(message, cause);
167 + }
168 + }
169 +}
1 +package org.onlab.util;
2 +
3 +import static org.junit.Assert.assertArrayEquals;
4 +import static org.junit.Assert.assertEquals;
5 +import static org.junit.Assert.assertNull;
6 +
7 +import org.junit.Before;
8 +import org.junit.Test;
9 +import org.onlab.util.TestUtils.TestUtilsException;
10 +
11 +/**
12 + * Test and usage examples for TestUtils.
13 + */
14 +public class TestUtilsTest {
15 +
16 + /**
17 + * Test data.
18 + */
19 + private static final class TestClass {
20 +
21 + @SuppressWarnings("unused")
22 + private int privateField = 42;
23 +
24 + @SuppressWarnings("unused")
25 + protected int protectedField = 2501; // CHECKSTYLE IGNORE THIS LINE
26 +
27 + /**
28 + * Protected method with multiple argument.
29 + *
30 + * @param x simply returns
31 + * @param y not used
32 + * @return x
33 + */
34 + @SuppressWarnings("unused")
35 + private int privateMethod(Number x, Long y) {
36 + return x.intValue();
37 + }
38 +
39 + /**
40 + * Protected method with no argument.
41 + *
42 + * @return int
43 + */
44 + @SuppressWarnings("unused")
45 + protected int protectedMethod() {
46 + return 42;
47 + }
48 +
49 + /**
50 + * Method returning array.
51 + *
52 + * @param ary random array
53 + * @return ary
54 + */
55 + @SuppressWarnings("unused")
56 + private int[] arrayReturnMethod(int[] ary) {
57 + return ary;
58 + }
59 +
60 + /**
61 + * Method without return value.
62 + *
63 + * @param s ignored
64 + */
65 + @SuppressWarnings("unused")
66 + private void voidMethod(String s) {
67 + System.out.println(s);
68 + }
69 + }
70 +
71 + private TestClass test;
72 +
73 + /**
74 + * Sets up the test fixture.
75 + */
76 + @Before
77 + public void setUp() {
78 + test = new TestClass();
79 + }
80 +
81 + /**
82 + * Example to access private field.
83 + *
84 + * @throws TestUtilsException TestUtils error
85 + */
86 + @Test
87 + public void testSetGetPrivateField() throws TestUtilsException {
88 +
89 + assertEquals(42, TestUtils.getField(test, "privateField"));
90 + TestUtils.setField(test, "privateField", 0xDEAD);
91 + assertEquals(0xDEAD, TestUtils.getField(test, "privateField"));
92 + }
93 +
94 + /**
95 + * Example to access protected field.
96 + *
97 + * @throws TestUtilsException TestUtils error
98 + */
99 + @Test
100 + public void testSetGetProtectedField() throws TestUtilsException {
101 +
102 + assertEquals(2501, TestUtils.getField(test, "protectedField"));
103 + TestUtils.setField(test, "protectedField", 0xBEEF);
104 + assertEquals(0xBEEF, TestUtils.getField(test, "protectedField"));
105 + }
106 +
107 + /**
108 + * Example to call private method and multiple parameters.
109 + * <p/>
110 + * It also illustrates that paramTypes must match declared type,
111 + * not the runtime types of arguments.
112 + *
113 + * @throws TestUtilsException TestUtils error
114 + */
115 + @Test
116 + public void testCallPrivateMethod() throws TestUtilsException {
117 +
118 + int result = TestUtils.callMethod(test, "privateMethod",
119 + new Class<?>[] {Number.class, Long.class},
120 + Long.valueOf(42), Long.valueOf(32));
121 + assertEquals(42, result);
122 + }
123 +
124 + /**
125 + * Example to call protected method and no parameters.
126 + *
127 + * @throws TestUtilsException TestUtils error
128 + */
129 + @Test
130 + public void testCallProtectedMethod() throws TestUtilsException {
131 +
132 + int result = TestUtils.callMethod(test, "protectedMethod",
133 + new Class<?>[] {});
134 + assertEquals(42, result);
135 + }
136 +
137 + /**
138 + * Example to call method returning array.
139 + * <p/>
140 + * Note: It is not required to receive as Object.
141 + * Following is just verifying it is not Boxed arrays.
142 + *
143 + * @throws TestUtilsException TestUtils error
144 + */
145 + @Test
146 + public void testCallArrayReturnMethod() throws TestUtilsException {
147 +
148 + int[] array = {1, 2, 3};
149 + Object aryResult = TestUtils.callMethod(test, "arrayReturnMethod",
150 + new Class<?>[] {int[].class}, array);
151 + assertEquals(int[].class, aryResult.getClass());
152 + assertArrayEquals(array, (int[]) aryResult);
153 + }
154 +
155 +
156 + /**
157 + * Example to call void returning method.
158 + * <p/>
159 + * Note: Return value will be null for void methods.
160 + *
161 + * @throws TestUtilsException TestUtils error
162 + */
163 + @Test
164 + public void testCallVoidReturnMethod() throws TestUtilsException {
165 +
166 + Object voidResult = TestUtils.callMethod(test, "voidMethod",
167 + String.class, "foobar");
168 + assertNull(voidResult);
169 + }
170 +}
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
23 <module>nio</module> 23 <module>nio</module>
24 <module>osgi</module> 24 <module>osgi</module>
25 <module>rest</module> 25 <module>rest</module>
26 + <module>thirdparty</module>
26 </modules> 27 </modules>
27 28
28 <dependencies> 29 <dependencies>
......
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
5 + <modelVersion>4.0.0</modelVersion>
6 +
7 + <parent>
8 + <groupId>org.onlab.onos</groupId>
9 + <artifactId>onlab-utils</artifactId>
10 + <version>1.0.0-SNAPSHOT</version>
11 + <relativePath>../pom.xml</relativePath>
12 + </parent>
13 +
14 + <artifactId>onlab-thirdparty</artifactId>
15 + <packaging>bundle</packaging>
16 +
17 + <description>ONLab third-party dependencies</description>
18 +
19 + <dependencies>
20 + <dependency>
21 + <groupId>com.googlecode.concurrent-trees</groupId>
22 + <artifactId>concurrent-trees</artifactId>
23 + <version>2.4.0</version>
24 + </dependency>
25 + </dependencies>
26 +
27 + <build>
28 + <plugins>
29 + <plugin>
30 + <groupId>org.apache.maven.plugins</groupId>
31 + <artifactId>maven-shade-plugin</artifactId>
32 + <version>2.3</version>
33 + <configuration>
34 + <filters>
35 + <filter>
36 + <artifact>com.googlecode.concurrent-trees:concurrent-trees</artifact>
37 + <includes>
38 + <include>com/googlecode/**</include>
39 + </includes>
40 +
41 + </filter>
42 + <filter>
43 + <artifact>com.google.guava:guava</artifact>
44 + <excludes>
45 + <exclude>**</exclude>
46 + </excludes>
47 + </filter>
48 + </filters>
49 + </configuration>
50 + <executions>
51 + <execution>
52 + <phase>package</phase>
53 + <goals>
54 + <goal>shade</goal>
55 + </goals>
56 + </execution>
57 + </executions>
58 + </plugin>
59 + <plugin>
60 + <groupId>org.apache.felix</groupId>
61 + <artifactId>maven-bundle-plugin</artifactId>
62 + <configuration>
63 + <instructions>
64 + <Export-Package>
65 + com.googlecode.concurrenttrees.*
66 + </Export-Package>
67 + </instructions>
68 + </configuration>
69 + </plugin>
70 + </plugins>
71 + </build>
72 +
73 +</project>
1 +package org.onlab.thirdparty;
2 +
3 +
4 +/**
5 + * Empty class required to get the onlab-thirdparty module to build properly.
6 + * <p/>
7 + * TODO Figure out how to remove this.
8 + */
9 +public class OnlabThirdparty {
10 +
11 +}
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
2 <html> 2 <html>
3 <head> 3 <head>
4 <title>ONOS GUI</title> 4 <title>ONOS GUI</title>
5 +
6 + <script src="libs/d3.min.js"></script>
7 + <script src="libs/jquery-2.1.1.min.js"></script>
5 </head> 8 </head>
6 <body> 9 <body>
7 <h1>ONOS GUI</h1> 10 <h1>ONOS GUI</h1>
......