Praseed Balakrishnan

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

Conflicts:
	providers/openflow/device/src/main/java/org/onlab/onos/provider/of/device/impl/OpenFlowDeviceProvider.java
Showing 253 changed files with 15366 additions and 3656 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 }
......
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-optical</artifactId>
15 + <packaging>bundle</packaging>
16 +
17 + <description>ONOS application for packet/optical deployments</description>
18 +
19 + <dependencies>
20 +
21 + <dependency>
22 + <groupId>org.onlab.onos</groupId>
23 + <artifactId>onos-cli</artifactId>
24 + <version>${project.version}</version>
25 + </dependency>
26 + <dependency>
27 + <groupId>org.apache.karaf.shell</groupId>
28 + <artifactId>org.apache.karaf.shell.console</artifactId>
29 + </dependency>
30 +
31 + <dependency>
32 + <groupId>org.codehaus.jackson</groupId>
33 + <artifactId>jackson-core-asl</artifactId>
34 + </dependency>
35 + <dependency>
36 + <groupId>org.codehaus.jackson</groupId>
37 + <artifactId>jackson-mapper-asl</artifactId>
38 + </dependency>
39 + <dependency>
40 + <groupId>com.fasterxml.jackson.core</groupId>
41 + <artifactId>jackson-annotations</artifactId>
42 + <scope>provided</scope>
43 + </dependency>
44 +
45 + </dependencies>
46 +
47 +</project>
1 +package org.onlab.onos.optical.cfg;
2 +
3 +import static org.onlab.onos.net.DeviceId.deviceId;
4 +
5 +import java.io.File;
6 +import java.io.IOException;
7 +import java.util.ArrayList;
8 +import java.util.Iterator;
9 +import java.util.List;
10 +import java.util.Map;
11 +import java.util.Set;
12 +
13 +import org.apache.felix.scr.annotations.Activate;
14 +import org.apache.felix.scr.annotations.Component;
15 +import org.apache.felix.scr.annotations.Deactivate;
16 +import org.apache.felix.scr.annotations.Reference;
17 +import org.apache.felix.scr.annotations.ReferenceCardinality;
18 +import org.codehaus.jackson.JsonNode;
19 +import org.codehaus.jackson.JsonParseException;
20 +import org.codehaus.jackson.annotate.JsonIgnoreProperties;
21 +import org.codehaus.jackson.map.JsonMappingException;
22 +import org.codehaus.jackson.map.ObjectMapper;
23 +import org.onlab.onos.net.ConnectPoint;
24 +import org.onlab.onos.net.DefaultAnnotations;
25 +import org.onlab.onos.net.Device;
26 +import org.onlab.onos.net.DeviceId;
27 +import org.onlab.onos.net.Link;
28 +import org.onlab.onos.net.MastershipRole;
29 +import org.onlab.onos.net.PortNumber;
30 +import org.onlab.onos.net.device.DefaultDeviceDescription;
31 +import org.onlab.onos.net.device.DeviceDescription;
32 +import org.onlab.onos.net.device.DeviceProvider;
33 +import org.onlab.onos.net.device.DeviceProviderRegistry;
34 +import org.onlab.onos.net.device.DeviceProviderService;
35 +import org.onlab.onos.net.link.DefaultLinkDescription;
36 +import org.onlab.onos.net.link.LinkProvider;
37 +import org.onlab.onos.net.link.LinkProviderRegistry;
38 +import org.onlab.onos.net.link.LinkProviderService;
39 +import org.onlab.onos.net.provider.AbstractProvider;
40 +import org.onlab.onos.net.provider.ProviderId;
41 +import org.onlab.packet.ChassisId;
42 +import org.slf4j.Logger;
43 +import org.slf4j.LoggerFactory;
44 +
45 +/**
46 + * OpticalConfigProvider emulates the SB network provider for optical switches,
47 + * optical links and any other state that needs to be configured for correct network
48 + * operations.
49 + *
50 + */
51 +
52 +@JsonIgnoreProperties(ignoreUnknown = true)
53 +@Component(immediate = true)
54 +public class OpticalConfigProvider extends AbstractProvider implements DeviceProvider, LinkProvider {
55 +
56 + protected static final Logger log = LoggerFactory
57 + .getLogger(OpticalConfigProvider.class);
58 +
59 + // TODO: fix hard coded file path later.
60 + private static final String DEFAULT_CONFIG_FILE =
61 + "/opt/onos/config/demo-3-roadm-2-ps.json";
62 + private String configFileName = DEFAULT_CONFIG_FILE;
63 +
64 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
65 + protected LinkProviderRegistry linkProviderRegistry;
66 +
67 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
68 + protected DeviceProviderRegistry deviceProviderRegistry;
69 +
70 + private static final String OPTICAL_ANNOTATION = "optical.";
71 +
72 + private LinkProviderService linkProviderService;
73 + private DeviceProviderService deviceProviderService;
74 +
75 + private static final List<Roadm> RAW_ROADMS = new ArrayList<>();
76 + private static final List<WdmLink> RAW_WDMLINKS = new ArrayList<>();
77 + private static final List<PktOptLink> RAW_PKTOPTLINKS = new ArrayList<>();
78 +
79 + private static final String ROADM = "Roadm";
80 + private static final String WDM_LINK = "wdmLink";
81 + private static final String PKT_OPT_LINK = "pktOptLink";
82 +
83 + protected OpticalNetworkConfig opticalNetworkConfig;
84 +
85 + public OpticalConfigProvider() {
86 + super(new ProviderId("of", "org.onlab.onos.provider.opticalConfig", true));
87 + }
88 +
89 + @Activate
90 + protected void activate() {
91 + linkProviderService = linkProviderRegistry.register(this);
92 + deviceProviderService = deviceProviderRegistry.register(this);
93 + log.info("Starting optical network configuration process...");
94 + log.info("Optical config file set to {}", configFileName);
95 +
96 + loadOpticalConfig();
97 + parseOpticalConfig();
98 + publishOpticalConfig();
99 + }
100 +
101 + @Deactivate
102 + protected void deactivate() {
103 + linkProviderRegistry.unregister(this);
104 + linkProviderService = null;
105 + deviceProviderRegistry.unregister(this);
106 + deviceProviderService = null;
107 + RAW_ROADMS.clear();
108 + RAW_WDMLINKS.clear();
109 + RAW_PKTOPTLINKS.clear();
110 + log.info("Stopped");
111 + }
112 +
113 + private void loadOpticalConfig() {
114 + ObjectMapper mapper = new ObjectMapper();
115 + opticalNetworkConfig = new OpticalNetworkConfig();
116 + try {
117 + opticalNetworkConfig = mapper.readValue(new File(configFileName), OpticalNetworkConfig.class);
118 + } catch (JsonParseException e) {
119 + String err = String.format("JsonParseException while loading network "
120 + + "config from file: %s: %s", configFileName, e.getMessage());
121 + log.error(err, e);
122 + } catch (JsonMappingException e) {
123 + String err = String.format(
124 + "JsonMappingException while loading network config "
125 + + "from file: %s: %s", configFileName, e.getMessage());
126 + log.error(err, e);
127 + } catch (IOException e) {
128 + String err = String.format("IOException while loading network config "
129 + + "from file: %s %s", configFileName, e.getMessage());
130 + log.error(err, e);
131 + }
132 + }
133 +
134 + private void parseOpticalConfig() {
135 + List<OpticalSwitchDescription> swList = opticalNetworkConfig.getOpticalSwitches();
136 + List<OpticalLinkDescription> lkList = opticalNetworkConfig.getOpticalLinks();
137 +
138 + for (OpticalSwitchDescription sw : swList) {
139 + String swtype = sw.getType();
140 + boolean allow = sw.isAllowed();
141 + if (swtype.equals(ROADM) && allow) {
142 + int regNum = 0;
143 + Set<Map.Entry<String, JsonNode>> m = sw.params.entrySet();
144 + for (Map.Entry<String, JsonNode> e : m) {
145 + String key = e.getKey();
146 + JsonNode j = e.getValue();
147 + if (key.equals("numRegen")) {
148 + regNum = j.asInt();
149 + }
150 + }
151 +
152 + Roadm newRoadm = new Roadm();
153 + newRoadm.setName(sw.name);
154 + newRoadm.setNodeId(sw.nodeDpid);
155 + newRoadm.setLongtitude(sw.longitude);
156 + newRoadm.setLatitude(sw.latitude);
157 + newRoadm.setRegenNum(regNum);
158 +
159 + RAW_ROADMS.add(newRoadm);
160 + log.info(newRoadm.toString());
161 + }
162 + }
163 +
164 + for (OpticalLinkDescription lk : lkList) {
165 + String lktype = lk.getType();
166 + switch (lktype) {
167 + case WDM_LINK:
168 + WdmLink newWdmLink = new WdmLink();
169 + newWdmLink.setSrcNodeId(lk.getNodeDpid1());
170 + newWdmLink.setSnkNodeId(lk.getNodeDpid2());
171 + newWdmLink.setAdminWeight(1000); // default weight for each WDM link.
172 + Set<Map.Entry<String, JsonNode>> m = lk.params.entrySet();
173 + for (Map.Entry<String, JsonNode> e : m) {
174 + String key = e.getKey();
175 + JsonNode j = e.getValue();
176 + if (key.equals("nodeName1")) {
177 + newWdmLink.setSrcNodeName(j.asText());
178 + } else if (key.equals("nodeName2")) {
179 + newWdmLink.setSnkNodeName(j.asText());
180 + } else if (key.equals("port1")) {
181 + newWdmLink.setSrcPort(j.asInt());
182 + } else if (key.equals("port2")) {
183 + newWdmLink.setSnkPort(j.asInt());
184 + } else if (key.equals("distKms")) {
185 + newWdmLink.setDistance(j.asDouble());
186 + } else if (key.equals("numWaves")) {
187 + newWdmLink.setWavelengthNumber(j.asInt());
188 + } else {
189 + log.error("error found");
190 + // TODO add exception processing;
191 + }
192 + }
193 + RAW_WDMLINKS.add(newWdmLink);
194 + log.info(newWdmLink.toString());
195 +
196 + break;
197 +
198 + case PKT_OPT_LINK:
199 + PktOptLink newPktOptLink = new PktOptLink();
200 + newPktOptLink.setSrcNodeId(lk.getNodeDpid1());
201 + newPktOptLink.setSnkNodeId(lk.getNodeDpid2());
202 + newPktOptLink.setAdminWeight(10); // default weight for each packet-optical link.
203 + Set<Map.Entry<String, JsonNode>> ptm = lk.params.entrySet();
204 + for (Map.Entry<String, JsonNode> e : ptm) {
205 + String key = e.getKey();
206 + JsonNode j = e.getValue();
207 + if (key.equals("nodeName1")) {
208 + newPktOptLink.setSrcNodeName(j.asText());
209 + } else if (key.equals("nodeName2")) {
210 + newPktOptLink.setSnkNodeName(j.asText());
211 + } else if (key.equals("port1")) {
212 + newPktOptLink.setSrcPort(j.asInt());
213 + } else if (key.equals("port2")) {
214 + newPktOptLink.setSnkPort(j.asInt());
215 + } else if (key.equals("bandWidth")) {
216 + newPktOptLink.setBandwdith(j.asDouble());
217 + } else {
218 + log.error("error found");
219 + // TODO add exception processing;
220 + }
221 + }
222 +
223 + RAW_PKTOPTLINKS.add(newPktOptLink);
224 + log.info(newPktOptLink.toString());
225 + break;
226 + default:
227 + }
228 + }
229 + }
230 +
231 + private void publishOpticalConfig() {
232 + if (deviceProviderService == null || linkProviderService == null) {
233 + return;
234 + }
235 +
236 + // Discover the optical ROADM objects
237 + Iterator<Roadm> iterWdmNode = RAW_ROADMS.iterator();
238 + while (iterWdmNode.hasNext()) {
239 + Roadm value = iterWdmNode.next();
240 + DeviceId did = deviceId("of:" + value.getNodeId().replace(":", ""));
241 + ChassisId cid = new ChassisId(value.getNodeId());
242 + DefaultAnnotations extendedAttributes = DefaultAnnotations.builder()
243 + .set(OPTICAL_ANNOTATION + "switchType", "ROADM")
244 + .set(OPTICAL_ANNOTATION + "switchName", value.getName())
245 + .set(OPTICAL_ANNOTATION + "latitude", Double.toString(value.getLatitude()))
246 + .set(OPTICAL_ANNOTATION + "longtitude", Double.toString(value.getLongtitude()))
247 + .set(OPTICAL_ANNOTATION + "regNum", Integer.toString(value.getRegenNum()))
248 + .build();
249 +
250 + DeviceDescription description =
251 + new DefaultDeviceDescription(did.uri(),
252 + Device.Type.SWITCH,
253 + "",
254 + "",
255 + "",
256 + "",
257 + cid,
258 + extendedAttributes);
259 + deviceProviderService.deviceConnected(did, description);
260 + }
261 +
262 + // Discover the optical WDM link objects
263 + Iterator<WdmLink> iterWdmlink = RAW_WDMLINKS.iterator();
264 + while (iterWdmlink.hasNext()) {
265 + WdmLink value = iterWdmlink.next();
266 +
267 + DeviceId srcNodeId = deviceId("of:" + value.getSrcNodeId().replace(":", ""));
268 + DeviceId snkNodeId = deviceId("of:" + value.getSnkNodeId().replace(":", ""));
269 +
270 + PortNumber srcPort = PortNumber.portNumber(value.getSrcPort());
271 + PortNumber snkPort = PortNumber.portNumber(value.getSnkPort());
272 +
273 + ConnectPoint srcPoint = new ConnectPoint(srcNodeId, srcPort);
274 + ConnectPoint snkPoint = new ConnectPoint(snkNodeId, snkPort);
275 +
276 + DefaultAnnotations extendedAttributes = DefaultAnnotations.builder()
277 + .set(OPTICAL_ANNOTATION + "linkType", "WDM")
278 + .set(OPTICAL_ANNOTATION + "distance", Double.toString(value.getDistance()))
279 + .set(OPTICAL_ANNOTATION + "cost", Double.toString(value.getDistance()))
280 + .set(OPTICAL_ANNOTATION + "adminWeight", Double.toString(value.getAdminWeight()))
281 + .set(OPTICAL_ANNOTATION + "wavelengthNum", Integer.toString(value.getWavelengthNumber()))
282 + .build();
283 +
284 + DefaultLinkDescription linkDescription =
285 + new DefaultLinkDescription(srcPoint,
286 + snkPoint,
287 + Link.Type.DIRECT,
288 + extendedAttributes);
289 +
290 + linkProviderService.linkDetected(linkDescription);
291 + log.info(String.format("WDM link: %s : %s",
292 + linkDescription.src().toString(), linkDescription.dst().toString()));
293 + }
294 +
295 + // Discover the packet optical link objects
296 + Iterator<PktOptLink> iterPktOptlink = RAW_PKTOPTLINKS.iterator();
297 + while (iterPktOptlink.hasNext()) {
298 + PktOptLink value = iterPktOptlink.next();
299 + DeviceId srcNodeId = deviceId("of:" + value.getSrcNodeId().replace(":", ""));
300 + DeviceId snkNodeId = deviceId("of:" + value.getSnkNodeId().replace(":", ""));
301 +
302 + PortNumber srcPort = PortNumber.portNumber(value.getSrcPort());
303 + PortNumber snkPort = PortNumber.portNumber(value.getSnkPort());
304 +
305 + ConnectPoint srcPoint = new ConnectPoint(srcNodeId, srcPort);
306 + ConnectPoint snkPoint = new ConnectPoint(snkNodeId, snkPort);
307 +
308 + DefaultAnnotations extendedAttributes = DefaultAnnotations.builder()
309 + .set(OPTICAL_ANNOTATION + "linkType", "PktOptLink")
310 + .set(OPTICAL_ANNOTATION + "bandwidth", Double.toString(value.getBandwidth()))
311 + .set(OPTICAL_ANNOTATION + "cost", Double.toString(value.getBandwidth()))
312 + .set(OPTICAL_ANNOTATION + "adminWeight", Double.toString(value.getAdminWeight()))
313 + .build();
314 +
315 + DefaultLinkDescription linkDescription =
316 + new DefaultLinkDescription(srcPoint,
317 + snkPoint,
318 + Link.Type.DIRECT,
319 + extendedAttributes);
320 +
321 + linkProviderService.linkDetected(linkDescription);
322 + log.info(String.format("Packet-optical link: %s : %s",
323 + linkDescription.src().toString(), linkDescription.dst().toString()));
324 + }
325 +
326 + }
327 +
328 + @Override
329 + public void triggerProbe(Device device) {
330 + // TODO We may want to consider re-reading config files and publishing them based on this event.
331 + }
332 +
333 + @Override
334 + public void roleChanged(Device device, MastershipRole newRole) {
335 + // TODO Auto-generated method stub.
336 + }
337 +
338 +}
1 +package org.onlab.onos.optical.cfg;
2 +
3 +import java.util.Map;
4 +import org.codehaus.jackson.JsonNode;
5 +import org.onlab.util.HexString;
6 +
7 +/**
8 + * Public class corresponding to JSON described data model.
9 + */
10 +public class OpticalLinkDescription {
11 + protected String type;
12 + protected Boolean allowed;
13 + protected long dpid1;
14 + protected long dpid2;
15 + protected String nodeDpid1;
16 + protected String nodeDpid2;
17 + protected Map<String, JsonNode> params;
18 + protected Map<String, String> publishAttributes;
19 +
20 + public String getType() {
21 + return type;
22 + }
23 +
24 + public void setType(String type) {
25 + this.type = type;
26 + }
27 +
28 + public Boolean isAllowed() {
29 + return allowed;
30 + }
31 +
32 + public void setAllowed(Boolean allowed) {
33 + this.allowed = allowed;
34 + }
35 +
36 + public String getNodeDpid1() {
37 + return nodeDpid1;
38 + }
39 +
40 + public void setNodeDpid1(String nodeDpid1) {
41 + this.nodeDpid1 = nodeDpid1;
42 + this.dpid1 = HexString.toLong(nodeDpid1);
43 + }
44 +
45 + public String getNodeDpid2() {
46 + return nodeDpid2;
47 + }
48 +
49 + public void setNodeDpid2(String nodeDpid2) {
50 + this.nodeDpid2 = nodeDpid2;
51 + this.dpid2 = HexString.toLong(nodeDpid2);
52 + }
53 +
54 + public long getDpid1() {
55 + return dpid1;
56 + }
57 +
58 + public void setDpid1(long dpid1) {
59 + this.dpid1 = dpid1;
60 + this.nodeDpid1 = HexString.toHexString(dpid1);
61 + }
62 +
63 + public long getDpid2() {
64 + return dpid2;
65 + }
66 +
67 + public void setDpid2(long dpid2) {
68 + this.dpid2 = dpid2;
69 + this.nodeDpid2 = HexString.toHexString(dpid2);
70 + }
71 +
72 + public Map<String, JsonNode> getParams() {
73 + return params;
74 + }
75 +
76 + public void setParams(Map<String, JsonNode> params) {
77 + this.params = params;
78 + }
79 +
80 + public Map<String, String> getPublishAttributes() {
81 + return publishAttributes;
82 + }
83 +
84 + public void setPublishAttributes(Map<String, String> publishAttributes) {
85 + this.publishAttributes = publishAttributes;
86 + }
87 +
88 +}
89 +
1 +package org.onlab.onos.optical.cfg;
2 +
3 +import java.util.ArrayList;
4 +import java.util.List;
5 +
6 +import org.slf4j.Logger;
7 +import org.slf4j.LoggerFactory;
8 +
9 +/**
10 + * Public class corresponding to JSON described data model.
11 + */
12 +public class OpticalNetworkConfig {
13 + protected static final Logger log = LoggerFactory.getLogger(OpticalNetworkConfig.class);
14 +
15 + private List<OpticalSwitchDescription> opticalSwitches;
16 + private List<OpticalLinkDescription> opticalLinks;
17 +
18 + public OpticalNetworkConfig() {
19 + opticalSwitches = new ArrayList<OpticalSwitchDescription>();
20 + opticalLinks = new ArrayList<OpticalLinkDescription>();
21 + }
22 +
23 + public List<OpticalSwitchDescription> getOpticalSwitches() {
24 + return opticalSwitches;
25 + }
26 +
27 + public void setOpticalSwitches(List<OpticalSwitchDescription> switches) {
28 + this.opticalSwitches = switches;
29 + }
30 +
31 + public List<OpticalLinkDescription> getOpticalLinks() {
32 + return opticalLinks;
33 + }
34 +
35 + public void setOpticalLinks(List<OpticalLinkDescription> links) {
36 + this.opticalLinks = links;
37 + }
38 +
39 +}
40 +
1 +package org.onlab.onos.optical.cfg;
2 +
3 +import java.util.Map;
4 +import org.codehaus.jackson.JsonNode;
5 +import org.codehaus.jackson.annotate.JsonProperty;
6 +import org.onlab.util.HexString;
7 +
8 +/**
9 + * Public class corresponding to JSON described data model.
10 + */
11 +public class OpticalSwitchDescription {
12 + protected String name;
13 + protected long dpid;
14 + protected String nodeDpid;
15 + protected String type;
16 + protected double latitude;
17 + protected double longitude;
18 + protected boolean allowed;
19 + protected Map<String, JsonNode> params;
20 + protected Map<String, String> publishAttributes;
21 +
22 + public String getName() {
23 + return name;
24 + }
25 + @JsonProperty("name")
26 + public void setName(String name) {
27 + this.name = name;
28 + }
29 +
30 + public long getDpid() {
31 + return dpid;
32 + }
33 + @JsonProperty("dpid")
34 + public void setDpid(long dpid) {
35 + this.dpid = dpid;
36 + this.nodeDpid = HexString.toHexString(dpid);
37 + }
38 +
39 + public String getNodeDpid() {
40 + return nodeDpid;
41 + }
42 +
43 + public String getHexDpid() {
44 + return nodeDpid;
45 + }
46 +
47 + public void setNodeDpid(String nodeDpid) {
48 + this.nodeDpid = nodeDpid;
49 + this.dpid = HexString.toLong(nodeDpid);
50 + }
51 +
52 + public String getType() {
53 + return type;
54 + }
55 +
56 + public void setType(String type) {
57 + this.type = type;
58 + }
59 +
60 + public double getLatitude() {
61 + return latitude;
62 + }
63 +
64 + public void setLatitude(double latitude) {
65 + this.latitude = latitude;
66 + }
67 +
68 + public double getLongitude() {
69 + return longitude;
70 + }
71 +
72 + public void setLongitude(double longitude) {
73 + this.longitude = longitude;
74 + }
75 +
76 + public boolean isAllowed() {
77 + return allowed;
78 + }
79 +
80 + public void setAllowed(boolean allowed) {
81 + this.allowed = allowed;
82 + }
83 +
84 + public Map<String, JsonNode> getParams() {
85 + return params;
86 + }
87 +
88 + public void setParams(Map<String, JsonNode> params) {
89 + this.params = params;
90 + }
91 +
92 + public Map<String, String> getPublishAttributes() {
93 + return publishAttributes;
94 + }
95 +
96 + public void setPublishAttributes(Map<String, String> publishAttributes) {
97 + this.publishAttributes = publishAttributes;
98 + }
99 +
100 +}
1 +package org.onlab.onos.optical.cfg;
2 +
3 +/**
4 + * Packet-optical link Java data object.
5 + */
6 +class PktOptLink {
7 + private String srcNodeName;
8 + private String snkNodeName;
9 + private String srcNodeId;
10 + private String snkNodeId;
11 + private int srcPort;
12 + private int snkPort;
13 + private double bandwidth;
14 + private double cost;
15 + private long adminWeight;
16 +
17 + public PktOptLink(String srcName, String snkName) {
18 + this.srcNodeName = srcName;
19 + this.snkNodeName = snkName;
20 + }
21 +
22 + public PktOptLink() {
23 + // TODO Auto-generated constructor stub
24 + }
25 +
26 + public void setSrcNodeName(String name) {
27 + this.srcNodeName = name;
28 + }
29 +
30 + public String getSrcNodeName() {
31 + return this.srcNodeName;
32 + }
33 +
34 + public void setSnkNodeName(String name) {
35 + this.snkNodeName = name;
36 + }
37 +
38 + public String getSnkNodeName() {
39 + return this.snkNodeName;
40 + }
41 +
42 + public void setSrcNodeId(String nodeId) {
43 + this.srcNodeId = nodeId;
44 + }
45 +
46 + public String getSrcNodeId() {
47 + return this.srcNodeId;
48 + }
49 +
50 + public void setSnkNodeId(String nodeId) {
51 + this.snkNodeId = nodeId;
52 + }
53 +
54 + public String getSnkNodeId() {
55 + return this.snkNodeId;
56 + }
57 +
58 + public void setSrcPort(int port) {
59 + this.srcPort = port;
60 + }
61 +
62 + public int getSrcPort() {
63 + return this.srcPort;
64 + }
65 +
66 + public void setSnkPort(int port) {
67 + this.snkPort = port;
68 + }
69 +
70 + public int getSnkPort() {
71 + return this.snkPort;
72 + }
73 +
74 + public void setBandwdith(double x) {
75 + this.bandwidth = x;
76 + }
77 +
78 + public double getBandwidth() {
79 + return this.bandwidth;
80 + }
81 +
82 + public void setCost(double x) {
83 + this.cost = x;
84 + }
85 +
86 + public double getCost() {
87 + return this.cost;
88 + }
89 +
90 + public void setAdminWeight(long x) {
91 + this.adminWeight = x;
92 + }
93 +
94 + public long getAdminWeight() {
95 + return this.adminWeight;
96 + }
97 +
98 + @Override
99 + public String toString() {
100 + return new StringBuilder(" srcNodeName: ").append(this.srcNodeName)
101 + .append(" snkNodeName: ").append(this.snkNodeName)
102 + .append(" srcNodeId: ").append(this.srcNodeId)
103 + .append(" snkNodeId: ").append(this.snkNodeId)
104 + .append(" srcPort: ").append(this.srcPort)
105 + .append(" snkPort: ").append(this.snkPort)
106 + .append(" bandwidth: ").append(this.bandwidth)
107 + .append(" cost: ").append(this.cost)
108 + .append(" adminWeight: ").append(this.adminWeight).toString();
109 + }
110 +}
1 +package org.onlab.onos.optical.cfg;
2 +
3 +/**
4 + * ROADM java data object converted from a JSON file.
5 + */
6 +class Roadm {
7 + private String name;
8 + private String nodeID;
9 + private double longtitude;
10 + private double latitude;
11 + private int regenNum;
12 +
13 + //TODO use the following attributes when needed for configurations
14 + private int tPort10G;
15 + private int tPort40G;
16 + private int tPort100G;
17 + private int wPort;
18 +
19 + public Roadm() {
20 + }
21 +
22 + public Roadm(String name) {
23 + this.name = name;
24 + }
25 +
26 + public void setName(String name) {
27 + this.name = name;
28 + }
29 +
30 + public String getName() {
31 + return this.name;
32 + }
33 +
34 + public void setNodeId(String nameId) {
35 + this.nodeID = nameId;
36 + }
37 +
38 + public String getNodeId() {
39 + return this.nodeID;
40 + }
41 +
42 + public void setLongtitude(double x) {
43 + this.longtitude = x;
44 + }
45 +
46 + public double getLongtitude() {
47 + return this.longtitude;
48 + }
49 +
50 + public void setLatitude(double y) {
51 + this.latitude = y;
52 + }
53 +
54 + public double getLatitude() {
55 + return this.latitude;
56 + }
57 +
58 + public void setRegenNum(int num) {
59 + this.regenNum = num;
60 + }
61 + public int getRegenNum() {
62 + return this.regenNum;
63 + }
64 +
65 + public void setTport10GNum(int num) {
66 + this.tPort10G = num;
67 + }
68 + public int getTport10GNum() {
69 + return this.tPort10G;
70 + }
71 +
72 + public void setTport40GNum(int num) {
73 + this.tPort40G = num;
74 + }
75 + public int getTport40GNum() {
76 + return this.tPort40G;
77 + }
78 +
79 + public void setTport100GNum(int num) {
80 + this.tPort100G = num;
81 + }
82 + public int getTport100GNum() {
83 + return this.tPort100G;
84 + }
85 +
86 + public void setWportNum(int num) {
87 + this.wPort = num;
88 + }
89 + public int getWportNum() {
90 + return this.wPort;
91 + }
92 +
93 + @Override
94 + public String toString() {
95 + return new StringBuilder(" ROADM Name: ").append(this.name)
96 + .append(" nodeID: ").append(this.nodeID)
97 + .append(" longtitude: ").append(this.longtitude)
98 + .append(" latitude: ").append(this.latitude)
99 + .append(" regenNum: ").append(this.regenNum)
100 + .append(" 10GTportNum: ").append(this.tPort10G)
101 + .append(" 40GTportNum: ").append(this.tPort40G)
102 + .append(" 100GTportNum: ").append(this.tPort100G)
103 + .append(" WportNum: ").append(this.wPort).toString();
104 + }
105 +}
106 +
1 +package org.onlab.onos.optical.cfg;
2 +
3 +/**
4 + * WDM Link Java data object converted from a JSON file.
5 + */
6 +class WdmLink {
7 + private String srcNodeName;
8 + private String snkNodeName;
9 + private String srcNodeId;
10 + private String snkNodeId;
11 + private int srcPort;
12 + private int snkPort;
13 + private double distance;
14 + private double cost;
15 + private int wavelengthNumber;
16 + private long adminWeight;
17 +
18 + public WdmLink(String name1, String name2) {
19 + this.srcNodeName = name1;
20 + this.snkNodeName = name2;
21 + }
22 +
23 + public WdmLink() {
24 + // TODO Auto-generated constructor stub
25 + }
26 +
27 + public void setSrcNodeName(String name) {
28 + this.srcNodeName = name;
29 + }
30 +
31 + public String getSrcNodeName() {
32 + return this.srcNodeName;
33 + }
34 +
35 + public void setSnkNodeName(String name) {
36 + this.snkNodeName = name;
37 + }
38 +
39 + public String getSnkNodeName() {
40 + return this.snkNodeName;
41 + }
42 +
43 + public void setSrcNodeId(String nodeId) {
44 + this.srcNodeId = nodeId;
45 + }
46 +
47 + public String getSrcNodeId() {
48 + return this.srcNodeId;
49 + }
50 +
51 + public void setSnkNodeId(String nodeId) {
52 + this.snkNodeId = nodeId;
53 + }
54 +
55 + public String getSnkNodeId() {
56 + return this.snkNodeId;
57 + }
58 +
59 + public void setSrcPort(int port) {
60 + this.srcPort = port;
61 + }
62 +
63 + public int getSrcPort() {
64 + return this.srcPort;
65 + }
66 +
67 + public void setSnkPort(int port) {
68 + this.snkPort = port;
69 + }
70 +
71 + public int getSnkPort() {
72 + return this.snkPort;
73 + }
74 +
75 + public void setDistance(double x) {
76 + this.distance = x;
77 + }
78 +
79 + public double getDistance() {
80 + return this.distance;
81 + }
82 +
83 + public void setCost(double x) {
84 + this.cost = x;
85 + }
86 +
87 + public double getCost() {
88 + return this.cost;
89 + }
90 +
91 + public void setWavelengthNumber(int x) {
92 + this.wavelengthNumber = x;
93 + }
94 +
95 + public int getWavelengthNumber() {
96 + return this.wavelengthNumber;
97 + }
98 +
99 + public void setAdminWeight(long x) {
100 + this.adminWeight = x;
101 + }
102 +
103 + public long getAdminWeight() {
104 + return this.adminWeight;
105 + }
106 +
107 + @Override
108 + public String toString() {
109 + return new StringBuilder(" srcNodeName: ").append(this.srcNodeName)
110 + .append(" snkNodeName: ").append(this.snkNodeName)
111 + .append(" srcNodeId: ").append(this.srcNodeId)
112 + .append(" snkNodeId: ").append(this.snkNodeId)
113 + .append(" srcPort: ").append(this.srcPort)
114 + .append(" snkPort: ").append(this.snkPort)
115 + .append(" distance: ").append(this.distance)
116 + .append(" cost: ").append(this.cost)
117 + .append(" wavelengthNumber: ").append(this.wavelengthNumber)
118 + .append(" adminWeight: ").append(this.adminWeight).toString();
119 + }
120 +}
121 +
1 +{
2 + "opticalSwitches": [
3 + {
4 + "allowed": true,
5 + "latitude": 37.6,
6 + "longitude": 122.3,
7 + "name": "SFO-W10",
8 + "nodeDpid": "00:00:ff:ff:ff:ff:ff:01",
9 + "params": {
10 + "numRegen": 0
11 + },
12 + "type": "Roadm"
13 + },
14 +
15 + {
16 + "allowed": true,
17 + "latitude": 37.3,
18 + "longitude": 121.9,
19 + "name": "SJC-W10",
20 + "nodeDpid": "00:00:ff:ff:ff:ff:ff:02",
21 + "params": {
22 + "numRegen": 0
23 + },
24 + "type": "Roadm"
25 + },
26 +
27 + {
28 + "allowed": true,
29 + "latitude": 33.9,
30 + "longitude": 118.4
31 + "name": "LAX-W10",
32 + "nodeDpid": "00:00:ff:ff:ff:ff:ff:03",
33 + "params": {
34 + "numRegen": 0
35 + },
36 + "type": "Roadm"
37 + },
38 +
39 + {
40 + "allowed": true,
41 + "latitude": 32.8,
42 + "longitude": 117.1,
43 + "name": "SDG-W10",
44 + "nodeDpid": "00:00:ff:ff:ff:ff:ff:04",
45 + "params": {
46 + "numRegen": 3
47 + },
48 + "type": "Roadm"
49 + },
50 +
51 + {
52 + "allowed": true,
53 + "latitude": 44.8,
54 + "longitude": 93.1,
55 + "name": "MSP-M10",
56 + "nodeDpid": "00:00:ff:ff:ff:ff:ff:05",
57 + "params": {
58 + "numRegen": 3
59 + },
60 + "type": "Roadm"
61 + },
62 +
63 + {
64 + "allowed": true,
65 + "latitude": 32.8,
66 + "longitude": 97.1,
67 + "name": "DFW-M10",
68 + "nodeDpid": "00:00:ff:ff:ff:ff:ff:06",
69 + "params": {
70 + "numRegen": 3
71 + },
72 + "type": "Roadm"
73 + },
74 +
75 + {
76 + "allowed": true,
77 + "latitude": 41.8,
78 + "longitude": 120.1,
79 + "name": "CHG-N10",
80 + "nodeDpid": "00:00:ff:ff:ff:ff:ff:07",
81 + "params": {
82 + "numRegen": 3
83 + },
84 + "type": "Roadm"
85 + },
86 +
87 + {
88 + "allowed": true,
89 + "latitude": 38.8,
90 + "longitude": 77.1,
91 + "name": "IAD-M10",
92 + "nodeDpid": "00:00:ff:ff:ff:ff:ff:08",
93 + "params": {
94 + "numRegen": 3
95 + },
96 + "type": "Roadm"
97 + },
98 +
99 + {
100 + "allowed": true,
101 + "latitude": 40.8,
102 + "longitude": 73.1,
103 + "name": "JFK-E10",
104 + "nodeDpid": "00:00:ff:ff:ff:ff:ff:09",
105 + "params": {
106 + "numRegen": 0
107 + },
108 + "type": "Roadm"
109 +
110 + },
111 +
112 + {
113 + "allowed": true,
114 + "latitude": 33.8,
115 + "longitude": 84.1,
116 + "name": "ATL-S10",
117 + "nodeDpid": "00:00:ff:ff:ff:ff:ff:0A",
118 + "params": {
119 + "numRegen": 0
120 + },
121 + "type": "Roadm"
122 + }
123 +
124 + ],
125 +
126 + "opticalLinks": [
127 + {
128 + "allowed": true,
129 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:01",
130 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:02",
131 + "params": {
132 + "distKms": 1000,
133 + "nodeName1": "SFO-W10",
134 + "nodeName2": "SJC-W10",
135 + "numWaves": 80,
136 + "port1": 10,
137 + "port2": 10
138 + },
139 + "type": "wdmLink"
140 + },
141 +
142 + {
143 + "allowed": true,
144 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:02",
145 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:03",
146 + "params": {
147 + "distKms": 1000,
148 + "nodeName1": "SJC-W10",
149 + "nodeName2": "LAX-W10",
150 + "numWaves": 80,
151 + "port1": 20,
152 + "port2": 10
153 + },
154 + "type": "wdmLink"
155 + },
156 +
157 + {
158 + "allowed": true,
159 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:03",
160 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:04",
161 + "params": {
162 + "distKms": 1000,
163 + "nodeName1": "LAX-W10",
164 + "nodeName2": "SDG-W10",
165 + "numWaves": 80,
166 + "port1": 30,
167 + "port2": 10
168 + },
169 + "type": "wdmLink"
170 + },
171 +
172 + {
173 + "allowed": true,
174 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:02",
175 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:05",
176 + "params": {
177 + "distKms": 4000,
178 + "nodeName1": "SJC-W10",
179 + "nodeName2": "MSP-M10",
180 + "numWaves": 80,
181 + "port1": 20,
182 + "port2": 10
183 + },
184 + "type": "wdmLink"
185 + },
186 +
187 + {
188 +
189 + "allowed": true,
190 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:03",
191 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:06",
192 + "params": {
193 + "distKms": 5000,
194 + "nodeName1": "LAX-W10",
195 + "nodeName2": "DFW-M10",
196 + "numWaves": 80,
197 + "port1": 20,
198 + "port2": 10
199 + },
200 + "type": "wdmLink"
201 + },
202 +
203 + {
204 + "allowed": true,
205 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:05",
206 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:06",
207 + "params": {
208 + "distKms": 3000,
209 + "nodeName1": "MSP-M10",
210 + "nodeName2": "DFW-M10",
211 + "numWaves": 80,
212 + "port1": 30,
213 + "port2": 20
214 + },
215 + "type": "wdmLink"
216 + },
217 +
218 + {
219 + "allowed": true,
220 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:05",
221 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:07",
222 + "params": {
223 + "distKms": 3000,
224 + "nodeName1": "MSP-M10",
225 + "nodeName2": "CHG-N10",
226 + "numWaves": 80,
227 + "port1": 20,
228 + "port2": 21
229 + },
230 + "type": "wdmLink"
231 + },
232 +
233 + {
234 +
235 + "allowed": true,
236 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:06",
237 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:08",
238 + "params": {
239 + "distKms": 4000,
240 + "nodeName1": "DFW-M10",
241 + "nodeName2": "IAD-M10",
242 + "numWaves": 80,
243 + "port1": 30,
244 + "port2": 10
245 + },
246 + "type": "wdmLink"
247 + },
248 +
249 + {
250 +
251 + "allowed": true,
252 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:07",
253 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:08",
254 + "params": {
255 + "distKms": 4000,
256 + "nodeName1": "CHG-M10",
257 + "nodeName2": "IAD-M10",
258 + "numWaves": 80,
259 + "port1": 30,
260 + "port2": 20
261 + },
262 + "type": "wdmLink"
263 + },
264 +
265 + {
266 + "allowed": true,
267 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:07",
268 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:09",
269 + "params": {
270 + "distKms": 5000,
271 + "nodeName1": "CHG-M10",
272 + "nodeName2": "JFK-E10",
273 + "numWaves": 80,
274 + "port1": 20,
275 + "port2": 10
276 + },
277 + "type": "wdmLink"
278 + },
279 +
280 + {
281 + "allowed": true,
282 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:08",
283 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:0A",
284 + "params": {
285 + "distKms": 3000,
286 + "nodeName1": "IAD-M10",
287 + "nodeName2": "ATL-S10",
288 + "numWaves": 80,
289 + "port1": 30,
290 + "port2": 10
291 + },
292 + "type": "wdmLink"
293 + },
294 +
295 + {
296 +
297 + "allowed": true,
298 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:09",
299 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:0A",
300 + "params": {
301 + "distKms": 4000,
302 + "nodeName1": "JFK-E10",
303 + "nodeName2": "ATL-S10",
304 + "numWaves": 80,
305 + "port1": 20,
306 + "port2": 20
307 + },
308 + "type": "wdmLink"
309 + },
310 +
311 +
312 + {
313 + "allowed": true,
314 + "nodeDpid1": "00:00:ff:ff:ff:ff:00:01",
315 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:01",
316 + "params": {
317 + "nodeName1": "SFO-R10",
318 + "nodeName2": "SFO-W10",
319 + "port1": 10,
320 + "port2": 1
321 + },
322 + "type": "pktOptLink"
323 + },
324 +
325 + {
326 + "allowed": true,
327 + "nodeDpid1": "00:00:ff:ff:ff:ff:00:03",
328 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:03",
329 + "params": {
330 + "nodeName1": "LAX-R10",
331 + "nodeName2": "LAX-W10",
332 + "port1": 10,
333 + "port2": 1
334 + },
335 + "type": "pktOptLink"
336 + },
337 +
338 + {
339 + "allowed": true,
340 + "nodeDpid1": "00:00:ff:ff:ff:ff:00:04",
341 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:04",
342 + "params": {
343 + "nodeName1": "SDG-R10",
344 + "nodeName2": "SDG-W10",
345 + "port1": 10,
346 + "port2": 1
347 + },
348 + "type": "pktOptLink"
349 + },
350 +
351 + {
352 + "allowed": true,
353 + "nodeDpid1": "00:00:ff:ff:ff:ff:00:07",
354 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:07",
355 + "params": {
356 + "nodeName1": "CHG-R10",
357 + "nodeName2": "CHG-W10",
358 + "port1": 10,
359 + "port2": 1
360 + },
361 + "type": "pktOptLink"
362 + },
363 +
364 + {
365 + "allowed": true,
366 + "nodeDpid1": "00:00:ff:ff:ff:ff:00:09",
367 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:09",
368 + "params": {
369 + "nodeName1": "JFK-R10",
370 + "nodeName2": "JFK-W10",
371 + "port1": 10,
372 + "port2": 1
373 + },
374 + "type": "pktOptLink"
375 + },
376 +
377 + {
378 + "allowed": true,
379 + "nodeDpid1": "00:00:ff:ff:ff:ff:00:0A",
380 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:0A",
381 + "params": {
382 + "nodeName1": "ATL-R10",
383 + "nodeName2": "ATL-W10",
384 + "port1": 10,
385 + "port2": 1
386 + },
387 + "type": "pktOptLink"
388 + },
389 +
390 + ]
391 +}
1 +{
2 + "opticalSwitches": [
3 + {
4 + "allowed": true,
5 + "latitude": 37.6,
6 + "longitude": 122.3,
7 + "name": "ROADM1",
8 + "nodeDpid": "00:00:ff:ff:ff:ff:ff:01",
9 + "params": {
10 + "numRegen": 0
11 + },
12 + "type": "Roadm"
13 + },
14 +
15 + {
16 + "allowed": true,
17 + "latitude": 37.3,
18 + "longitude": 121.9,
19 + "name": "ROADM2",
20 + "nodeDpid": "00:00:ff:ff:ff:ff:ff:02",
21 + "params": {
22 + "numRegen": 0
23 + },
24 + "type": "Roadm"
25 + },
26 +
27 + {
28 + "allowed": true,
29 + "latitude": 33.9,
30 + "longitude": 118.4,
31 + "name": "ROADM3",
32 + "nodeDpid": "00:00:ff:ff:ff:ff:ff:03",
33 + "params": {
34 + "numRegen": 2
35 + },
36 + "type": "Roadm"
37 + }
38 + ],
39 +
40 + "opticalLinks": [
41 + {
42 + "allowed": true,
43 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:01",
44 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:03",
45 + "params": {
46 + "distKms": 1000,
47 + "nodeName1": "ROADM1",
48 + "nodeName2": "ROADM3",
49 + "numWaves": 80,
50 + "port1": 10,
51 + "port2": 30
52 + },
53 + "type": "wdmLink"
54 + },
55 +
56 + {
57 + "allowed": true,
58 + "nodeDpid1": "00:00:ff:ff:ff:ff:ff:03",
59 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:02",
60 + "params": {
61 + "distKms": 2000,
62 + "nodeName1": "ROADM3",
63 + "nodeName2": "ROADM2",
64 + "numWaves": 80,
65 + "port1": 31,
66 + "port2": 20
67 + },
68 + "type": "wdmLink"
69 + },
70 +
71 +
72 + {
73 + "allowed": true,
74 + "nodeDpid1": "00:00:ff:ff:ff:ff:00:01",
75 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:01",
76 + "params": {
77 + "nodeName1": "ROUTER1",
78 + "nodeName2": "ROADM1",
79 + "bandWidth": 100000,
80 + "port1": 10,
81 + "port2": 11
82 + },
83 + "type": "pktOptLink"
84 + },
85 +
86 + {
87 + "allowed": true,
88 + "nodeDpid1": "00:00:ff:ff:ff:ff:00:02",
89 + "nodeDpid2": "00:00:ff:ff:ff:ff:ff:02",
90 + "params": {
91 + "nodeName1": "ROUTER2",
92 + "nodeName2": "ROADM2",
93 + "bandWidth": 100000,
94 + "port1": 10,
95 + "port2": 21
96 + },
97 + "type": "pktOptLink"
98 + }
99 +
100 + ]
101 +}
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
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>
29 + <module>optical</module>
28 </modules> 30 </modules>
29 31
30 <properties> 32 <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>
......
...@@ -18,11 +18,11 @@ import com.google.common.collect.Sets; ...@@ -18,11 +18,11 @@ import com.google.common.collect.Sets;
18 /** 18 /**
19 * Provides IntefaceService using PortAddresses data from the HostService. 19 * Provides IntefaceService using PortAddresses data from the HostService.
20 */ 20 */
21 -public class HostServiceBasedInterfaceService implements InterfaceService { 21 +public class HostToInterfaceAdaptor implements InterfaceService {
22 22
23 private final HostService hostService; 23 private final HostService hostService;
24 24
25 - public HostServiceBasedInterfaceService(HostService hostService) { 25 + public HostToInterfaceAdaptor(HostService hostService) {
26 this.hostService = checkNotNull(hostService); 26 this.hostService = checkNotNull(hostService);
27 } 27 }
28 28
......
...@@ -25,10 +25,10 @@ import org.slf4j.LoggerFactory; ...@@ -25,10 +25,10 @@ import org.slf4j.LoggerFactory;
25 /** 25 /**
26 * Manages the connectivity requirements between peers. 26 * Manages the connectivity requirements between peers.
27 */ 27 */
28 -public class PeerConnectivity { 28 +public class PeerConnectivityManager {
29 29
30 private static final Logger log = LoggerFactory.getLogger( 30 private static final Logger log = LoggerFactory.getLogger(
31 - PeerConnectivity.class); 31 + PeerConnectivityManager.class);
32 32
33 // TODO these shouldn't be defined here 33 // TODO these shouldn't be defined here
34 private static final short BGP_PORT = 179; 34 private static final short BGP_PORT = 179;
...@@ -41,7 +41,7 @@ public class PeerConnectivity { ...@@ -41,7 +41,7 @@ public class PeerConnectivity {
41 // TODO this sucks. 41 // TODO this sucks.
42 private int intentId = 0; 42 private int intentId = 0;
43 43
44 - public PeerConnectivity(SdnIpConfigService configInfoService, 44 + public PeerConnectivityManager(SdnIpConfigService configInfoService,
45 InterfaceService interfaceService, IntentService intentService) { 45 InterfaceService interfaceService, IntentService intentService) {
46 this.configInfoService = configInfoService; 46 this.configInfoService = configInfoService;
47 this.interfaceService = interfaceService; 47 this.interfaceService = interfaceService;
...@@ -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 + // TODO hack to enable SDN-IP now for testing
137 + isElectedLeader = true;
138 + isActivatedLeader = true;
139 +
140 + bgpUpdatesExecutor.execute(new Runnable() {
141 + @Override
142 + public void run() {
143 + doUpdatesThread();
144 + }
145 + });
146 +
147 + bgpIntentsSynchronizerExecutor.execute(new Runnable() {
148 + @Override
149 + public void run() {
150 + doIntentSynchronizationThread();
151 + }
152 + });
153 + }
154 +
155 + //@Override TODO hook this up to something
156 + public void leaderChanged(boolean isLeader) {
157 + log.debug("Leader changed: {}", isLeader);
158 +
159 + if (!isLeader) {
160 + this.isElectedLeader = false;
161 + this.isActivatedLeader = false;
162 + return; // Nothing to do
163 + }
164 + this.isActivatedLeader = false;
165 + this.isElectedLeader = true;
166 +
167 + //
168 + // Tell the Intents Synchronizer thread to start the synchronization
169 + //
170 + intentsSynchronizerSemaphore.release();
171 + }
172 +
173 + @Override
174 + public void update(RouteUpdate routeUpdate) {
175 + log.debug("Received new route Update: {}", routeUpdate);
176 +
177 + try {
178 + routeUpdates.put(routeUpdate);
179 + } catch (InterruptedException e) {
180 + log.debug("Interrupted while putting on routeUpdates queue", e);
181 + Thread.currentThread().interrupt();
182 + }
183 + }
184 +
185 + /**
186 + * Thread for Intent Synchronization.
187 + */
188 + private void doIntentSynchronizationThread() {
189 + boolean interrupted = false;
190 + try {
191 + while (!interrupted) {
192 + try {
193 + intentsSynchronizerSemaphore.acquire();
194 + //
195 + // Drain all permits, because a single synchronization is
196 + // sufficient.
197 + //
198 + intentsSynchronizerSemaphore.drainPermits();
199 + } catch (InterruptedException e) {
200 + log.debug("Interrupted while waiting to become " +
201 + "Intent Synchronization leader");
202 + interrupted = true;
203 + break;
204 + }
205 + syncIntents();
206 + }
207 + } finally {
208 + if (interrupted) {
209 + Thread.currentThread().interrupt();
210 + }
211 + }
212 + }
213 +
214 + /**
215 + * Thread for handling route updates.
216 + */
217 + private void doUpdatesThread() {
218 + boolean interrupted = false;
219 + try {
220 + while (!interrupted) {
221 + try {
222 + RouteUpdate update = routeUpdates.take();
223 + switch (update.type()) {
224 + case UPDATE:
225 + processRouteAdd(update.routeEntry());
226 + break;
227 + case DELETE:
228 + processRouteDelete(update.routeEntry());
229 + break;
230 + default:
231 + log.error("Unknown update Type: {}", update.type());
232 + break;
233 + }
234 + } catch (InterruptedException e) {
235 + log.debug("Interrupted while taking from updates queue", e);
236 + interrupted = true;
237 + } catch (Exception e) {
238 + log.debug("exception", e);
239 + }
240 + }
241 + } finally {
242 + if (interrupted) {
243 + Thread.currentThread().interrupt();
244 + }
245 + }
246 + }
247 +
248 + /**
249 + * Performs Intents Synchronization between the internally stored Route
250 + * Intents and the installed Route Intents.
251 + */
252 + private void syncIntents() {
253 + synchronized (this) {
254 + if (!isElectedLeader) {
255 + return; // Nothing to do: not the leader anymore
256 + }
257 + log.debug("Syncing SDN-IP Route Intents...");
258 +
259 + Map<IpPrefix, MultiPointToSinglePointIntent> fetchedIntents =
260 + new HashMap<>();
261 +
262 + //
263 + // Fetch all intents, and classify the Multi-Point-to-Point Intents
264 + // based on the matching prefix.
265 + //
266 + for (Intent intent : intentService.getIntents()) {
267 + //
268 + // TODO: Ignore all intents that are not installed by
269 + // the SDN-IP application.
270 + //
271 + if (!(intent instanceof MultiPointToSinglePointIntent)) {
272 + continue;
273 + }
274 + MultiPointToSinglePointIntent mp2pIntent =
275 + (MultiPointToSinglePointIntent) intent;
276 + /*Match match = mp2pIntent.getMatch();
277 + if (!(match instanceof PacketMatch)) {
278 + continue;
279 + }
280 + PacketMatch packetMatch = (PacketMatch) match;
281 + Ip4Prefix prefix = packetMatch.getDstIpAddress();
282 + if (prefix == null) {
283 + continue;
284 + }
285 + fetchedIntents.put(prefix, mp2pIntent);*/
286 + for (Criterion criterion : mp2pIntent.selector().criteria()) {
287 + if (criterion.type() == Type.IPV4_DST) {
288 + IPCriterion ipCriterion = (IPCriterion) criterion;
289 + fetchedIntents.put(ipCriterion.ip(), mp2pIntent);
290 + }
291 + }
292 +
293 + }
294 +
295 + //
296 + // Compare for each prefix the local IN-MEMORY Intents with the
297 + // FETCHED Intents:
298 + // - If the IN-MEMORY Intent is same as the FETCHED Intent, store
299 + // the FETCHED Intent in the local memory (i.e., override the
300 + // IN-MEMORY Intent) to preserve the original Intent ID
301 + // - if the IN-MEMORY Intent is not same as the FETCHED Intent,
302 + // delete the FETCHED Intent, and push/install the IN-MEMORY
303 + // Intent.
304 + // - If there is an IN-MEMORY Intent for a prefix, but no FETCHED
305 + // Intent for same prefix, then push/install the IN-MEMORY
306 + // Intent.
307 + // - If there is a FETCHED Intent for a prefix, but no IN-MEMORY
308 + // Intent for same prefix, then delete/withdraw the FETCHED
309 + // Intent.
310 + //
311 + Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>>
312 + storeInMemoryIntents = new LinkedList<>();
313 + Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>>
314 + addIntents = new LinkedList<>();
315 + Collection<Pair<IpPrefix, MultiPointToSinglePointIntent>>
316 + deleteIntents = new LinkedList<>();
317 + for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry :
318 + pushedRouteIntents.entrySet()) {
319 + IpPrefix prefix = entry.getKey();
320 + MultiPointToSinglePointIntent inMemoryIntent =
321 + entry.getValue();
322 + MultiPointToSinglePointIntent fetchedIntent =
323 + fetchedIntents.get(prefix);
324 +
325 + if (fetchedIntent == null) {
326 + //
327 + // No FETCHED Intent for same prefix: push the IN-MEMORY
328 + // Intent.
329 + //
330 + addIntents.add(Pair.of(prefix, inMemoryIntent));
331 + continue;
332 + }
333 +
334 + //
335 + // If IN-MEMORY Intent is same as the FETCHED Intent,
336 + // store the FETCHED Intent in the local memory.
337 + //
338 + if (compareMultiPointToSinglePointIntents(inMemoryIntent,
339 + fetchedIntent)) {
340 + storeInMemoryIntents.add(Pair.of(prefix, fetchedIntent));
341 + } else {
342 + //
343 + // The IN-MEMORY Intent is not same as the FETCHED Intent,
344 + // hence delete the FETCHED Intent, and install the
345 + // IN-MEMORY Intent.
346 + //
347 + deleteIntents.add(Pair.of(prefix, fetchedIntent));
348 + addIntents.add(Pair.of(prefix, inMemoryIntent));
349 + }
350 + fetchedIntents.remove(prefix);
351 + }
352 +
353 + //
354 + // Any remaining FETCHED Intents have to be deleted/withdrawn
355 + //
356 + for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry :
357 + fetchedIntents.entrySet()) {
358 + IpPrefix prefix = entry.getKey();
359 + MultiPointToSinglePointIntent fetchedIntent = entry.getValue();
360 + deleteIntents.add(Pair.of(prefix, fetchedIntent));
361 + }
362 +
363 + //
364 + // Perform the actions:
365 + // 1. Store in memory fetched intents that are same. Can be done
366 + // even if we are not the leader anymore
367 + // 2. Delete intents: check if the leader before each operation
368 + // 3. Add intents: check if the leader before each operation
369 + //
370 + for (Pair<IpPrefix, MultiPointToSinglePointIntent> pair :
371 + storeInMemoryIntents) {
372 + IpPrefix prefix = pair.getLeft();
373 + MultiPointToSinglePointIntent intent = pair.getRight();
374 + log.debug("Intent synchronization: updating in-memory " +
375 + "Intent for prefix: {}", prefix);
376 + pushedRouteIntents.put(prefix, intent);
377 + }
378 + //
379 + isActivatedLeader = true; // Allow push of Intents
380 + for (Pair<IpPrefix, MultiPointToSinglePointIntent> pair :
381 + deleteIntents) {
382 + IpPrefix prefix = pair.getLeft();
383 + MultiPointToSinglePointIntent intent = pair.getRight();
384 + if (!isElectedLeader) {
385 + isActivatedLeader = false;
386 + return;
387 + }
388 + log.debug("Intent synchronization: deleting Intent for " +
389 + "prefix: {}", prefix);
390 + intentService.withdraw(intent);
391 + }
392 + //
393 + for (Pair<IpPrefix, MultiPointToSinglePointIntent> pair :
394 + addIntents) {
395 + IpPrefix prefix = pair.getLeft();
396 + MultiPointToSinglePointIntent intent = pair.getRight();
397 + if (!isElectedLeader) {
398 + isActivatedLeader = false;
399 + return;
400 + }
401 + log.debug("Intent synchronization: adding Intent for " +
402 + "prefix: {}", prefix);
403 + intentService.submit(intent);
404 + }
405 + if (!isElectedLeader) {
406 + isActivatedLeader = false;
407 + }
408 + log.debug("Syncing SDN-IP routes completed.");
409 + }
410 + }
411 +
412 + /**
413 + * Compares two Multi-point to Single Point Intents whether they represent
414 + * same logical intention.
415 + *
416 + * @param intent1 the first Intent to compare
417 + * @param intent2 the second Intent to compare
418 + * @return true if both Intents represent same logical intention, otherwise
419 + * false
420 + */
421 + private boolean compareMultiPointToSinglePointIntents(
422 + MultiPointToSinglePointIntent intent1,
423 + MultiPointToSinglePointIntent intent2) {
424 + /*Match match1 = intent1.getMatch();
425 + Match match2 = intent2.getMatch();
426 + Action action1 = intent1.getAction();
427 + Action action2 = intent2.getAction();
428 + Set<SwitchPort> ingressPorts1 = intent1.getIngressPorts();
429 + Set<SwitchPort> ingressPorts2 = intent2.getIngressPorts();
430 + SwitchPort egressPort1 = intent1.getEgressPort();
431 + SwitchPort egressPort2 = intent2.getEgressPort();
432 +
433 + return Objects.equal(match1, match2) &&
434 + Objects.equal(action1, action2) &&
435 + Objects.equal(egressPort1, egressPort2) &&
436 + Objects.equal(ingressPorts1, ingressPorts2);*/
437 + return Objects.equal(intent1.selector(), intent2.selector()) &&
438 + Objects.equal(intent1.treatment(), intent2.treatment()) &&
439 + Objects.equal(intent1.ingressPoints(), intent2.ingressPoints()) &&
440 + Objects.equal(intent1.egressPoint(), intent2.egressPoint());
441 + }
442 +
443 + /**
444 + * Processes adding a route entry.
445 + * <p/>
446 + * Put new route entry into the radix tree. If there was an existing
447 + * next hop for this prefix, but the next hop was different, then execute
448 + * deleting old route entry. If the next hop is the SDN domain, we do not
449 + * handle it at the moment. Otherwise, execute adding a route.
450 + *
451 + * @param routeEntry the route entry to add
452 + */
453 + protected void processRouteAdd(RouteEntry routeEntry) {
454 + synchronized (this) {
455 + log.debug("Processing route add: {}", routeEntry);
456 +
457 + IpPrefix prefix = routeEntry.prefix();
458 + IpAddress nextHop = null;
459 + RouteEntry foundRouteEntry =
460 + bgpRoutes.put(RouteEntry.createBinaryString(prefix),
461 + routeEntry);
462 + if (foundRouteEntry != null) {
463 + nextHop = foundRouteEntry.nextHop();
464 + }
465 +
466 + if (nextHop != null && !nextHop.equals(routeEntry.nextHop())) {
467 + // There was an existing nexthop for this prefix. This update
468 + // supersedes that, so we need to remove the old flows for this
469 + // prefix from the switches
470 + executeRouteDelete(routeEntry);
471 + }
472 + if (nextHop != null && nextHop.equals(routeEntry.nextHop())) {
473 + return;
474 + }
475 +
476 + if (routeEntry.nextHop().equals(LOCAL_NEXT_HOP)) {
477 + // Route originated by SDN domain
478 + // We don't handle these at the moment
479 + log.debug("Own route {} to {}",
480 + routeEntry.prefix(), routeEntry.nextHop());
481 + return;
482 + }
483 +
484 + executeRouteAdd(routeEntry);
485 + }
486 + }
487 +
488 + /**
489 + * Executes adding a route entry.
490 + * <p/>
491 + * Find out the egress Interface and MAC address of next hop router for
492 + * this route entry. If the MAC address can not be found in ARP cache,
493 + * then this prefix will be put in routesWaitingOnArp queue. Otherwise,
494 + * new route intent will be created and installed.
495 + *
496 + * @param routeEntry the route entry to add
497 + */
498 + private void executeRouteAdd(RouteEntry routeEntry) {
499 + log.debug("Executing route add: {}", routeEntry);
500 +
501 + // See if we know the MAC address of the next hop
502 + //MacAddress nextHopMacAddress =
503 + //proxyArp.getMacAddress(routeEntry.getNextHop());
504 + MacAddress nextHopMacAddress = null;
505 + Set<Host> hosts = hostService.getHostsByIp(
506 + routeEntry.nextHop().toPrefix());
507 + if (!hosts.isEmpty()) {
508 + // TODO how to handle if multiple hosts are returned?
509 + nextHopMacAddress = hosts.iterator().next().mac();
510 + }
511 +
512 + if (nextHopMacAddress == null) {
513 + routesWaitingOnArp.put(routeEntry.nextHop(), routeEntry);
514 + //proxyArp.sendArpRequest(routeEntry.getNextHop(), this, true);
515 + // TODO maybe just do this for every prefix anyway
516 + hostService.startMonitoringIp(routeEntry.nextHop());
517 + return;
518 + }
519 +
520 + addRouteIntentToNextHop(routeEntry.prefix(),
521 + routeEntry.nextHop(),
522 + nextHopMacAddress);
523 + }
524 +
525 + /**
526 + * Adds a route intent given a prefix and a next hop IP address. This
527 + * method will find the egress interface for the intent.
528 + *
529 + * @param prefix IP prefix of the route to add
530 + * @param nextHopIpAddress IP address of the next hop
531 + * @param nextHopMacAddress MAC address of the next hop
532 + */
533 + private void addRouteIntentToNextHop(IpPrefix prefix,
534 + IpAddress nextHopIpAddress,
535 + MacAddress nextHopMacAddress) {
536 +
537 + // Find the attachment point (egress interface) of the next hop
538 + Interface egressInterface;
539 + if (configInfoService.getBgpPeers().containsKey(nextHopIpAddress)) {
540 + // Route to a peer
541 + log.debug("Route to peer {}", nextHopIpAddress);
542 + BgpPeer peer =
543 + configInfoService.getBgpPeers().get(nextHopIpAddress);
544 + egressInterface =
545 + interfaceService.getInterface(peer.connectPoint());
546 + } else {
547 + // Route to non-peer
548 + log.debug("Route to non-peer {}", nextHopIpAddress);
549 + egressInterface =
550 + interfaceService.getMatchingInterface(nextHopIpAddress);
551 + if (egressInterface == null) {
552 + log.warn("No outgoing interface found for {}",
553 + nextHopIpAddress);
554 + return;
555 + }
556 + }
557 +
558 + doAddRouteIntent(prefix, egressInterface, nextHopMacAddress);
559 + }
560 +
561 + /**
562 + * Installs a route intent for a prefix.
563 + * <p/>
564 + * Intent will match dst IP prefix and rewrite dst MAC address at all other
565 + * border switches, then forward packets according to dst MAC address.
566 + *
567 + * @param prefix IP prefix from route
568 + * @param egressInterface egress Interface connected to next hop router
569 + * @param nextHopMacAddress MAC address of next hop router
570 + */
571 + private void doAddRouteIntent(IpPrefix prefix, Interface egressInterface,
572 + MacAddress nextHopMacAddress) {
573 + log.debug("Adding intent for prefix {}, next hop mac {}",
574 + prefix, nextHopMacAddress);
575 +
576 + MultiPointToSinglePointIntent pushedIntent =
577 + pushedRouteIntents.get(prefix);
578 +
579 + // Just for testing.
580 + if (pushedIntent != null) {
581 + log.error("There should not be a pushed intent: {}", pushedIntent);
582 + }
583 +
584 + ConnectPoint egressPort = egressInterface.connectPoint();
585 +
586 + Set<ConnectPoint> ingressPorts = new HashSet<>();
587 +
588 + for (Interface intf : interfaceService.getInterfaces()) {
589 + if (!intf.equals(egressInterface)) {
590 + ConnectPoint srcPort = intf.connectPoint();
591 + ingressPorts.add(srcPort);
592 + }
593 + }
594 +
595 + // Match the destination IP prefix at the first hop
596 + //PacketMatchBuilder builder = new PacketMatchBuilder();
597 + //builder.setEtherType(Ethernet.TYPE_IPV4).setDstIpNet(prefix);
598 + //PacketMatch packetMatch = builder.build();
599 + TrafficSelector selector = DefaultTrafficSelector.builder()
600 + .matchEthType(Ethernet.TYPE_IPV4)
601 + .matchIPDst(prefix)
602 + .build();
603 +
604 + // Rewrite the destination MAC address
605 + //ModifyDstMacAction modifyDstMacAction =
606 + //new ModifyDstMacAction(nextHopMacAddress);
607 + TrafficTreatment treatment = DefaultTrafficTreatment.builder()
608 + .setEthDst(nextHopMacAddress)
609 + .build();
610 +
611 + MultiPointToSinglePointIntent intent =
612 + new MultiPointToSinglePointIntent(nextIntentId(),
613 + selector, treatment, ingressPorts, egressPort);
614 +
615 + if (isElectedLeader && isActivatedLeader) {
616 + log.debug("Intent installation: adding Intent for prefix: {}",
617 + prefix);
618 + intentService.submit(intent);
619 + }
620 +
621 + // Maintain the Intent
622 + pushedRouteIntents.put(prefix, intent);
623 + }
624 +
625 + /**
626 + * Executes deleting a route entry.
627 + * <p/>
628 + * Removes prefix from radix tree, and if successful, then try to delete
629 + * the related intent.
630 + *
631 + * @param routeEntry the route entry to delete
632 + */
633 + protected void processRouteDelete(RouteEntry routeEntry) {
634 + synchronized (this) {
635 + log.debug("Processing route delete: {}", routeEntry);
636 + IpPrefix prefix = routeEntry.prefix();
637 +
638 + // TODO check the change of logic here - remove doesn't check that
639 + // the route entry was what we expected (and we can't do this
640 + // concurrently)
641 +
642 + if (bgpRoutes.remove(RouteEntry.createBinaryString(prefix))) {
643 + //
644 + // Only delete flows if an entry was actually removed from the
645 + // tree. If no entry was removed, the <prefix, nexthop> wasn't
646 + // there so it's probably already been removed and we don't
647 + // need to do anything.
648 + //
649 + executeRouteDelete(routeEntry);
650 + }
651 +
652 + routesWaitingOnArp.remove(routeEntry.nextHop(), routeEntry);
653 + // TODO cancel the request in the ARP manager as well
654 + }
655 + }
656 +
657 + /**
658 + * Executed deleting a route entry.
659 + *
660 + * @param routeEntry the route entry to delete
661 + */
662 + private void executeRouteDelete(RouteEntry routeEntry) {
663 + log.debug("Executing route delete: {}", routeEntry);
664 +
665 + IpPrefix prefix = routeEntry.prefix();
666 +
667 + MultiPointToSinglePointIntent intent =
668 + pushedRouteIntents.remove(prefix);
669 +
670 + if (intent == null) {
671 + log.debug("There is no intent in pushedRouteIntents to delete " +
672 + "for prefix: {}", prefix);
673 + } else {
674 + if (isElectedLeader && isActivatedLeader) {
675 + log.debug("Intent installation: deleting Intent for prefix: {}",
676 + prefix);
677 + intentService.withdraw(intent);
678 + }
679 + }
680 + }
681 +
682 + /**
683 + * This method handles the prefixes which are waiting for ARP replies for
684 + * MAC addresses of next hops.
685 + *
686 + * @param ipAddress next hop router IP address, for which we sent ARP
687 + * request out
688 + * @param macAddress MAC address which is relative to the ipAddress
689 + */
690 + //@Override
691 + // TODO change name
692 + public void arpResponse(IpAddress ipAddress, MacAddress macAddress) {
693 + log.debug("Received ARP response: {} => {}", ipAddress, macAddress);
694 +
695 + // We synchronize on this to prevent changes to the radix tree
696 + // while we're pushing intents. If the tree changes, the
697 + // tree and intents could get out of sync.
698 + synchronized (this) {
699 +
700 + Set<RouteEntry> routesToPush =
701 + routesWaitingOnArp.removeAll(ipAddress);
702 +
703 + for (RouteEntry routeEntry : routesToPush) {
704 + // These will always be adds
705 + IpPrefix prefix = routeEntry.prefix();
706 + String binaryString = RouteEntry.createBinaryString(prefix);
707 + RouteEntry foundRouteEntry =
708 + bgpRoutes.getValueForExactKey(binaryString);
709 + if (foundRouteEntry != null &&
710 + foundRouteEntry.nextHop().equals(routeEntry.nextHop())) {
711 + log.debug("Pushing prefix {} next hop {}",
712 + routeEntry.prefix(), routeEntry.nextHop());
713 + // We only push prefix flows if the prefix is still in the
714 + // radix tree and the next hop is the same as our
715 + // update.
716 + // The prefix could have been removed while we were waiting
717 + // for the ARP, or the next hop could have changed.
718 + addRouteIntentToNextHop(prefix, ipAddress, macAddress);
719 + } else {
720 + log.debug("Received ARP response, but {}/{} is no longer in"
721 + + " the radix tree", routeEntry.prefix(),
722 + routeEntry.nextHop());
723 + }
724 + }
725 + }
726 + }
727 +
728 + /**
729 + * Gets the SDN-IP routes.
730 + *
731 + * @return the SDN-IP routes
732 + */
733 + public Collection<RouteEntry> getRoutes() {
734 + Iterator<KeyValuePair<RouteEntry>> it =
735 + bgpRoutes.getKeyValuePairsForKeysStartingWith("").iterator();
736 +
737 + List<RouteEntry> routes = new LinkedList<>();
738 +
739 + while (it.hasNext()) {
740 + KeyValuePair<RouteEntry> entry = it.next();
741 + routes.add(entry.getValue());
742 + }
743 +
744 + return routes;
745 + }
746 +
747 + /**
748 + * Generates a new unique intent ID.
749 + *
750 + * @return the new intent ID.
751 + */
752 + private IntentId nextIntentId() {
753 + return new IntentId(intentId++);
754 + }
755 +
756 + /**
757 + * Listener for host events.
758 + */
759 + class InternalHostListener implements HostListener {
760 + @Override
761 + public void event(HostEvent event) {
762 + if (event.type() == HostEvent.Type.HOST_ADDED ||
763 + event.type() == HostEvent.Type.HOST_UPDATED) {
764 + Host host = event.subject();
765 + for (IpPrefix ip : host.ipAddresses()) {
766 + arpResponse(ip.toIpAddress(), host.mac());
767 + }
768 + }
769 + }
770 + }
771 +}
...@@ -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
...@@ -27,7 +36,9 @@ public class SdnIp { ...@@ -27,7 +36,9 @@ public class SdnIp {
27 protected HostService hostService; 36 protected HostService hostService;
28 37
29 private SdnIpConfigReader config; 38 private SdnIpConfigReader config;
30 - private PeerConnectivity peerConnectivity; 39 + private PeerConnectivityManager peerConnectivity;
40 + private Router router;
41 + private BgpSessionManager bgpSessionManager;
31 42
32 @Activate 43 @Activate
33 protected void activate() { 44 protected void activate() {
...@@ -36,15 +47,40 @@ public class SdnIp { ...@@ -36,15 +47,40 @@ public class SdnIp {
36 config = new SdnIpConfigReader(); 47 config = new SdnIpConfigReader();
37 config.init(); 48 config.init();
38 49
39 - InterfaceService interfaceService = new HostServiceBasedInterfaceService(hostService); 50 + InterfaceService interfaceService = new HostToInterfaceAdaptor(hostService);
40 51
41 - peerConnectivity = new PeerConnectivity(config, interfaceService, intentService); 52 + peerConnectivity = new PeerConnectivityManager(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 + }
82 +
83 + static String dpidToUri(String dpid) {
84 + return "of:" + dpid.replace(":", "");
85 + }
50 } 86 }
......
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.easymock.EasyMock.createMock;
4 +import static org.easymock.EasyMock.expect;
5 +import static org.easymock.EasyMock.replay;
6 +import static org.easymock.EasyMock.reportMatcher;
7 +import static org.easymock.EasyMock.reset;
8 +import static org.easymock.EasyMock.verify;
9 +
10 +import java.util.ArrayList;
11 +import java.util.Collections;
12 +import java.util.HashMap;
13 +import java.util.LinkedList;
14 +import java.util.List;
15 +import java.util.Map;
16 +
17 +import org.easymock.IArgumentMatcher;
18 +import org.junit.Before;
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.PortNumber;
24 +import org.onlab.onos.net.flow.DefaultTrafficSelector;
25 +import org.onlab.onos.net.flow.DefaultTrafficTreatment;
26 +import org.onlab.onos.net.flow.TrafficSelector;
27 +import org.onlab.onos.net.flow.TrafficTreatment;
28 +import org.onlab.onos.net.intent.IntentId;
29 +import org.onlab.onos.net.intent.IntentService;
30 +import org.onlab.onos.net.intent.PointToPointIntent;
31 +import org.onlab.onos.sdnip.bgp.BgpConstants;
32 +import org.onlab.onos.sdnip.config.BgpPeer;
33 +import org.onlab.onos.sdnip.config.BgpSpeaker;
34 +import org.onlab.onos.sdnip.config.Interface;
35 +import org.onlab.onos.sdnip.config.InterfaceAddress;
36 +import org.onlab.onos.sdnip.config.SdnIpConfigService;
37 +import org.onlab.packet.Ethernet;
38 +import org.onlab.packet.IPv4;
39 +import org.onlab.packet.IpAddress;
40 +import org.onlab.packet.IpPrefix;
41 +import org.onlab.packet.MacAddress;
42 +
43 +import com.google.common.collect.Sets;
44 +
45 +/**
46 + * Unit tests for PeerConnectivityManager interface.
47 + */
48 +public class PeerConnectivityManagerTest {
49 +
50 + private PeerConnectivityManager peerConnectivityManager;
51 + private IntentService intentService;
52 + private SdnIpConfigService configInfoService;
53 + private InterfaceService interfaceService;
54 +
55 + private Map<String, BgpSpeaker> bgpSpeakers;
56 + private Map<String, Interface> interfaces;
57 + private Map<IpAddress, BgpPeer> peers;
58 +
59 + private Map<String, BgpSpeaker> configuredBgpSpeakers;
60 + private Map<String, Interface> configuredInterfaces;
61 + private Map<IpAddress, BgpPeer> configuredPeers;
62 + private List<PointToPointIntent> intentList;
63 +
64 + private final String dpid1 = "00:00:00:00:00:00:00:01";
65 + private final String dpid2 = "00:00:00:00:00:00:00:02";
66 +
67 + private final DeviceId deviceId1 =
68 + DeviceId.deviceId(SdnIp.dpidToUri(dpid1));
69 + private final DeviceId deviceId2 =
70 + DeviceId.deviceId(SdnIp.dpidToUri(dpid2));
71 +
72 + // Interfaces connected to BGP speakers
73 + private final ConnectPoint s1Eth100 =
74 + new ConnectPoint(deviceId1, PortNumber.portNumber(100));
75 + private final ConnectPoint s2Eth100 =
76 + new ConnectPoint(deviceId2, PortNumber.portNumber(100));
77 +
78 + // Interfaces connected to BGP peers
79 + private final ConnectPoint s1Eth1 =
80 + new ConnectPoint(deviceId1, PortNumber.portNumber(1));
81 + private final ConnectPoint s2Eth1 =
82 + new ConnectPoint(deviceId2, PortNumber.portNumber(1));
83 +
84 + // We don't compare the intent ID so all expected intents can use the same ID
85 + private final IntentId testIntentId = new IntentId(0);
86 +
87 + private final TrafficTreatment noTreatment =
88 + DefaultTrafficTreatment.builder().build();
89 +
90 + @Before
91 + public void setUp() throws Exception {
92 + bgpSpeakers = Collections.unmodifiableMap(setUpBgpSpeakers());
93 + interfaces = Collections.unmodifiableMap(setUpInterfaces());
94 + peers = Collections.unmodifiableMap(setUpPeers());
95 +
96 + initPeerConnectivity();
97 + intentList = setUpIntentList();
98 + }
99 +
100 + /**
101 + * Sets up BGP speakers.
102 + *
103 + * @return configured BGP speakers as a map from speaker name to speaker
104 + */
105 + private Map<String, BgpSpeaker> setUpBgpSpeakers() {
106 +
107 + configuredBgpSpeakers = new HashMap<>();
108 +
109 + BgpSpeaker bgpSpeaker1 = new BgpSpeaker(
110 + "bgpSpeaker1",
111 + "00:00:00:00:00:00:00:01", 100,
112 + "00:00:00:00:00:01");
113 + List<InterfaceAddress> interfaceAddresses1 =
114 + new LinkedList<InterfaceAddress>();
115 + interfaceAddresses1.add(new InterfaceAddress(dpid1, 1, "192.168.10.101"));
116 + interfaceAddresses1.add(new InterfaceAddress(dpid2, 1, "192.168.20.101"));
117 + bgpSpeaker1.setInterfaceAddresses(interfaceAddresses1);
118 + configuredBgpSpeakers.put(bgpSpeaker1.name(), bgpSpeaker1);
119 +
120 + // BGP speaker2 is attached to the same switch port with speaker1
121 + BgpSpeaker bgpSpeaker2 = new BgpSpeaker(
122 + "bgpSpeaker2",
123 + "00:00:00:00:00:00:00:01", 100,
124 + "00:00:00:00:00:02");
125 + List<InterfaceAddress> interfaceAddresses2 =
126 + new LinkedList<InterfaceAddress>();
127 + interfaceAddresses2.add(new InterfaceAddress(dpid1, 1, "192.168.10.102"));
128 + interfaceAddresses2.add(new InterfaceAddress(dpid2, 1, "192.168.20.102"));
129 + bgpSpeaker2.setInterfaceAddresses(interfaceAddresses2);
130 + configuredBgpSpeakers.put(bgpSpeaker2.name(), bgpSpeaker2);
131 +
132 + BgpSpeaker bgpSpeaker3 = new BgpSpeaker(
133 + "bgpSpeaker3",
134 + "00:00:00:00:00:00:00:02", 100,
135 + "00:00:00:00:00:03");
136 + List<InterfaceAddress> interfaceAddresses3 =
137 + new LinkedList<InterfaceAddress>();
138 + interfaceAddresses3.add(new InterfaceAddress(dpid1, 1, "192.168.10.103"));
139 + interfaceAddresses3.add(new InterfaceAddress(dpid2, 1, "192.168.20.103"));
140 + bgpSpeaker3.setInterfaceAddresses(interfaceAddresses3);
141 + configuredBgpSpeakers.put(bgpSpeaker3.name(), bgpSpeaker3);
142 +
143 + return configuredBgpSpeakers;
144 + }
145 +
146 + /**
147 + * Sets up logical interfaces, which emulate the configured interfaces
148 + * in SDN-IP application.
149 + *
150 + * @return configured interfaces as a MAP from Interface name to Interface
151 + */
152 + private Map<String, Interface> setUpInterfaces() {
153 +
154 + configuredInterfaces = new HashMap<>();
155 +
156 + String interfaceSw1Eth1 = "s1-eth1";
157 + Interface intfsw1eth1 = new Interface(s1Eth1,
158 + Collections.singleton(IpPrefix.valueOf("192.168.10.0/24")),
159 + MacAddress.valueOf("00:00:00:00:00:01"));
160 +
161 + configuredInterfaces.put(interfaceSw1Eth1, intfsw1eth1);
162 + String interfaceSw2Eth1 = "s2-eth1";
163 + Interface intfsw2eth1 = new Interface(s2Eth1,
164 + Collections.singleton(IpPrefix.valueOf("192.168.20.0/24")),
165 + MacAddress.valueOf("00:00:00:00:00:02"));
166 + configuredInterfaces.put(interfaceSw2Eth1, intfsw2eth1);
167 +
168 + interfaceService = createMock(InterfaceService.class);
169 +
170 + expect(interfaceService.getInterface(s1Eth1))
171 + .andReturn(intfsw1eth1).anyTimes();
172 + expect(interfaceService.getInterface(s2Eth1))
173 + .andReturn(intfsw2eth1).anyTimes();
174 +
175 + // Non-existent interface used during one of the tests
176 + expect(interfaceService.getInterface(new ConnectPoint(
177 + DeviceId.deviceId(SdnIp.dpidToUri("00:00:00:00:00:00:01:00")),
178 + PortNumber.portNumber(1))))
179 + .andReturn(null).anyTimes();
180 +
181 + expect(interfaceService.getInterfaces()).andReturn(
182 + Sets.newHashSet(configuredInterfaces.values())).anyTimes();
183 + replay(interfaceService);
184 +
185 + return configuredInterfaces;
186 + }
187 +
188 + /**
189 + * Sets up BGP daemon peers.
190 + *
191 + * @return configured BGP peers as a MAP from peer IP address to BgpPeer
192 + */
193 + private Map<IpAddress, BgpPeer> setUpPeers() {
194 +
195 + configuredPeers = new HashMap<>();
196 +
197 + String peerSw1Eth1 = "192.168.10.1";
198 + configuredPeers.put(IpAddress.valueOf(peerSw1Eth1),
199 + new BgpPeer(dpid1, 1, peerSw1Eth1));
200 +
201 + // Two BGP peers are connected to switch 2 port 1.
202 + String peer1Sw2Eth1 = "192.168.20.1";
203 + configuredPeers.put(IpAddress.valueOf(peer1Sw2Eth1),
204 + new BgpPeer(dpid2, 1, peer1Sw2Eth1));
205 +
206 + String peer2Sw2Eth1 = "192.168.20.2";
207 + configuredPeers.put(IpAddress.valueOf(peer2Sw2Eth1),
208 + new BgpPeer(dpid2, 1, peer2Sw2Eth1));
209 +
210 + return configuredPeers;
211 + }
212 +
213 + /**
214 + * Sets up expected point to point intent list.
215 + *
216 + * @return point to point intent list
217 + */
218 + private List<PointToPointIntent> setUpIntentList() {
219 +
220 + intentList = new ArrayList<PointToPointIntent>();
221 +
222 + setUpBgpIntents();
223 + setUpIcmpIntents();
224 +
225 + return intentList;
226 +
227 + }
228 +
229 + /**
230 + * Constructs a BGP intent and put it into the intentList.
231 + * <p/>
232 + * The purpose of this method is too simplify the setUpBgpIntents() method,
233 + * and to make the setUpBgpIntents() easy to read.
234 + *
235 + * @param srcPrefix source IP prefix to match
236 + * @param dstPrefix destination IP prefix to match
237 + * @param srcTcpPort source TCP port to match
238 + * @param dstTcpPort destination TCP port to match
239 + * @param srcConnectPoint source connect point for PointToPointIntent
240 + * @param dstConnectPoint destination connect point for PointToPointIntent
241 + */
242 + private void bgpPathintentConstructor(String srcPrefix, String dstPrefix,
243 + Short srcTcpPort, Short dstTcpPort,
244 + ConnectPoint srcConnectPoint, ConnectPoint dstConnectPoint) {
245 +
246 + TrafficSelector.Builder builder = DefaultTrafficSelector.builder()
247 + .matchEthType(Ethernet.TYPE_IPV4)
248 + .matchIPProtocol(IPv4.PROTOCOL_TCP)
249 + .matchIPSrc(IpPrefix.valueOf(srcPrefix))
250 + .matchIPDst(IpPrefix.valueOf(dstPrefix));
251 +
252 + if (srcTcpPort != null) {
253 + builder.matchTcpSrc(srcTcpPort);
254 + }
255 + if (dstTcpPort != null) {
256 + builder.matchTcpDst(dstTcpPort);
257 + }
258 +
259 + PointToPointIntent intent = new PointToPointIntent(
260 + testIntentId, builder.build(), noTreatment,
261 + srcConnectPoint, dstConnectPoint);
262 +
263 + intentList.add(intent);
264 + }
265 +
266 + /**
267 + * Sets up intents for BGP paths.
268 + */
269 + private void setUpBgpIntents() {
270 +
271 + Short bgpPort = Short.valueOf((short) BgpConstants.BGP_PORT);
272 +
273 + // Start to build intents between BGP speaker1 and BGP peer1
274 + bgpPathintentConstructor(
275 + "192.168.10.101/32", "192.168.10.1/32", null, bgpPort,
276 + s1Eth100, s1Eth1);
277 + bgpPathintentConstructor(
278 + "192.168.10.101/32", "192.168.10.1/32", bgpPort, null,
279 + s1Eth100, s1Eth1);
280 +
281 + bgpPathintentConstructor(
282 + "192.168.10.1/32", "192.168.10.101/32", null, bgpPort,
283 + s1Eth1, s1Eth100);
284 + bgpPathintentConstructor(
285 + "192.168.10.1/32", "192.168.10.101/32", bgpPort, null,
286 + s1Eth1, s1Eth100);
287 +
288 + // Start to build intents between BGP speaker1 and BGP peer2
289 + bgpPathintentConstructor(
290 + "192.168.20.101/32", "192.168.20.1/32", null, bgpPort,
291 + s1Eth100, s2Eth1);
292 + bgpPathintentConstructor(
293 + "192.168.20.101/32", "192.168.20.1/32", bgpPort, null,
294 + s1Eth100, s2Eth1);
295 +
296 + bgpPathintentConstructor(
297 + "192.168.20.1/32", "192.168.20.101/32", null, bgpPort,
298 + s2Eth1, s1Eth100);
299 + bgpPathintentConstructor(
300 + "192.168.20.1/32", "192.168.20.101/32", bgpPort, null,
301 + s2Eth1, s1Eth100);
302 +
303 + // Start to build intents between BGP speaker1 and BGP peer3
304 + bgpPathintentConstructor(
305 + "192.168.20.101/32", "192.168.20.2/32", null, bgpPort,
306 + s1Eth100, s2Eth1);
307 + bgpPathintentConstructor(
308 + "192.168.20.101/32", "192.168.20.2/32", bgpPort, null,
309 + s1Eth100, s2Eth1);
310 +
311 + bgpPathintentConstructor(
312 + "192.168.20.2/32", "192.168.20.101/32", null, bgpPort,
313 + s2Eth1, s1Eth100);
314 + bgpPathintentConstructor(
315 + "192.168.20.2/32", "192.168.20.101/32", bgpPort, null,
316 + s2Eth1, s1Eth100);
317 +
318 + //
319 + // Start to build intents between BGP speaker2 and BGP peer1
320 + bgpPathintentConstructor(
321 + "192.168.10.102/32", "192.168.10.1/32", null, bgpPort,
322 + s1Eth100, s1Eth1);
323 + bgpPathintentConstructor(
324 + "192.168.10.102/32", "192.168.10.1/32", bgpPort, null,
325 + s1Eth100, s1Eth1);
326 +
327 + bgpPathintentConstructor(
328 + "192.168.10.1/32", "192.168.10.102/32", null, bgpPort,
329 + s1Eth1, s1Eth100);
330 + bgpPathintentConstructor(
331 + "192.168.10.1/32", "192.168.10.102/32", bgpPort, null,
332 + s1Eth1, s1Eth100);
333 + // Start to build intents between BGP speaker2 and BGP peer2
334 + bgpPathintentConstructor(
335 + "192.168.20.102/32", "192.168.20.1/32", null, bgpPort,
336 + s1Eth100, s2Eth1);
337 + bgpPathintentConstructor(
338 + "192.168.20.102/32", "192.168.20.1/32", bgpPort, null,
339 + s1Eth100, s2Eth1);
340 +
341 + bgpPathintentConstructor(
342 + "192.168.20.1/32", "192.168.20.102/32", null, bgpPort,
343 + s2Eth1, s1Eth100);
344 + bgpPathintentConstructor(
345 + "192.168.20.1/32", "192.168.20.102/32", bgpPort, null,
346 + s2Eth1, s1Eth100);
347 +
348 + // Start to build intents between BGP speaker2 and BGP peer3
349 + bgpPathintentConstructor(
350 + "192.168.20.102/32", "192.168.20.2/32", null, bgpPort,
351 + s1Eth100, s2Eth1);
352 + bgpPathintentConstructor(
353 + "192.168.20.102/32", "192.168.20.2/32", bgpPort, null,
354 + s1Eth100, s2Eth1);
355 +
356 + bgpPathintentConstructor(
357 + "192.168.20.2/32", "192.168.20.102/32", null, bgpPort,
358 + s2Eth1, s1Eth100);
359 + bgpPathintentConstructor(
360 + "192.168.20.2/32", "192.168.20.102/32", bgpPort, null,
361 + s2Eth1, s1Eth100);
362 +
363 + //
364 + // Start to build intents between BGP speaker3 and BGP peer1
365 + bgpPathintentConstructor(
366 + "192.168.10.103/32", "192.168.10.1/32", null, bgpPort,
367 + s2Eth100, s1Eth1);
368 + bgpPathintentConstructor(
369 + "192.168.10.103/32", "192.168.10.1/32", bgpPort, null,
370 + s2Eth100, s1Eth1);
371 +
372 + bgpPathintentConstructor(
373 + "192.168.10.1/32", "192.168.10.103/32", null, bgpPort,
374 + s1Eth1, s2Eth100);
375 + bgpPathintentConstructor(
376 + "192.168.10.1/32", "192.168.10.103/32", bgpPort, null,
377 + s1Eth1, s2Eth100);
378 +
379 + // Start to build intents between BGP speaker3 and BGP peer2
380 + bgpPathintentConstructor(
381 + "192.168.20.103/32", "192.168.20.1/32", null, bgpPort,
382 + s2Eth100, s2Eth1);
383 + bgpPathintentConstructor(
384 + "192.168.20.103/32", "192.168.20.1/32", bgpPort, null,
385 + s2Eth100, s2Eth1);
386 +
387 + bgpPathintentConstructor(
388 + "192.168.20.1/32", "192.168.20.103/32", null, bgpPort,
389 + s2Eth1, s2Eth100);
390 + bgpPathintentConstructor(
391 + "192.168.20.1/32", "192.168.20.103/32", bgpPort, null,
392 + s2Eth1, s2Eth100);
393 +
394 + // Start to build intents between BGP speaker3 and BGP peer3
395 + bgpPathintentConstructor(
396 + "192.168.20.103/32", "192.168.20.2/32", null, bgpPort,
397 + s2Eth100, s2Eth1);
398 + bgpPathintentConstructor(
399 + "192.168.20.103/32", "192.168.20.2/32", bgpPort, null,
400 + s2Eth100, s2Eth1);
401 +
402 + bgpPathintentConstructor(
403 + "192.168.20.2/32", "192.168.20.103/32", null, bgpPort,
404 + s2Eth1, s2Eth100);
405 + bgpPathintentConstructor(
406 + "192.168.20.2/32", "192.168.20.103/32", bgpPort, null,
407 + s2Eth1, s2Eth100);
408 + }
409 +
410 + /**
411 + * Constructs a BGP intent and put it into the intentList.
412 + * <p/>
413 + * The purpose of this method is too simplify the setUpBgpIntents() method,
414 + * and to make the setUpBgpIntents() easy to read.
415 + *
416 + * @param srcPrefix source IP prefix to match
417 + * @param dstPrefix destination IP prefix to match
418 + * @param srcConnectPoint source connect point for PointToPointIntent
419 + * @param dstConnectPoint destination connect point for PointToPointIntent
420 + */
421 + private void icmpPathintentConstructor(String srcPrefix, String dstPrefix,
422 + ConnectPoint srcConnectPoint, ConnectPoint dstConnectPoint) {
423 +
424 + TrafficSelector selector = DefaultTrafficSelector.builder()
425 + .matchEthType(Ethernet.TYPE_IPV4)
426 + .matchIPProtocol(IPv4.PROTOCOL_ICMP)
427 + .matchIPSrc(IpPrefix.valueOf(srcPrefix))
428 + .matchIPDst(IpPrefix.valueOf(dstPrefix))
429 + .build();
430 +
431 + PointToPointIntent intent = new PointToPointIntent(
432 + testIntentId, selector, noTreatment,
433 + srcConnectPoint, dstConnectPoint);
434 +
435 + intentList.add(intent);
436 + }
437 +
438 + /**
439 + * Sets up intents for ICMP paths.
440 + */
441 + private void setUpIcmpIntents() {
442 +
443 + // Start to build intents between BGP speaker1 and BGP peer1
444 + icmpPathintentConstructor(
445 + "192.168.10.101/32", "192.168.10.1/32", s1Eth100, s1Eth1);
446 + icmpPathintentConstructor(
447 + "192.168.10.1/32", "192.168.10.101/32", s1Eth1, s1Eth100);
448 +
449 + // Start to build intents between BGP speaker1 and BGP peer2
450 + icmpPathintentConstructor(
451 + "192.168.20.101/32", "192.168.20.1/32", s1Eth100, s2Eth1);
452 + icmpPathintentConstructor(
453 + "192.168.20.1/32", "192.168.20.101/32", s2Eth1, s1Eth100);
454 +
455 + // Start to build intents between BGP speaker1 and BGP peer3
456 + icmpPathintentConstructor(
457 + "192.168.20.101/32", "192.168.20.2/32", s1Eth100, s2Eth1);
458 + icmpPathintentConstructor(
459 + "192.168.20.2/32", "192.168.20.101/32", s2Eth1, s1Eth100);
460 +
461 + //
462 + // Start to build intents between BGP speaker2 and BGP peer1
463 + icmpPathintentConstructor(
464 + "192.168.10.102/32", "192.168.10.1/32", s1Eth100, s1Eth1);
465 + icmpPathintentConstructor(
466 + "192.168.10.1/32", "192.168.10.102/32", s1Eth1, s1Eth100);
467 +
468 + // Start to build intents between BGP speaker2 and BGP peer2
469 + icmpPathintentConstructor(
470 + "192.168.20.102/32", "192.168.20.1/32", s1Eth100, s2Eth1);
471 + icmpPathintentConstructor(
472 + "192.168.20.1/32", "192.168.20.102/32", s2Eth1, s1Eth100);
473 +
474 + // Start to build intents between BGP speaker2 and BGP peer3
475 + icmpPathintentConstructor(
476 + "192.168.20.102/32", "192.168.20.2/32", s1Eth100, s2Eth1);
477 + icmpPathintentConstructor(
478 + "192.168.20.2/32", "192.168.20.102/32", s2Eth1, s1Eth100);
479 +
480 + //
481 + // Start to build intents between BGP speaker3 and BGP peer1
482 + icmpPathintentConstructor(
483 + "192.168.10.103/32", "192.168.10.1/32", s2Eth100, s1Eth1);
484 + icmpPathintentConstructor(
485 + "192.168.10.1/32", "192.168.10.103/32", s1Eth1, s2Eth100);
486 +
487 + // Start to build intents between BGP speaker3 and BGP peer2
488 + icmpPathintentConstructor(
489 + "192.168.20.103/32", "192.168.20.1/32", s2Eth100, s2Eth1);
490 + icmpPathintentConstructor(
491 + "192.168.20.1/32", "192.168.20.103/32", s2Eth1, s2Eth100);
492 +
493 + // Start to build intents between BGP speaker3 and BGP peer3
494 + icmpPathintentConstructor(
495 + "192.168.20.103/32", "192.168.20.2/32", s2Eth100, s2Eth1);
496 + icmpPathintentConstructor(
497 + "192.168.20.2/32", "192.168.20.103/32", s2Eth1, s2Eth100);
498 +
499 + }
500 +
501 + /**
502 + * Initializes peer connectivity testing environment.
503 + */
504 + private void initPeerConnectivity() {
505 +
506 + configInfoService = createMock(SdnIpConfigService.class);
507 + expect(configInfoService.getBgpPeers()).andReturn(peers).anyTimes();
508 + expect(configInfoService.getBgpSpeakers()).andReturn(bgpSpeakers).anyTimes();
509 + replay(configInfoService);
510 +
511 + intentService = createMock(IntentService.class);
512 + replay(intentService);
513 +
514 + peerConnectivityManager = new PeerConnectivityManager(configInfoService,
515 + interfaceService, intentService);
516 + }
517 +
518 + /*
519 + * EasyMock matcher that matches {@link PointToPointIntent}s but
520 + * ignores the {@link IntentId} when matching.
521 + * <p/>
522 + * The normal intent equals method tests that the intent IDs are equal,
523 + * however in these tests we can't know what the intent IDs will be in
524 + * advance, so we can't set up expected intents with the correct IDs. Thus,
525 + * the solution is to use an EasyMock matcher that verifies that all the
526 + * value properties of the provided intent match the expected values, but
527 + * ignores the intent ID when testing equality.
528 + */
529 + private static final class IdAgnosticPointToPointIntentMatcher implements
530 + IArgumentMatcher {
531 +
532 + private final PointToPointIntent intent;
533 + private String providedIntentString;
534 +
535 + /**
536 + * Constructor taking the expected intent to match against.
537 + *
538 + * @param intent the expected intent
539 + */
540 + public IdAgnosticPointToPointIntentMatcher(PointToPointIntent intent) {
541 + this.intent = intent;
542 + }
543 +
544 + @Override
545 + public void appendTo(StringBuffer strBuffer) {
546 + strBuffer.append("PointToPointIntentMatcher unable to match: "
547 + + providedIntentString);
548 + }
549 +
550 + @Override
551 + public boolean matches(Object object) {
552 + if (!(object instanceof PointToPointIntent)) {
553 + return false;
554 + }
555 +
556 + PointToPointIntent providedIntent = (PointToPointIntent) object;
557 + providedIntentString = providedIntent.toString();
558 +
559 + PointToPointIntent matchIntent =
560 + new PointToPointIntent(providedIntent.id(),
561 + intent.selector(), intent.treatment(),
562 + intent.ingressPoint(), intent.egressPoint());
563 +
564 + return matchIntent.equals(providedIntent);
565 + }
566 + }
567 +
568 + /**
569 + * Matcher method to set an expected intent to match against (ignoring the
570 + * the intent ID).
571 + *
572 + * @param intent the expected intent
573 + * @return something of type PointToPointIntent
574 + */
575 + private static PointToPointIntent eqExceptId(
576 + PointToPointIntent intent) {
577 + reportMatcher(new IdAgnosticPointToPointIntentMatcher(intent));
578 + return null;
579 + }
580 +
581 + /**
582 + * Tests whether peer connectivity manager can set up correct BGP and
583 + * ICMP intents according to specific configuration.
584 + * <p/>
585 + * Two tricky cases included in the configuration are: 2 peers on a same
586 + * switch port, peer on the same switch with BGPd.
587 + */
588 + @Test
589 + public void testConnectionSetup() {
590 +
591 + reset(intentService);
592 +
593 + // Sets up the expected PointToPoint intents.
594 + for (int i = 0; i < intentList.size(); i++) {
595 + intentService.submit(eqExceptId(intentList.get(i)));
596 + }
597 +
598 + replay(intentService);
599 +
600 + // Running the interface to be tested.
601 + peerConnectivityManager.start();
602 +
603 + verify(intentService);
604 +
605 + }
606 +
607 + /**
608 + * Tests a corner case, when there are no interfaces in the configuration.
609 + */
610 + @Test
611 + public void testNullInterfaces() {
612 + reset(interfaceService);
613 + expect(interfaceService.getInterfaces()).andReturn(
614 + Sets.<Interface>newHashSet()).anyTimes();
615 + expect(interfaceService.getInterface(s2Eth1))
616 + .andReturn(null).anyTimes();
617 + expect(interfaceService.getInterface(s1Eth1))
618 + .andReturn(null).anyTimes();
619 + replay(interfaceService);
620 +
621 + reset(configInfoService);
622 + expect(configInfoService.getBgpPeers()).andReturn(peers).anyTimes();
623 + expect(configInfoService.getBgpSpeakers()).andReturn(bgpSpeakers).anyTimes();
624 + replay(configInfoService);
625 +
626 + reset(intentService);
627 + replay(intentService);
628 + peerConnectivityManager.start();
629 + verify(intentService);
630 + }
631 +
632 + /**
633 + * Tests a corner case, when there are no BGP peers in the configuration.
634 + */
635 + @Test
636 + public void testNullBgpPeers() {
637 + reset(interfaceService);
638 + expect(interfaceService.getInterfaces()).andReturn(
639 + Sets.newHashSet(interfaces.values())).anyTimes();
640 + replay(interfaceService);
641 +
642 + reset(configInfoService);
643 + expect(configInfoService.getBgpPeers()).andReturn(
644 + new HashMap<IpAddress, BgpPeer>()).anyTimes();
645 + expect(configInfoService.getBgpSpeakers()).andReturn(
646 + bgpSpeakers).anyTimes();
647 + replay(configInfoService);
648 +
649 + reset(intentService);
650 + replay(intentService);
651 + peerConnectivityManager.start();
652 + verify(intentService);
653 + }
654 +
655 + /**
656 + * Tests a corner case, when there is no BGP speakers in the configuration.
657 + */
658 + @Test
659 + public void testNullBgpSpeakers() {
660 + reset(interfaceService);
661 + expect(interfaceService.getInterfaces()).andReturn(
662 + Sets.newHashSet(interfaces.values())).anyTimes();
663 + replay(interfaceService);
664 +
665 + reset(configInfoService);
666 + expect(configInfoService.getBgpPeers()).andReturn(
667 + peers).anyTimes();
668 + expect(configInfoService.getBgpSpeakers()).andReturn(
669 + null).anyTimes();
670 + replay(configInfoService);
671 +
672 + reset(intentService);
673 + replay(intentService);
674 + peerConnectivityManager.start();
675 + verify(intentService);
676 + }
677 +
678 + /**
679 + * Tests a corner case, when there is no Interface configured for one BGP
680 + * peer.
681 + */
682 + @Test
683 + public void testNoPeerInterface() {
684 + String peerSw100Eth1 = "192.168.200.1";
685 + configuredPeers.put(IpAddress.valueOf(peerSw100Eth1),
686 + new BgpPeer("00:00:00:00:00:00:01:00", 1, peerSw100Eth1));
687 + testConnectionSetup();
688 + }
689 +
690 + /**
691 + * Tests a corner case, when there is no Interface configured for one BGP
692 + * speaker.
693 + * TODO: we should add a configuration correctness checking module/method
694 + * before testing this corner case.
695 + */
696 + @Ignore
697 + @Test
698 + public void testNoSpeakerInterface() {
699 + BgpSpeaker bgpSpeaker100 = new BgpSpeaker(
700 + "bgpSpeaker100",
701 + "00:00:00:00:00:00:01:00", 100,
702 + "00:00:00:00:01:00");
703 + List<InterfaceAddress> interfaceAddresses100 =
704 + new LinkedList<InterfaceAddress>();
705 + interfaceAddresses100.add(new InterfaceAddress(dpid1, 1, "192.168.10.201"));
706 + interfaceAddresses100.add(new InterfaceAddress(dpid2, 1, "192.168.20.201"));
707 + bgpSpeaker100.setInterfaceAddresses(interfaceAddresses100);
708 + configuredBgpSpeakers.put(bgpSpeaker100.name(), bgpSpeaker100);
709 + testConnectionSetup();
710 + }
711 +}
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;
2 +
3 +import static com.google.common.collect.Lists.newArrayList;
4 +
5 +import java.util.Collections;
6 +import java.util.List;
7 +
8 +import org.apache.karaf.shell.commands.Command;
9 +import org.onlab.onos.cluster.NodeId;
10 +import org.onlab.onos.mastership.MastershipService;
11 +import org.onlab.onos.net.Device;
12 +import org.onlab.onos.net.DeviceId;
13 +import org.onlab.onos.net.device.DeviceService;
14 +
15 +
16 +/**
17 + * Lists mastership roles of nodes for each device.
18 + */
19 +@Command(scope = "onos", name = "roles",
20 + description = "Lists mastership roles of nodes for each device.")
21 +public class RolesCommand extends AbstractShellCommand {
22 +
23 + private static final String FMT_HDR = "%s: master=%s\nstandbys: %s nodes";
24 + private static final String FMT_SB = "\t%s";
25 +
26 + @Override
27 + protected void execute() {
28 + DeviceService deviceService = get(DeviceService.class);
29 + MastershipService roleService = get(MastershipService.class);
30 +
31 + for (Device d : getSortedDevices(deviceService)) {
32 + DeviceId did = d.id();
33 + printRoles(roleService, did);
34 + }
35 + }
36 +
37 + /**
38 + * Returns the list of devices sorted using the device ID URIs.
39 + *
40 + * @param service device service
41 + * @return sorted device list
42 + */
43 + protected static List<Device> getSortedDevices(DeviceService service) {
44 + List<Device> devices = newArrayList(service.getDevices());
45 + Collections.sort(devices, Comparators.ELEMENT_COMPARATOR);
46 + return devices;
47 + }
48 +
49 + /**
50 + * Prints the role information for a device.
51 + *
52 + * @param deviceId the ID of the device
53 + * @param master the current master
54 + */
55 + protected void printRoles(MastershipService service, DeviceId deviceId) {
56 + List<NodeId> nodes = service.getNodesFor(deviceId);
57 + NodeId first = null;
58 + NodeId master = null;
59 +
60 + if (!nodes.isEmpty()) {
61 + first = nodes.get(0);
62 + }
63 + if (first != null &&
64 + first.equals(service.getMasterFor(deviceId))) {
65 + master = nodes.get(0);
66 + nodes.remove(master);
67 + }
68 + print(FMT_HDR, deviceId, master == null ? "NONE" : master, nodes.size());
69 +
70 + for (NodeId nid : nodes) {
71 + print(FMT_SB, nid);
72 + }
73 + }
74 +}
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 +}
......
...@@ -90,11 +90,15 @@ public class IntentPushTestCommand extends AbstractShellCommand ...@@ -90,11 +90,15 @@ public class IntentPushTestCommand extends AbstractShellCommand
90 service.submit(intent); 90 service.submit(intent);
91 } 91 }
92 try { 92 try {
93 - latch.await(5, TimeUnit.SECONDS); 93 + if (latch.await(10, TimeUnit.SECONDS)) {
94 - printResults(count); 94 + printResults(count);
95 + } else {
96 + print("I FAIL MISERABLY -> %d", latch.getCount());
97 + }
95 } catch (InterruptedException e) { 98 } catch (InterruptedException e) {
96 print(e.toString()); 99 print(e.toString());
97 } 100 }
101 +
98 service.removeListener(this); 102 service.removeListener(this);
99 } 103 }
100 104
...@@ -140,6 +144,8 @@ public class IntentPushTestCommand extends AbstractShellCommand ...@@ -140,6 +144,8 @@ public class IntentPushTestCommand extends AbstractShellCommand
140 } else { 144 } else {
141 log.warn("install event latch is null"); 145 log.warn("install event latch is null");
142 } 146 }
147 + } else {
148 + log.info("I FAIL -> {}", event);
143 } 149 }
144 } 150 }
145 } 151 }
......
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 }
......
...@@ -13,6 +13,10 @@ ...@@ -13,6 +13,10 @@
13 <command> 13 <command>
14 <action class="org.onlab.onos.cli.NodeRemoveCommand"/> 14 <action class="org.onlab.onos.cli.NodeRemoveCommand"/>
15 </command> 15 </command>
16 +
17 + <command>
18 + <action class="org.onlab.onos.cli.RolesCommand"/>
19 + </command>
16 <command> 20 <command>
17 <action class="org.onlab.onos.cli.MastersListCommand"/> 21 <action class="org.onlab.onos.cli.MastersListCommand"/>
18 <completers> 22 <completers>
......
...@@ -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.mastership; 1 package org.onlab.onos.mastership;
2 2
3 +import java.util.List;
3 import java.util.Set; 4 import java.util.Set;
4 5
5 import org.onlab.onos.cluster.NodeId; 6 import org.onlab.onos.cluster.NodeId;
...@@ -50,6 +51,15 @@ public interface MastershipService { ...@@ -50,6 +51,15 @@ public interface MastershipService {
50 NodeId getMasterFor(DeviceId deviceId); 51 NodeId getMasterFor(DeviceId deviceId);
51 52
52 /** 53 /**
54 + * Returns controllers connected to a given device, in order of
55 + * preference. The first entry in the list is the current master.
56 + *
57 + * @param deviceId the identifier of the device
58 + * @return a list of controller IDs
59 + */
60 + List<NodeId> getNodesFor(DeviceId deviceId);
61 +
62 + /**
53 * Returns the devices for which a controller is master. 63 * Returns the devices for which a controller is master.
54 * 64 *
55 * @param nodeId the ID of the controller 65 * @param nodeId the ID of the controller
......
1 package org.onlab.onos.mastership; 1 package org.onlab.onos.mastership;
2 2
3 +import java.util.List;
3 import java.util.Set; 4 import java.util.Set;
4 5
5 import org.onlab.onos.cluster.NodeId; 6 import org.onlab.onos.cluster.NodeId;
...@@ -41,6 +42,15 @@ public interface MastershipStore extends Store<MastershipEvent, MastershipStoreD ...@@ -41,6 +42,15 @@ public interface MastershipStore extends Store<MastershipEvent, MastershipStoreD
41 NodeId getMaster(DeviceId deviceId); 42 NodeId getMaster(DeviceId deviceId);
42 43
43 /** 44 /**
45 + * Returns the controllers connected to a device, in mastership-
46 + * preference order.
47 + *
48 + * @param deviceId the device identifier
49 + * @return an ordered list of controller IDs
50 + */
51 + List<NodeId> getNodes(DeviceId deviceId);
52 +
53 + /**
44 * Returns the devices that a controller instance is master of. 54 * Returns the devices that a controller instance is master of.
45 * 55 *
46 * @param nodeId the instance identifier 56 * @param nodeId the instance identifier
...@@ -48,6 +58,7 @@ public interface MastershipStore extends Store<MastershipEvent, MastershipStoreD ...@@ -48,6 +58,7 @@ public interface MastershipStore extends Store<MastershipEvent, MastershipStoreD
48 */ 58 */
49 Set<DeviceId> getDevices(NodeId nodeId); 59 Set<DeviceId> getDevices(NodeId nodeId);
50 60
61 +
51 /** 62 /**
52 * Sets a device's role for a specified controller instance. 63 * Sets a device's role for a specified controller instance.
53 * 64 *
......
1 package org.onlab.onos.net; 1 package org.onlab.onos.net;
2 2
3 import org.onlab.onos.net.provider.ProviderId; 3 import org.onlab.onos.net.provider.ProviderId;
4 +import org.onlab.packet.ChassisId;
4 5
5 import java.util.Objects; 6 import java.util.Objects;
6 7
...@@ -16,6 +17,7 @@ public class DefaultDevice extends AbstractElement implements Device { ...@@ -16,6 +17,7 @@ public class DefaultDevice extends AbstractElement implements Device {
16 private final String serialNumber; 17 private final String serialNumber;
17 private final String hwVersion; 18 private final String hwVersion;
18 private final String swVersion; 19 private final String swVersion;
20 + private final ChassisId chassisId;
19 21
20 // For serialization 22 // For serialization
21 private DefaultDevice() { 23 private DefaultDevice() {
...@@ -24,6 +26,7 @@ public class DefaultDevice extends AbstractElement implements Device { ...@@ -24,6 +26,7 @@ public class DefaultDevice extends AbstractElement implements Device {
24 this.hwVersion = null; 26 this.hwVersion = null;
25 this.swVersion = null; 27 this.swVersion = null;
26 this.serialNumber = null; 28 this.serialNumber = null;
29 + this.chassisId = null;
27 } 30 }
28 31
29 /** 32 /**
...@@ -40,13 +43,15 @@ public class DefaultDevice extends AbstractElement implements Device { ...@@ -40,13 +43,15 @@ public class DefaultDevice extends AbstractElement implements Device {
40 */ 43 */
41 public DefaultDevice(ProviderId providerId, DeviceId id, Type type, 44 public DefaultDevice(ProviderId providerId, DeviceId id, Type type,
42 String manufacturer, String hwVersion, String swVersion, 45 String manufacturer, String hwVersion, String swVersion,
43 - String serialNumber, Annotations... annotations) { 46 + String serialNumber, ChassisId chassisId,
47 + Annotations... annotations) {
44 super(providerId, id, annotations); 48 super(providerId, id, annotations);
45 this.type = type; 49 this.type = type;
46 this.manufacturer = manufacturer; 50 this.manufacturer = manufacturer;
47 this.hwVersion = hwVersion; 51 this.hwVersion = hwVersion;
48 this.swVersion = swVersion; 52 this.swVersion = swVersion;
49 this.serialNumber = serialNumber; 53 this.serialNumber = serialNumber;
54 + this.chassisId = chassisId;
50 } 55 }
51 56
52 @Override 57 @Override
...@@ -80,6 +85,11 @@ public class DefaultDevice extends AbstractElement implements Device { ...@@ -80,6 +85,11 @@ public class DefaultDevice extends AbstractElement implements Device {
80 } 85 }
81 86
82 @Override 87 @Override
88 + public ChassisId chassisId() {
89 + return chassisId;
90 + }
91 +
92 + @Override
83 public int hashCode() { 93 public int hashCode() {
84 return Objects.hash(id, type, manufacturer, hwVersion, swVersion, serialNumber); 94 return Objects.hash(id, type, manufacturer, hwVersion, swVersion, serialNumber);
85 } 95 }
......
1 package org.onlab.onos.net; 1 package org.onlab.onos.net;
2 2
3 +import org.onlab.packet.ChassisId;
4 +
3 /** 5 /**
4 * Representation of a network infrastructure device. 6 * Representation of a network infrastructure device.
5 */ 7 */
...@@ -54,6 +56,13 @@ public interface Device extends Element { ...@@ -54,6 +56,13 @@ public interface Device extends Element {
54 */ 56 */
55 String serialNumber(); 57 String serialNumber();
56 58
59 + /**
60 + * Returns the device chassis id.
61 + *
62 + * @return chassis id
63 + */
64 + ChassisId chassisId();
65 +
57 // Device realizedBy(); ? 66 // Device realizedBy(); ?
58 67
59 // ports are not provided directly, but rather via DeviceService.getPorts(Device device); 68 // ports are not provided directly, but rather via DeviceService.getPorts(Device device);
......
...@@ -2,6 +2,7 @@ package org.onlab.onos.net.device; ...@@ -2,6 +2,7 @@ package org.onlab.onos.net.device;
2 2
3 import org.onlab.onos.net.AbstractDescription; 3 import org.onlab.onos.net.AbstractDescription;
4 import org.onlab.onos.net.SparseAnnotations; 4 import org.onlab.onos.net.SparseAnnotations;
5 +import org.onlab.packet.ChassisId;
5 6
6 import java.net.URI; 7 import java.net.URI;
7 8
...@@ -20,6 +21,7 @@ public class DefaultDeviceDescription extends AbstractDescription ...@@ -20,6 +21,7 @@ public class DefaultDeviceDescription extends AbstractDescription
20 private final String hwVersion; 21 private final String hwVersion;
21 private final String swVersion; 22 private final String swVersion;
22 private final String serialNumber; 23 private final String serialNumber;
24 + private final ChassisId chassisId;
23 25
24 /** 26 /**
25 * Creates a device description using the supplied information. 27 * Creates a device description using the supplied information.
...@@ -34,7 +36,7 @@ public class DefaultDeviceDescription extends AbstractDescription ...@@ -34,7 +36,7 @@ public class DefaultDeviceDescription extends AbstractDescription
34 */ 36 */
35 public DefaultDeviceDescription(URI uri, Type type, String manufacturer, 37 public DefaultDeviceDescription(URI uri, Type type, String manufacturer,
36 String hwVersion, String swVersion, 38 String hwVersion, String swVersion,
37 - String serialNumber, 39 + String serialNumber, ChassisId chassis,
38 SparseAnnotations... annotations) { 40 SparseAnnotations... annotations) {
39 super(annotations); 41 super(annotations);
40 this.uri = checkNotNull(uri, "Device URI cannot be null"); 42 this.uri = checkNotNull(uri, "Device URI cannot be null");
...@@ -43,6 +45,7 @@ public class DefaultDeviceDescription extends AbstractDescription ...@@ -43,6 +45,7 @@ public class DefaultDeviceDescription extends AbstractDescription
43 this.hwVersion = hwVersion; 45 this.hwVersion = hwVersion;
44 this.swVersion = swVersion; 46 this.swVersion = swVersion;
45 this.serialNumber = serialNumber; 47 this.serialNumber = serialNumber;
48 + this.chassisId = chassis;
46 } 49 }
47 50
48 /** 51 /**
...@@ -54,7 +57,7 @@ public class DefaultDeviceDescription extends AbstractDescription ...@@ -54,7 +57,7 @@ public class DefaultDeviceDescription extends AbstractDescription
54 SparseAnnotations... annotations) { 57 SparseAnnotations... annotations) {
55 this(base.deviceURI(), base.type(), base.manufacturer(), 58 this(base.deviceURI(), base.type(), base.manufacturer(),
56 base.hwVersion(), base.swVersion(), base.serialNumber(), 59 base.hwVersion(), base.swVersion(), base.serialNumber(),
57 - annotations); 60 + base.chassisId(), annotations);
58 } 61 }
59 62
60 @Override 63 @Override
...@@ -88,6 +91,11 @@ public class DefaultDeviceDescription extends AbstractDescription ...@@ -88,6 +91,11 @@ public class DefaultDeviceDescription extends AbstractDescription
88 } 91 }
89 92
90 @Override 93 @Override
94 + public ChassisId chassisId() {
95 + return chassisId;
96 + }
97 +
98 + @Override
91 public String toString() { 99 public String toString() {
92 return toStringHelper(this) 100 return toStringHelper(this)
93 .add("uri", uri).add("type", type).add("mfr", manufacturer) 101 .add("uri", uri).add("type", type).add("mfr", manufacturer)
...@@ -104,5 +112,6 @@ public class DefaultDeviceDescription extends AbstractDescription ...@@ -104,5 +112,6 @@ public class DefaultDeviceDescription extends AbstractDescription
104 this.hwVersion = null; 112 this.hwVersion = null;
105 this.swVersion = null; 113 this.swVersion = null;
106 this.serialNumber = null; 114 this.serialNumber = null;
115 + this.chassisId = null;
107 } 116 }
108 } 117 }
......
...@@ -2,6 +2,7 @@ package org.onlab.onos.net.device; ...@@ -2,6 +2,7 @@ package org.onlab.onos.net.device;
2 2
3 import org.onlab.onos.net.Description; 3 import org.onlab.onos.net.Description;
4 import org.onlab.onos.net.Device; 4 import org.onlab.onos.net.Device;
5 +import org.onlab.packet.ChassisId;
5 6
6 import java.net.URI; 7 import java.net.URI;
7 8
...@@ -54,4 +55,11 @@ public interface DeviceDescription extends Description { ...@@ -54,4 +55,11 @@ public interface DeviceDescription extends Description {
54 */ 55 */
55 String serialNumber(); 56 String serialNumber();
56 57
58 + /**
59 + * Returns a device chassis id.
60 + *
61 + * @return chassis id
62 + */
63 + ChassisId chassisId();
64 +
57 } 65 }
......
...@@ -6,9 +6,10 @@ import static org.slf4j.LoggerFactory.getLogger; ...@@ -6,9 +6,10 @@ import static org.slf4j.LoggerFactory.getLogger;
6 import org.onlab.onos.net.DeviceId; 6 import org.onlab.onos.net.DeviceId;
7 import org.slf4j.Logger; 7 import org.slf4j.Logger;
8 8
9 -public class DefaultFlowEntry extends DefaultFlowRule implements FlowEntry { 9 +public class DefaultFlowEntry extends DefaultFlowRule
10 + implements FlowEntry, StoredFlowEntry {
10 11
11 - private final Logger log = getLogger(getClass()); 12 + private static final Logger log = getLogger(DefaultFlowEntry.class);
12 13
13 private long life; 14 private long life;
14 private long packets; 15 private long packets;
......
...@@ -11,7 +11,7 @@ import org.slf4j.Logger; ...@@ -11,7 +11,7 @@ import org.slf4j.Logger;
11 11
12 public class DefaultFlowRule implements FlowRule { 12 public class DefaultFlowRule implements FlowRule {
13 13
14 - private final Logger log = getLogger(getClass()); 14 + private static final Logger log = getLogger(DefaultFlowRule.class);
15 15
16 private final DeviceId deviceId; 16 private final DeviceId deviceId;
17 private final int priority; 17 private final int priority;
......
...@@ -65,6 +65,7 @@ public interface FlowEntry extends FlowRule { ...@@ -65,6 +65,7 @@ public interface FlowEntry extends FlowRule {
65 */ 65 */
66 long bytes(); 66 long bytes();
67 67
68 + // TODO: consider removing this attribute
68 /** 69 /**
69 * When this flow entry was last deemed active. 70 * When this flow entry was last deemed active.
70 * @return epoch time of last activity 71 * @return epoch time of last activity
...@@ -72,35 +73,6 @@ public interface FlowEntry extends FlowRule { ...@@ -72,35 +73,6 @@ public interface FlowEntry extends FlowRule {
72 long lastSeen(); 73 long lastSeen();
73 74
74 /** 75 /**
75 - * Sets the last active epoch time.
76 - */
77 - void setLastSeen();
78 -
79 - /**
80 - * Sets the new state for this entry.
81 - * @param newState new flow entry state.
82 - */
83 - void setState(FlowEntryState newState);
84 -
85 - /**
86 - * Sets how long this entry has been entered in the system.
87 - * @param life epoch time
88 - */
89 - void setLife(long life);
90 -
91 - /**
92 - * Number of packets seen by this entry.
93 - * @param packets a long value
94 - */
95 - void setPackets(long packets);
96 -
97 - /**
98 - * Number of bytes seen by this rule.
99 - * @param bytes a long value
100 - */
101 - void setBytes(long bytes);
102 -
103 - /**
104 * Indicates the error type. 76 * Indicates the error type.
105 * @return an integer value of the error 77 * @return an integer value of the error
106 */ 78 */
......
1 +package org.onlab.onos.net.flow;
2 +
3 +
4 +public interface StoredFlowEntry extends FlowEntry {
5 +
6 + /**
7 + * Sets the last active epoch time.
8 + */
9 + void setLastSeen();
10 +
11 + /**
12 + * Sets the new state for this entry.
13 + * @param newState new flow entry state.
14 + */
15 + void setState(FlowEntryState newState);
16 +
17 + /**
18 + * Sets how long this entry has been entered in the system.
19 + * @param life epoch time
20 + */
21 + void setLife(long life);
22 +
23 + /**
24 + * Number of packets seen by this entry.
25 + * @param packets a long value
26 + */
27 + void setPackets(long packets);
28 +
29 + /**
30 + * Number of bytes seen by this rule.
31 + * @param bytes a long value
32 + */
33 + void setBytes(long bytes);
34 +
35 +}
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;
......
...@@ -4,6 +4,7 @@ import java.util.Collection; ...@@ -4,6 +4,7 @@ import java.util.Collection;
4 import java.util.Objects; 4 import java.util.Objects;
5 import java.util.Set; 5 import java.util.Set;
6 6
7 +import org.onlab.onos.net.ConnectPoint;
7 import org.onlab.onos.net.Link; 8 import org.onlab.onos.net.Link;
8 import org.onlab.onos.net.flow.TrafficSelector; 9 import org.onlab.onos.net.flow.TrafficSelector;
9 import org.onlab.onos.net.flow.TrafficTreatment; 10 import org.onlab.onos.net.flow.TrafficTreatment;
...@@ -14,10 +15,12 @@ import com.google.common.base.MoreObjects; ...@@ -14,10 +15,12 @@ import com.google.common.base.MoreObjects;
14 * Abstraction of a connectivity intent that is implemented by a set of path 15 * Abstraction of a connectivity intent that is implemented by a set of path
15 * segments. 16 * segments.
16 */ 17 */
17 -public class LinkCollectionIntent extends ConnectivityIntent implements InstallableIntent { 18 +public final class LinkCollectionIntent extends ConnectivityIntent implements InstallableIntent {
18 19
19 private final Set<Link> links; 20 private final Set<Link> links;
20 21
22 + private final ConnectPoint egressPoint;
23 +
21 /** 24 /**
22 * Creates a new point-to-point intent with the supplied ingress/egress 25 * Creates a new point-to-point intent with the supplied ingress/egress
23 * ports and using the specified explicit path. 26 * ports and using the specified explicit path.
...@@ -26,19 +29,23 @@ public class LinkCollectionIntent extends ConnectivityIntent implements Installa ...@@ -26,19 +29,23 @@ public class LinkCollectionIntent extends ConnectivityIntent implements Installa
26 * @param selector traffic match 29 * @param selector traffic match
27 * @param treatment action 30 * @param treatment action
28 * @param links traversed links 31 * @param links traversed links
32 + * @param egressPoint egress point
29 * @throws NullPointerException {@code path} is null 33 * @throws NullPointerException {@code path} is null
30 */ 34 */
31 public LinkCollectionIntent(IntentId id, 35 public LinkCollectionIntent(IntentId id,
32 TrafficSelector selector, 36 TrafficSelector selector,
33 TrafficTreatment treatment, 37 TrafficTreatment treatment,
34 - Set<Link> links) { 38 + Set<Link> links,
39 + ConnectPoint egressPoint) {
35 super(id, selector, treatment); 40 super(id, selector, treatment);
36 this.links = links; 41 this.links = links;
42 + this.egressPoint = egressPoint;
37 } 43 }
38 44
39 protected LinkCollectionIntent() { 45 protected LinkCollectionIntent() {
40 super(); 46 super();
41 this.links = null; 47 this.links = null;
48 + this.egressPoint = null;
42 } 49 }
43 50
44 @Override 51 @Override
...@@ -46,10 +53,25 @@ public class LinkCollectionIntent extends ConnectivityIntent implements Installa ...@@ -46,10 +53,25 @@ public class LinkCollectionIntent extends ConnectivityIntent implements Installa
46 return links; 53 return links;
47 } 54 }
48 55
56 + /**
57 + * Returns the set of links that represent the network connections needed
58 + * by this intent.
59 + *
60 + * @return Set of links for the network hops needed by this intent
61 + */
49 public Set<Link> links() { 62 public Set<Link> links() {
50 return links; 63 return links;
51 } 64 }
52 65
66 + /**
67 + * Returns the egress point of the intent.
68 + *
69 + * @return the egress point
70 + */
71 + public ConnectPoint egressPoint() {
72 + return egressPoint;
73 + }
74 +
53 @Override 75 @Override
54 public boolean equals(Object o) { 76 public boolean equals(Object o) {
55 if (this == o) { 77 if (this == o) {
...@@ -64,12 +86,13 @@ public class LinkCollectionIntent extends ConnectivityIntent implements Installa ...@@ -64,12 +86,13 @@ public class LinkCollectionIntent extends ConnectivityIntent implements Installa
64 86
65 LinkCollectionIntent that = (LinkCollectionIntent) o; 87 LinkCollectionIntent that = (LinkCollectionIntent) o;
66 88
67 - return Objects.equals(this.links, that.links); 89 + return Objects.equals(this.links, that.links) &&
90 + Objects.equals(this.egressPoint, that.egressPoint);
68 } 91 }
69 92
70 @Override 93 @Override
71 public int hashCode() { 94 public int hashCode() {
72 - return Objects.hash(super.hashCode(), links); 95 + return Objects.hash(super.hashCode(), links, egressPoint);
73 } 96 }
74 97
75 @Override 98 @Override
...@@ -79,6 +102,7 @@ public class LinkCollectionIntent extends ConnectivityIntent implements Installa ...@@ -79,6 +102,7 @@ public class LinkCollectionIntent extends ConnectivityIntent implements Installa
79 .add("match", selector()) 102 .add("match", selector())
80 .add("action", treatment()) 103 .add("action", treatment())
81 .add("links", links()) 104 .add("links", links())
105 + .add("egress", egressPoint())
82 .toString(); 106 .toString();
83 } 107 }
84 } 108 }
......
...@@ -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;
......
...@@ -24,7 +24,7 @@ public class DefaultInboundPacket implements InboundPacket { ...@@ -24,7 +24,7 @@ public class DefaultInboundPacket implements InboundPacket {
24 * @param parsed parsed ethernet frame 24 * @param parsed parsed ethernet frame
25 * @param unparsed unparsed raw bytes 25 * @param unparsed unparsed raw bytes
26 */ 26 */
27 - public DefaultInboundPacket(ConnectPoint receivedFrom, Ethernet parsed, 27 + public DefaultInboundPacket(ConnectPoint receivedFrom, Ethernet parsed,
28 ByteBuffer unparsed) { 28 ByteBuffer unparsed) {
29 this.receivedFrom = receivedFrom; 29 this.receivedFrom = receivedFrom;
30 this.parsed = parsed; 30 this.parsed = parsed;
......
...@@ -62,6 +62,9 @@ public abstract class AbstractProviderRegistry<P extends Provider, S extends Pro ...@@ -62,6 +62,9 @@ public abstract class AbstractProviderRegistry<P extends Provider, S extends Pro
62 ((AbstractProviderService) service).invalidate(); 62 ((AbstractProviderService) service).invalidate();
63 services.remove(provider.id()); 63 services.remove(provider.id());
64 providers.remove(provider.id()); 64 providers.remove(provider.id());
65 + if (!provider.id().isAncillary()) {
66 + providersByScheme.remove(provider.id().scheme());
67 + }
65 } 68 }
66 } 69 }
67 70
......
...@@ -7,6 +7,8 @@ import org.onlab.onos.net.Provided; ...@@ -7,6 +7,8 @@ import org.onlab.onos.net.Provided;
7 */ 7 */
8 public interface Topology extends Provided { 8 public interface Topology extends Provided {
9 9
10 + // FIXME: Following is not true right now. It is actually System.nanoTime(),
11 + // which has no relation to epoch time, wall clock, etc.
10 /** 12 /**
11 * Returns the time, specified in milliseconds since start of epoch, 13 * Returns the time, specified in milliseconds since start of epoch,
12 * when the topology became active and made available. 14 * when the topology became active and made available.
......
...@@ -37,6 +37,15 @@ public interface ClusterCommunicationService { ...@@ -37,6 +37,15 @@ public interface ClusterCommunicationService {
37 boolean multicast(ClusterMessage message, Set<NodeId> nodeIds) throws IOException; 37 boolean multicast(ClusterMessage message, Set<NodeId> nodeIds) throws IOException;
38 38
39 /** 39 /**
40 + * Sends a message synchronously.
41 + * @param message message to send
42 + * @param toNodeId recipient node identifier
43 + * @return ClusterMessageResponse which is reply future.
44 + * @throws IOException
45 + */
46 + ClusterMessageResponse sendAndReceive(ClusterMessage message, NodeId toNodeId) throws IOException;
47 +
48 + /**
40 * Adds a new subscriber for the specified message subject. 49 * Adds a new subscriber for the specified message subject.
41 * 50 *
42 * @param subject message subject 51 * @param subject message subject
......
1 package org.onlab.onos.store.cluster.messaging; 1 package org.onlab.onos.store.cluster.messaging;
2 2
3 +import java.io.IOException;
4 +
3 import org.onlab.onos.cluster.NodeId; 5 import org.onlab.onos.cluster.NodeId;
4 6
5 // TODO: Should payload type be ByteBuffer? 7 // TODO: Should payload type be ByteBuffer?
...@@ -49,4 +51,14 @@ public class ClusterMessage { ...@@ -49,4 +51,14 @@ public class ClusterMessage {
49 public byte[] payload() { 51 public byte[] payload() {
50 return payload; 52 return payload;
51 } 53 }
54 +
55 + /**
56 + * Sends a response to the sender.
57 + *
58 + * @param data payload response.
59 + * @throws IOException
60 + */
61 + public void respond(byte[] data) throws IOException {
62 + throw new IllegalStateException("One can only repond to message recived from others.");
63 + }
52 } 64 }
......
1 +package org.onlab.onos.store.cluster.messaging;
2 +
3 +import java.util.concurrent.TimeUnit;
4 +import java.util.concurrent.TimeoutException;
5 +
6 +import org.onlab.onos.cluster.NodeId;
7 +
8 +public interface ClusterMessageResponse {
9 + public NodeId sender();
10 + public byte[] get(long timeout, TimeUnit timeunit) throws TimeoutException;
11 + public byte[] get(long timeout) throws InterruptedException;
12 +}
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
......
...@@ -4,6 +4,7 @@ import org.onlab.onos.cluster.NodeId; ...@@ -4,6 +4,7 @@ import org.onlab.onos.cluster.NodeId;
4 import org.onlab.onos.net.DeviceId; 4 import org.onlab.onos.net.DeviceId;
5 import org.onlab.onos.net.MastershipRole; 5 import org.onlab.onos.net.MastershipRole;
6 6
7 +import java.util.List;
7 import java.util.Set; 8 import java.util.Set;
8 9
9 /** 10 /**
...@@ -46,4 +47,9 @@ public class MastershipServiceAdapter implements MastershipService { ...@@ -46,4 +47,9 @@ public class MastershipServiceAdapter implements MastershipService {
46 public MastershipTermService requestTermService() { 47 public MastershipTermService requestTermService() {
47 return null; 48 return null;
48 } 49 }
50 +
51 + @Override
52 + public List<NodeId> getNodesFor(DeviceId deviceId) {
53 + return null;
54 + }
49 } 55 }
......
...@@ -3,6 +3,7 @@ package org.onlab.onos.net; ...@@ -3,6 +3,7 @@ package org.onlab.onos.net;
3 import com.google.common.testing.EqualsTester; 3 import com.google.common.testing.EqualsTester;
4 import org.junit.Test; 4 import org.junit.Test;
5 import org.onlab.onos.net.provider.ProviderId; 5 import org.onlab.onos.net.provider.ProviderId;
6 +import org.onlab.packet.ChassisId;
6 7
7 import static org.junit.Assert.assertEquals; 8 import static org.junit.Assert.assertEquals;
8 import static org.onlab.onos.net.Device.Type.SWITCH; 9 import static org.onlab.onos.net.Device.Type.SWITCH;
...@@ -21,14 +22,15 @@ public class DefaultDeviceTest { ...@@ -21,14 +22,15 @@ public class DefaultDeviceTest {
21 static final String SW = "3.9.1"; 22 static final String SW = "3.9.1";
22 static final String SN1 = "43311-12345"; 23 static final String SN1 = "43311-12345";
23 static final String SN2 = "42346-43512"; 24 static final String SN2 = "42346-43512";
25 + static final ChassisId CID = new ChassisId();
24 26
25 @Test 27 @Test
26 public void testEquality() { 28 public void testEquality() {
27 - Device d1 = new DefaultDevice(PID, DID1, SWITCH, MFR, HW, SW, SN1); 29 + Device d1 = new DefaultDevice(PID, DID1, SWITCH, MFR, HW, SW, SN1, CID);
28 - Device d2 = new DefaultDevice(PID, DID1, SWITCH, MFR, HW, SW, SN1); 30 + Device d2 = new DefaultDevice(PID, DID1, SWITCH, MFR, HW, SW, SN1, CID);
29 - Device d3 = new DefaultDevice(PID, DID2, SWITCH, MFR, HW, SW, SN2); 31 + Device d3 = new DefaultDevice(PID, DID2, SWITCH, MFR, HW, SW, SN2, CID);
30 - Device d4 = new DefaultDevice(PID, DID2, SWITCH, MFR, HW, SW, SN2); 32 + Device d4 = new DefaultDevice(PID, DID2, SWITCH, MFR, HW, SW, SN2, CID);
31 - Device d5 = new DefaultDevice(PID, DID2, SWITCH, MFR, HW, SW, SN1); 33 + Device d5 = new DefaultDevice(PID, DID2, SWITCH, MFR, HW, SW, SN1, CID);
32 34
33 new EqualsTester().addEqualityGroup(d1, d2) 35 new EqualsTester().addEqualityGroup(d1, d2)
34 .addEqualityGroup(d3, d4) 36 .addEqualityGroup(d3, d4)
...@@ -38,13 +40,13 @@ public class DefaultDeviceTest { ...@@ -38,13 +40,13 @@ public class DefaultDeviceTest {
38 40
39 @Test 41 @Test
40 public void basics() { 42 public void basics() {
41 - Device device = new DefaultDevice(PID, DID1, SWITCH, MFR, HW, SW, SN1); 43 + Device device = new DefaultDevice(PID, DID1, SWITCH, MFR, HW, SW, SN1, CID);
42 validate(device); 44 validate(device);
43 } 45 }
44 46
45 @Test 47 @Test
46 public void annotations() { 48 public void annotations() {
47 - Device device = new DefaultDevice(PID, DID1, SWITCH, MFR, HW, SW, SN1, 49 + Device device = new DefaultDevice(PID, DID1, SWITCH, MFR, HW, SW, SN1, CID,
48 DefaultAnnotations.builder().set("foo", "bar").build()); 50 DefaultAnnotations.builder().set("foo", "bar").build());
49 validate(device); 51 validate(device);
50 assertEquals("incorrect provider", "bar", device.annotations().value("foo")); 52 assertEquals("incorrect provider", "bar", device.annotations().value("foo"));
......
...@@ -3,6 +3,7 @@ package org.onlab.onos.net; ...@@ -3,6 +3,7 @@ package org.onlab.onos.net;
3 import com.google.common.testing.EqualsTester; 3 import com.google.common.testing.EqualsTester;
4 import org.junit.Test; 4 import org.junit.Test;
5 import org.onlab.onos.net.provider.ProviderId; 5 import org.onlab.onos.net.provider.ProviderId;
6 +import org.onlab.packet.ChassisId;
6 7
7 import static org.junit.Assert.assertEquals; 8 import static org.junit.Assert.assertEquals;
8 import static org.onlab.onos.net.Device.Type.SWITCH; 9 import static org.onlab.onos.net.Device.Type.SWITCH;
...@@ -22,7 +23,8 @@ public class DefaultPortTest { ...@@ -22,7 +23,8 @@ public class DefaultPortTest {
22 23
23 @Test 24 @Test
24 public void testEquality() { 25 public void testEquality() {
25 - Device device = new DefaultDevice(PID, DID1, SWITCH, "m", "h", "s", "n"); 26 + Device device = new DefaultDevice(PID, DID1, SWITCH, "m", "h", "s", "n",
27 + new ChassisId());
26 Port p1 = new DefaultPort(device, portNumber(1), true); 28 Port p1 = new DefaultPort(device, portNumber(1), true);
27 Port p2 = new DefaultPort(device, portNumber(1), true); 29 Port p2 = new DefaultPort(device, portNumber(1), true);
28 Port p3 = new DefaultPort(device, portNumber(2), true); 30 Port p3 = new DefaultPort(device, portNumber(2), true);
...@@ -37,7 +39,8 @@ public class DefaultPortTest { ...@@ -37,7 +39,8 @@ public class DefaultPortTest {
37 39
38 @Test 40 @Test
39 public void basics() { 41 public void basics() {
40 - Device device = new DefaultDevice(PID, DID1, SWITCH, "m", "h", "s", "n"); 42 + Device device = new DefaultDevice(PID, DID1, SWITCH, "m", "h", "s", "n",
43 + new ChassisId());
41 Port port = new DefaultPort(device, portNumber(1), true); 44 Port port = new DefaultPort(device, portNumber(1), true);
42 assertEquals("incorrect element", device, port.element()); 45 assertEquals("incorrect element", device, port.element());
43 assertEquals("incorrect number", portNumber(1), port.number()); 46 assertEquals("incorrect number", portNumber(1), port.number());
......
1 package org.onlab.onos.net; 1 package org.onlab.onos.net;
2 2
3 import org.onlab.onos.net.provider.ProviderId; 3 import org.onlab.onos.net.provider.ProviderId;
4 +import org.onlab.packet.ChassisId;
4 import org.onlab.packet.IpPrefix; 5 import org.onlab.packet.IpPrefix;
5 6
6 import java.util.ArrayList; 7 import java.util.ArrayList;
...@@ -37,7 +38,7 @@ public final class NetTestTools { ...@@ -37,7 +38,7 @@ public final class NetTestTools {
37 // Crates a new device with the specified id 38 // Crates a new device with the specified id
38 public static Device device(String id) { 39 public static Device device(String id) {
39 return new DefaultDevice(PID, did(id), Device.Type.SWITCH, 40 return new DefaultDevice(PID, did(id), Device.Type.SWITCH,
40 - "mfg", "1.0", "1.1", "1234"); 41 + "mfg", "1.0", "1.1", "1234", new ChassisId());
41 } 42 }
42 43
43 // Crates a new host with the specified id 44 // Crates a new host with the specified id
...@@ -47,10 +48,16 @@ public final class NetTestTools { ...@@ -47,10 +48,16 @@ public final class NetTestTools {
47 new HashSet<IpPrefix>()); 48 new HashSet<IpPrefix>());
48 } 49 }
49 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 +
50 // Short-hand for creating a link. 56 // Short-hand for creating a link.
51 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) {
52 - return new DefaultLink(PID, new ConnectPoint(did(src), portNumber(sp)), 58 + return new DefaultLink(PID,
53 - new ConnectPoint(did(dst), portNumber(dp)), 59 + connectPoint(src, sp),
60 + connectPoint(dst, dp),
54 Link.Type.DIRECT); 61 Link.Type.DIRECT);
55 } 62 }
56 63
......
1 package org.onlab.onos.net.device; 1 package org.onlab.onos.net.device;
2 2
3 import org.junit.Test; 3 import org.junit.Test;
4 +import org.onlab.packet.ChassisId;
4 5
5 import java.net.URI; 6 import java.net.URI;
6 7
...@@ -18,12 +19,13 @@ public class DefaultDeviceDescriptionTest { ...@@ -18,12 +19,13 @@ public class DefaultDeviceDescriptionTest {
18 private static final String HW = "1.1.x"; 19 private static final String HW = "1.1.x";
19 private static final String SW = "3.9.1"; 20 private static final String SW = "3.9.1";
20 private static final String SN = "43311-12345"; 21 private static final String SN = "43311-12345";
22 + private static final ChassisId CID = new ChassisId();
21 23
22 24
23 @Test 25 @Test
24 public void basics() { 26 public void basics() {
25 DeviceDescription device = 27 DeviceDescription device =
26 - new DefaultDeviceDescription(DURI, SWITCH, MFR, HW, SW, SN); 28 + new DefaultDeviceDescription(DURI, SWITCH, MFR, HW, SW, SN, CID);
27 assertEquals("incorrect uri", DURI, device.deviceURI()); 29 assertEquals("incorrect uri", DURI, device.deviceURI());
28 assertEquals("incorrect type", SWITCH, device.type()); 30 assertEquals("incorrect type", SWITCH, device.type());
29 assertEquals("incorrect manufacturer", MFR, device.manufacturer()); 31 assertEquals("incorrect manufacturer", MFR, device.manufacturer());
...@@ -31,6 +33,7 @@ public class DefaultDeviceDescriptionTest { ...@@ -31,6 +33,7 @@ public class DefaultDeviceDescriptionTest {
31 assertEquals("incorrect sw", SW, device.swVersion()); 33 assertEquals("incorrect sw", SW, device.swVersion());
32 assertEquals("incorrect serial", SN, device.serialNumber()); 34 assertEquals("incorrect serial", SN, device.serialNumber());
33 assertTrue("incorrect toString", device.toString().contains("uri=of:foo")); 35 assertTrue("incorrect toString", device.toString().contains("uri=of:foo"));
36 + assertTrue("Incorrect chassis", device.chassisId().value() == 0);
34 } 37 }
35 38
36 } 39 }
......
...@@ -11,6 +11,7 @@ import org.onlab.onos.net.Device; ...@@ -11,6 +11,7 @@ import org.onlab.onos.net.Device;
11 import org.onlab.onos.net.Port; 11 import org.onlab.onos.net.Port;
12 import org.onlab.onos.net.PortNumber; 12 import org.onlab.onos.net.PortNumber;
13 import org.onlab.onos.net.provider.ProviderId; 13 import org.onlab.onos.net.provider.ProviderId;
14 +import org.onlab.packet.ChassisId;
14 15
15 /** 16 /**
16 * Tests of the device event. 17 * Tests of the device event.
...@@ -19,7 +20,7 @@ public class DeviceEventTest extends AbstractEventTest { ...@@ -19,7 +20,7 @@ public class DeviceEventTest extends AbstractEventTest {
19 20
20 private Device createDevice() { 21 private Device createDevice() {
21 return new DefaultDevice(new ProviderId("of", "foo"), deviceId("of:foo"), 22 return new DefaultDevice(new ProviderId("of", "foo"), deviceId("of:foo"),
22 - Device.Type.SWITCH, "box", "hw", "sw", "sn"); 23 + Device.Type.SWITCH, "box", "hw", "sw", "sn", new ChassisId());
23 } 24 }
24 25
25 @Override 26 @Override
......
...@@ -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
......
...@@ -18,9 +18,9 @@ public class DefaultGraphDescriptionTest { ...@@ -18,9 +18,9 @@ public class DefaultGraphDescriptionTest {
18 18
19 private static final DeviceId D3 = deviceId("3"); 19 private static final DeviceId D3 = deviceId("3");
20 20
21 - static final Device DEV1 = new DefaultDevice(PID, D1, SWITCH, "", "", "", ""); 21 + static final Device DEV1 = new DefaultDevice(PID, D1, SWITCH, "", "", "", "", null);
22 - static final Device DEV2 = new DefaultDevice(PID, D2, SWITCH, "", "", "", ""); 22 + static final Device DEV2 = new DefaultDevice(PID, D2, SWITCH, "", "", "", "", null);
23 - static final Device DEV3 = new DefaultDevice(PID, D3, SWITCH, "", "", "", ""); 23 + static final Device DEV3 = new DefaultDevice(PID, D3, SWITCH, "", "", "", "", null);
24 24
25 @Test 25 @Test
26 public void basics() { 26 public void basics() {
......
...@@ -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
......
...@@ -3,6 +3,7 @@ package org.onlab.onos.cluster.impl; ...@@ -3,6 +3,7 @@ package org.onlab.onos.cluster.impl;
3 import static com.google.common.base.Preconditions.checkNotNull; 3 import static com.google.common.base.Preconditions.checkNotNull;
4 import static org.slf4j.LoggerFactory.getLogger; 4 import static org.slf4j.LoggerFactory.getLogger;
5 5
6 +import java.util.List;
6 import java.util.Set; 7 import java.util.Set;
7 import java.util.concurrent.atomic.AtomicInteger; 8 import java.util.concurrent.atomic.AtomicInteger;
8 9
...@@ -103,7 +104,6 @@ implements MastershipService, MastershipAdminService { ...@@ -103,7 +104,6 @@ implements MastershipService, MastershipAdminService {
103 MastershipEvent event = null; 104 MastershipEvent event = null;
104 event = store.relinquishRole( 105 event = store.relinquishRole(
105 clusterService.getLocalNode().id(), deviceId); 106 clusterService.getLocalNode().id(), deviceId);
106 -
107 if (event != null) { 107 if (event != null) {
108 post(event); 108 post(event);
109 } 109 }
...@@ -127,6 +127,11 @@ implements MastershipService, MastershipAdminService { ...@@ -127,6 +127,11 @@ implements MastershipService, MastershipAdminService {
127 return store.getDevices(nodeId); 127 return store.getDevices(nodeId);
128 } 128 }
129 129
130 + @Override
131 + public List<NodeId> getNodesFor(DeviceId deviceId) {
132 + checkNotNull(deviceId, DEVICE_ID_NULL);
133 + return store.getNodes(deviceId);
134 + }
130 135
131 @Override 136 @Override
132 public MastershipTermService requestTermService() { 137 public MastershipTermService requestTermService() {
...@@ -223,7 +228,8 @@ implements MastershipService, MastershipAdminService { ...@@ -223,7 +228,8 @@ implements MastershipService, MastershipAdminService {
223 return true; 228 return true;
224 } 229 }
225 //else { 230 //else {
226 - //FIXME: break tie for equal-sized clusters, can we use hz's functions? 231 + //FIXME: break tie for equal-sized clusters,
232 + // maybe by number of connected switches
227 // } 233 // }
228 return false; 234 return false;
229 } 235 }
......
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
......
...@@ -144,7 +144,7 @@ public class DeviceManager ...@@ -144,7 +144,7 @@ public class DeviceManager
144 144
145 // Applies the specified role to the device; ignores NONE 145 // Applies the specified role to the device; ignores NONE
146 private void applyRole(DeviceId deviceId, MastershipRole newRole) { 146 private void applyRole(DeviceId deviceId, MastershipRole newRole) {
147 - if (newRole.equals(MastershipRole.NONE)) { 147 + if (!newRole.equals(MastershipRole.NONE)) {
148 Device device = store.getDevice(deviceId); 148 Device device = store.getDevice(deviceId);
149 // FIXME: Device might not be there yet. (eventual consistent) 149 // FIXME: Device might not be there yet. (eventual consistent)
150 if (device == null) { 150 if (device == null) {
...@@ -161,6 +161,9 @@ public class DeviceManager ...@@ -161,6 +161,9 @@ public class DeviceManager
161 @Override 161 @Override
162 public void removeDevice(DeviceId deviceId) { 162 public void removeDevice(DeviceId deviceId) {
163 checkNotNull(deviceId, DEVICE_ID_NULL); 163 checkNotNull(deviceId, DEVICE_ID_NULL);
164 + // XXX is this intended to apply to the full global topology?
165 + // if so, we probably don't want the fact that we aren't
166 + // MASTER to get in the way, as it would do now.
164 DeviceEvent event = store.removeDevice(deviceId); 167 DeviceEvent event = store.removeDevice(deviceId);
165 if (event != null) { 168 if (event != null) {
166 log.info("Device {} administratively removed", deviceId); 169 log.info("Device {} administratively removed", deviceId);
...@@ -203,19 +206,21 @@ public class DeviceManager ...@@ -203,19 +206,21 @@ public class DeviceManager
203 log.info("Device {} connected", deviceId); 206 log.info("Device {} connected", deviceId);
204 // check my Role 207 // check my Role
205 MastershipRole role = mastershipService.requestRoleFor(deviceId); 208 MastershipRole role = mastershipService.requestRoleFor(deviceId);
206 - 209 + log.info("## - our role for {} is {} [master is {}]", deviceId, role,
210 + mastershipService.getMasterFor(deviceId));
207 if (role != MastershipRole.MASTER) { 211 if (role != MastershipRole.MASTER) {
208 // TODO: Do we need to explicitly tell the Provider that 212 // TODO: Do we need to explicitly tell the Provider that
209 // this instance is no longer the MASTER? probably not 213 // this instance is no longer the MASTER? probably not
210 return; 214 return;
211 } 215 }
212 -
213 MastershipTerm term = mastershipService.requestTermService() 216 MastershipTerm term = mastershipService.requestTermService()
214 .getMastershipTerm(deviceId); 217 .getMastershipTerm(deviceId);
218 +
215 if (!term.master().equals(clusterService.getLocalNode().id())) { 219 if (!term.master().equals(clusterService.getLocalNode().id())) {
216 // lost mastership after requestRole told this instance was MASTER. 220 // lost mastership after requestRole told this instance was MASTER.
217 return; 221 return;
218 } 222 }
223 +
219 // tell clock provider if this instance is the master 224 // tell clock provider if this instance is the master
220 deviceClockProviderService.setMastershipTerm(deviceId, term); 225 deviceClockProviderService.setMastershipTerm(deviceId, term);
221 226
...@@ -256,19 +261,38 @@ public class DeviceManager ...@@ -256,19 +261,38 @@ public class DeviceManager
256 // but if I was the last STANDBY connection, etc. and no one else 261 // but if I was the last STANDBY connection, etc. and no one else
257 // was there to mark the device offline, this instance may need to 262 // was there to mark the device offline, this instance may need to
258 // temporarily request for Master Role and mark offline. 263 // temporarily request for Master Role and mark offline.
264 + log.info("## for {} role is {}", deviceId, mastershipService.getLocalRole(deviceId));
259 if (!mastershipService.getLocalRole(deviceId).equals(MastershipRole.MASTER)) { 265 if (!mastershipService.getLocalRole(deviceId).equals(MastershipRole.MASTER)) {
260 log.debug("Device {} disconnected, but I am not the master", deviceId); 266 log.debug("Device {} disconnected, but I am not the master", deviceId);
261 //let go of ability to be backup 267 //let go of ability to be backup
262 mastershipService.relinquishMastership(deviceId); 268 mastershipService.relinquishMastership(deviceId);
263 return; 269 return;
264 } 270 }
265 - DeviceEvent event = store.markOffline(deviceId);
266 - //relinquish master role and ability to be backup.
267 - mastershipService.relinquishMastership(deviceId);
268 271
269 - if (event != null) { 272 + DeviceEvent event = null;
270 - log.info("Device {} disconnected", deviceId); 273 + try {
271 - post(event); 274 + event = store.markOffline(deviceId);
275 + } catch (IllegalStateException e) {
276 + //there are times when this node will correctly have mastership, BUT
277 + //that isn't reflected in the ClockManager before the device disconnects.
278 + //we want to let go of the device anyways, so make sure this happens.
279 +
280 + // FIXME: Come up with workaround for above scenario.
281 + MastershipTerm term = termService.getMastershipTerm(deviceId);
282 + final NodeId myNodeId = clusterService.getLocalNode().id();
283 + // TODO: Move this type of check inside device clock manager, etc.
284 + if (myNodeId.equals(term.master())) {
285 + deviceClockProviderService.setMastershipTerm(deviceId, term);
286 + event = store.markOffline(deviceId);
287 + }
288 + } finally {
289 + //relinquish master role and ability to be backup.
290 + mastershipService.relinquishMastership(deviceId);
291 +
292 + if (event != null) {
293 + log.info("Device {} disconnected", deviceId);
294 + post(event);
295 + }
272 } 296 }
273 } 297 }
274 298
...@@ -279,7 +303,15 @@ public class DeviceManager ...@@ -279,7 +303,15 @@ public class DeviceManager
279 checkNotNull(portDescriptions, 303 checkNotNull(portDescriptions,
280 "Port descriptions list cannot be null"); 304 "Port descriptions list cannot be null");
281 checkValidity(); 305 checkValidity();
306 + //XXX what's this doing here?
282 this.provider().id(); 307 this.provider().id();
308 +
309 + if (!mastershipService.getLocalRole(deviceId).equals(MastershipRole.MASTER)) {
310 + // TODO If we become master, then we'll trigger something to update this
311 + // info to fix any inconsistencies that may result during the handoff.
312 + return;
313 + }
314 +
283 List<DeviceEvent> events = store.updatePorts(this.provider().id(), 315 List<DeviceEvent> events = store.updatePorts(this.provider().id(),
284 deviceId, portDescriptions); 316 deviceId, portDescriptions);
285 for (DeviceEvent event : events) { 317 for (DeviceEvent event : events) {
...@@ -293,6 +325,12 @@ public class DeviceManager ...@@ -293,6 +325,12 @@ public class DeviceManager
293 checkNotNull(deviceId, DEVICE_ID_NULL); 325 checkNotNull(deviceId, DEVICE_ID_NULL);
294 checkNotNull(portDescription, PORT_DESCRIPTION_NULL); 326 checkNotNull(portDescription, PORT_DESCRIPTION_NULL);
295 checkValidity(); 327 checkValidity();
328 +
329 + if (!mastershipService.getLocalRole(deviceId).equals(MastershipRole.MASTER)) {
330 + // TODO If we become master, then we'll trigger something to update this
331 + // info to fix any inconsistencies that may result during the handoff.
332 + return;
333 + }
296 DeviceEvent event = store.updatePortStatus(this.provider().id(), 334 DeviceEvent event = store.updatePortStatus(this.provider().id(),
297 deviceId, portDescription); 335 deviceId, portDescription);
298 if (event != null) { 336 if (event != null) {
...@@ -328,27 +366,37 @@ public class DeviceManager ...@@ -328,27 +366,37 @@ public class DeviceManager
328 final DeviceId did = event.subject(); 366 final DeviceId did = event.subject();
329 final NodeId myNodeId = clusterService.getLocalNode().id(); 367 final NodeId myNodeId = clusterService.getLocalNode().id();
330 368
369 + log.info("## got Mastershipevent for dev {}", did);
331 if (myNodeId.equals(event.master())) { 370 if (myNodeId.equals(event.master())) {
332 MastershipTerm term = termService.getMastershipTerm(did); 371 MastershipTerm term = termService.getMastershipTerm(did);
333 372
334 - if (term.master().equals(myNodeId)) { 373 + if (!myNodeId.equals(term.master())) {
335 - // only set the new term if I am the master 374 + // something went wrong in consistency, let go
336 - deviceClockProviderService.setMastershipTerm(did, term); 375 + mastershipService.relinquishMastership(did);
376 + applyRole(did, MastershipRole.STANDBY);
377 + return;
337 } 378 }
338 379
380 + log.info("## setting term for CPS as new master for {}", did);
381 + // only set the new term if I am the master
382 + deviceClockProviderService.setMastershipTerm(did, term);
383 +
339 // FIXME: we should check that the device is connected on our end. 384 // FIXME: we should check that the device is connected on our end.
340 // currently, this is not straight forward as the actual switch 385 // currently, this is not straight forward as the actual switch
341 - // implementation is hidden from the registry. 386 + // implementation is hidden from the registry. Maybe we can ask the
342 - if (!isAvailable(did)) { 387 + // provider.
388 + // if the device is null here, we are the first master to claim the
389 + // device. No worries, the DeviceManager will create one soon.
390 + Device device = getDevice(did);
391 + if ((device != null) && !isAvailable(did)) {
343 //flag the device as online. Is there a better way to do this? 392 //flag the device as online. Is there a better way to do this?
344 - Device device = getDevice(did);
345 store.createOrUpdateDevice(device.providerId(), did, 393 store.createOrUpdateDevice(device.providerId(), did,
346 new DefaultDeviceDescription( 394 new DefaultDeviceDescription(
347 did.uri(), device.type(), device.manufacturer(), 395 did.uri(), device.type(), device.manufacturer(),
348 device.hwVersion(), device.swVersion(), 396 device.hwVersion(), device.swVersion(),
349 - device.serialNumber())); 397 + device.serialNumber(), device.chassisId()));
350 } 398 }
351 - 399 + //TODO re-collect device information to fix potential staleness
352 applyRole(did, MastershipRole.MASTER); 400 applyRole(did, MastershipRole.MASTER);
353 } else { 401 } else {
354 applyRole(did, MastershipRole.STANDBY); 402 applyRole(did, MastershipRole.STANDBY);
......
...@@ -3,8 +3,8 @@ package org.onlab.onos.net.flow.impl; ...@@ -3,8 +3,8 @@ package org.onlab.onos.net.flow.impl;
3 import static com.google.common.base.Preconditions.checkNotNull; 3 import static com.google.common.base.Preconditions.checkNotNull;
4 import static org.slf4j.LoggerFactory.getLogger; 4 import static org.slf4j.LoggerFactory.getLogger;
5 5
6 -import java.util.Iterator;
7 import java.util.List; 6 import java.util.List;
7 +import java.util.Map;
8 import java.util.concurrent.CancellationException; 8 import java.util.concurrent.CancellationException;
9 import java.util.concurrent.ExecutionException; 9 import java.util.concurrent.ExecutionException;
10 import java.util.concurrent.Future; 10 import java.util.concurrent.Future;
...@@ -45,6 +45,7 @@ import org.slf4j.Logger; ...@@ -45,6 +45,7 @@ import org.slf4j.Logger;
45 45
46 import com.google.common.collect.ArrayListMultimap; 46 import com.google.common.collect.ArrayListMultimap;
47 import com.google.common.collect.Lists; 47 import com.google.common.collect.Lists;
48 +import com.google.common.collect.Maps;
48 import com.google.common.collect.Multimap; 49 import com.google.common.collect.Multimap;
49 50
50 /** 51 /**
...@@ -197,6 +198,8 @@ public class FlowRuleManager ...@@ -197,6 +198,8 @@ public class FlowRuleManager
197 extends AbstractProviderService<FlowRuleProvider> 198 extends AbstractProviderService<FlowRuleProvider>
198 implements FlowRuleProviderService { 199 implements FlowRuleProviderService {
199 200
201 + final Map<FlowEntry, Long> lastSeen = Maps.newConcurrentMap();
202 +
200 protected InternalFlowRuleProviderService(FlowRuleProvider provider) { 203 protected InternalFlowRuleProviderService(FlowRuleProvider provider) {
201 super(provider); 204 super(provider);
202 } 205 }
...@@ -205,6 +208,7 @@ public class FlowRuleManager ...@@ -205,6 +208,7 @@ public class FlowRuleManager
205 public void flowRemoved(FlowEntry flowEntry) { 208 public void flowRemoved(FlowEntry flowEntry) {
206 checkNotNull(flowEntry, FLOW_RULE_NULL); 209 checkNotNull(flowEntry, FLOW_RULE_NULL);
207 checkValidity(); 210 checkValidity();
211 + lastSeen.remove(flowEntry);
208 FlowEntry stored = store.getFlowEntry(flowEntry); 212 FlowEntry stored = store.getFlowEntry(flowEntry);
209 if (stored == null) { 213 if (stored == null) {
210 log.info("Rule already evicted from store: {}", flowEntry); 214 log.info("Rule already evicted from store: {}", flowEntry);
...@@ -292,14 +296,25 @@ public class FlowRuleManager ...@@ -292,14 +296,25 @@ public class FlowRuleManager
292 if (storedRule == null) { 296 if (storedRule == null) {
293 return false; 297 return false;
294 } 298 }
295 - long timeout = storedRule.timeout() * 1000; 299 + final long timeout = storedRule.timeout() * 1000;
296 - Long currentTime = System.currentTimeMillis(); 300 + final long currentTime = System.currentTimeMillis();
297 if (storedRule.packets() != swRule.packets()) { 301 if (storedRule.packets() != swRule.packets()) {
298 - storedRule.setLastSeen(); 302 + lastSeen.put(storedRule, currentTime);
299 return true; 303 return true;
300 } 304 }
305 + if (!lastSeen.containsKey(storedRule)) {
306 + // checking for the first time
307 + lastSeen.put(storedRule, storedRule.lastSeen());
308 + // Use following if lastSeen attr. was removed.
309 + //lastSeen.put(storedRule, currentTime);
310 + }
311 + Long last = lastSeen.get(storedRule);
312 + if (last == null) {
313 + // concurrently removed? let the liveness check fail
314 + return false;
315 + }
301 316
302 - if ((currentTime - storedRule.lastSeen()) <= timeout) { 317 + if ((currentTime - last) <= timeout) {
303 return true; 318 return true;
304 } 319 }
305 return false; 320 return false;
...@@ -316,10 +331,7 @@ public class FlowRuleManager ...@@ -316,10 +331,7 @@ public class FlowRuleManager
316 public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries) { 331 public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries) {
317 List<FlowEntry> storedRules = Lists.newLinkedList(store.getFlowEntries(deviceId)); 332 List<FlowEntry> storedRules = Lists.newLinkedList(store.getFlowEntries(deviceId));
318 333
319 - Iterator<FlowEntry> switchRulesIterator = flowEntries.iterator(); 334 + for (FlowEntry rule : flowEntries) {
320 -
321 - while (switchRulesIterator.hasNext()) {
322 - FlowEntry rule = switchRulesIterator.next();
323 if (storedRules.remove(rule)) { 335 if (storedRules.remove(rule)) {
324 // we both have the rule, let's update some info then. 336 // we both have the rule, let's update some info then.
325 flowAdded(rule); 337 flowAdded(rule);
...@@ -401,7 +413,7 @@ public class FlowRuleManager ...@@ -401,7 +413,7 @@ public class FlowRuleManager
401 CompletedBatchOperation completed; 413 CompletedBatchOperation completed;
402 for (Future<CompletedBatchOperation> future : futures) { 414 for (Future<CompletedBatchOperation> future : futures) {
403 completed = future.get(); 415 completed = future.get();
404 - success = validateBatchOperation(failed, completed, future); 416 + success = validateBatchOperation(failed, completed);
405 } 417 }
406 418
407 return finalizeBatchOperation(success, failed); 419 return finalizeBatchOperation(success, failed);
...@@ -426,14 +438,13 @@ public class FlowRuleManager ...@@ -426,14 +438,13 @@ public class FlowRuleManager
426 long now = System.nanoTime(); 438 long now = System.nanoTime();
427 long thisTimeout = end - now; 439 long thisTimeout = end - now;
428 completed = future.get(thisTimeout, TimeUnit.NANOSECONDS); 440 completed = future.get(thisTimeout, TimeUnit.NANOSECONDS);
429 - success = validateBatchOperation(failed, completed, future); 441 + success = validateBatchOperation(failed, completed);
430 } 442 }
431 return finalizeBatchOperation(success, failed); 443 return finalizeBatchOperation(success, failed);
432 } 444 }
433 445
434 private boolean validateBatchOperation(List<FlowEntry> failed, 446 private boolean validateBatchOperation(List<FlowEntry> failed,
435 - CompletedBatchOperation completed, 447 + CompletedBatchOperation completed) {
436 - Future<CompletedBatchOperation> future) {
437 448
438 if (isCancelled()) { 449 if (isCancelled()) {
439 throw new CancellationException(); 450 throw new CancellationException();
......
...@@ -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() {
......
1 package org.onlab.onos.net.intent.impl; 1 package org.onlab.onos.net.intent.impl;
2 2
3 +import static org.onlab.onos.net.flow.DefaultTrafficTreatment.builder;
4 +import static org.slf4j.LoggerFactory.getLogger;
5 +
3 import java.util.List; 6 import java.util.List;
4 import java.util.concurrent.Future; 7 import java.util.concurrent.Future;
5 8
...@@ -10,7 +13,9 @@ import org.apache.felix.scr.annotations.Reference; ...@@ -10,7 +13,9 @@ import org.apache.felix.scr.annotations.Reference;
10 import org.apache.felix.scr.annotations.ReferenceCardinality; 13 import org.apache.felix.scr.annotations.ReferenceCardinality;
11 import org.onlab.onos.ApplicationId; 14 import org.onlab.onos.ApplicationId;
12 import org.onlab.onos.CoreService; 15 import org.onlab.onos.CoreService;
16 +import org.onlab.onos.net.DeviceId;
13 import org.onlab.onos.net.Link; 17 import org.onlab.onos.net.Link;
18 +import org.onlab.onos.net.PortNumber;
14 import org.onlab.onos.net.flow.CompletedBatchOperation; 19 import org.onlab.onos.net.flow.CompletedBatchOperation;
15 import org.onlab.onos.net.flow.DefaultFlowRule; 20 import org.onlab.onos.net.flow.DefaultFlowRule;
16 import org.onlab.onos.net.flow.DefaultTrafficSelector; 21 import org.onlab.onos.net.flow.DefaultTrafficSelector;
...@@ -29,9 +34,6 @@ import org.slf4j.Logger; ...@@ -29,9 +34,6 @@ import org.slf4j.Logger;
29 34
30 import com.google.common.collect.Lists; 35 import com.google.common.collect.Lists;
31 36
32 -import static org.onlab.onos.net.flow.DefaultTrafficTreatment.builder;
33 -import static org.slf4j.LoggerFactory.getLogger;
34 -
35 /** 37 /**
36 * Installer for {@link org.onlab.onos.net.intent.LinkCollectionIntent} 38 * Installer for {@link org.onlab.onos.net.intent.LinkCollectionIntent}
37 * path segment intents. 39 * path segment intents.
...@@ -79,15 +81,17 @@ public class LinkCollectionIntentInstaller implements IntentInstaller<LinkCollec ...@@ -79,15 +81,17 @@ public class LinkCollectionIntentInstaller implements IntentInstaller<LinkCollec
79 DefaultTrafficSelector.builder(intent.selector()); 81 DefaultTrafficSelector.builder(intent.selector());
80 List<FlowRuleBatchEntry> rules = Lists.newLinkedList(); 82 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
81 for (Link link : intent.links()) { 83 for (Link link : intent.links()) {
82 - TrafficTreatment treatment = builder() 84 + rules.add(createBatchEntry(FlowRuleOperation.ADD,
83 - .setOutput(link.src().port()).build(); 85 + builder.build(),
84 - 86 + link.src().deviceId(),
85 - FlowRule rule = new DefaultFlowRule(link.src().deviceId(), 87 + link.src().port()));
86 - builder.build(), treatment,
87 - 123, appId, 600);
88 - rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule));
89 } 88 }
90 89
90 + rules.add(createBatchEntry(FlowRuleOperation.ADD,
91 + builder.build(),
92 + intent.egressPoint().deviceId(),
93 + intent.egressPoint().port()));
94 +
91 return applyBatch(rules); 95 return applyBatch(rules);
92 } 96 }
93 97
...@@ -98,13 +102,39 @@ public class LinkCollectionIntentInstaller implements IntentInstaller<LinkCollec ...@@ -98,13 +102,39 @@ public class LinkCollectionIntentInstaller implements IntentInstaller<LinkCollec
98 List<FlowRuleBatchEntry> rules = Lists.newLinkedList(); 102 List<FlowRuleBatchEntry> rules = Lists.newLinkedList();
99 103
100 for (Link link : intent.links()) { 104 for (Link link : intent.links()) {
101 - TrafficTreatment treatment = builder() 105 + rules.add(createBatchEntry(FlowRuleOperation.REMOVE,
102 - .setOutput(link.src().port()).build(); 106 + builder.build(),
103 - FlowRule rule = new DefaultFlowRule(link.src().deviceId(), 107 + link.src().deviceId(),
104 - builder.build(), treatment, 108 + link.src().port()));
105 - 123, appId, 600);
106 - rules.add(new FlowRuleBatchEntry(FlowRuleOperation.REMOVE, rule));
107 } 109 }
110 +
111 + rules.add(createBatchEntry(FlowRuleOperation.REMOVE,
112 + builder.build(),
113 + intent.egressPoint().deviceId(),
114 + intent.egressPoint().port()));
115 +
108 return applyBatch(rules); 116 return applyBatch(rules);
109 } 117 }
118 +
119 + /**
120 + * Creates a FlowRuleBatchEntry based on the provided parameters.
121 + *
122 + * @param operation the FlowRuleOperation to use
123 + * @param selector the traffic selector
124 + * @param deviceId the device ID for the flow rule
125 + * @param outPort the output port of the flow rule
126 + * @return the new flow rule batch entry
127 + */
128 + private FlowRuleBatchEntry createBatchEntry(FlowRuleOperation operation,
129 + TrafficSelector selector,
130 + DeviceId deviceId,
131 + PortNumber outPort) {
132 +
133 + TrafficTreatment treatment = builder().setOutput(outPort).build();
134 +
135 + FlowRule rule = new DefaultFlowRule(deviceId,
136 + selector, treatment, 123, appId, 600);
137 +
138 + return new FlowRuleBatchEntry(operation, rule);
139 + }
110 } 140 }
......
...@@ -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() {
...@@ -62,7 +62,7 @@ public class MultiPointToSinglePointIntentCompiler ...@@ -62,7 +62,7 @@ public class MultiPointToSinglePointIntentCompiler
62 62
63 Intent result = new LinkCollectionIntent(intentIdGenerator.getNewId(), 63 Intent result = new LinkCollectionIntent(intentIdGenerator.getNewId(),
64 intent.selector(), intent.treatment(), 64 intent.selector(), intent.treatment(),
65 - links); 65 + links, intent.egressPoint());
66 return Arrays.asList(result); 66 return Arrays.asList(result);
67 } 67 }
68 68
......
...@@ -96,7 +96,7 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> { ...@@ -96,7 +96,7 @@ public class PathIntentInstaller implements IntentInstaller<PathIntent> {
96 96
97 FlowRule rule = new DefaultFlowRule(link.src().deviceId(), 97 FlowRule rule = new DefaultFlowRule(link.src().deviceId(),
98 builder.build(), treatment, 98 builder.build(), treatment,
99 - 123, appId, 600); 99 + 123, appId, 15);
100 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule)); 100 rules.add(new FlowRuleBatchEntry(FlowRuleOperation.ADD, rule));
101 prev = link.dst(); 101 prev = link.dst();
102 } 102 }
......
...@@ -43,7 +43,7 @@ public class PointToPointIntentCompiler ...@@ -43,7 +43,7 @@ public class PointToPointIntentCompiler
43 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 43 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
44 protected HostService hostService; 44 protected HostService hostService;
45 45
46 - private IdGenerator<IntentId> intentIdGenerator; 46 + protected IdGenerator<IntentId> intentIdGenerator;
47 47
48 @Activate 48 @Activate
49 public void activate() { 49 public void activate() {
......
...@@ -16,6 +16,7 @@ import org.onlab.onos.event.EventDeliveryService; ...@@ -16,6 +16,7 @@ import org.onlab.onos.event.EventDeliveryService;
16 import org.onlab.onos.net.ConnectPoint; 16 import org.onlab.onos.net.ConnectPoint;
17 import org.onlab.onos.net.DeviceId; 17 import org.onlab.onos.net.DeviceId;
18 import org.onlab.onos.net.Link; 18 import org.onlab.onos.net.Link;
19 +import org.onlab.onos.net.MastershipRole;
19 import org.onlab.onos.net.device.DeviceEvent; 20 import org.onlab.onos.net.device.DeviceEvent;
20 import org.onlab.onos.net.device.DeviceListener; 21 import org.onlab.onos.net.device.DeviceListener;
21 import org.onlab.onos.net.device.DeviceService; 22 import org.onlab.onos.net.device.DeviceService;
...@@ -139,11 +140,17 @@ public class LinkManager ...@@ -139,11 +140,17 @@ public class LinkManager
139 140
140 @Override 141 @Override
141 public void removeLinks(ConnectPoint connectPoint) { 142 public void removeLinks(ConnectPoint connectPoint) {
143 + if (deviceService.getRole(connectPoint.deviceId()) != MastershipRole.MASTER) {
144 + return;
145 + }
142 removeLinks(getLinks(connectPoint)); 146 removeLinks(getLinks(connectPoint));
143 } 147 }
144 148
145 @Override 149 @Override
146 public void removeLinks(DeviceId deviceId) { 150 public void removeLinks(DeviceId deviceId) {
151 + if (deviceService.getRole(deviceId) != MastershipRole.MASTER) {
152 + return;
153 + }
147 removeLinks(getDeviceLinks(deviceId)); 154 removeLinks(getDeviceLinks(deviceId));
148 } 155 }
149 156
...@@ -189,6 +196,15 @@ public class LinkManager ...@@ -189,6 +196,15 @@ public class LinkManager
189 public void linkDetected(LinkDescription linkDescription) { 196 public void linkDetected(LinkDescription linkDescription) {
190 checkNotNull(linkDescription, LINK_DESC_NULL); 197 checkNotNull(linkDescription, LINK_DESC_NULL);
191 checkValidity(); 198 checkValidity();
199 +
200 + ConnectPoint src = linkDescription.src();
201 + ConnectPoint dst = linkDescription.dst();
202 + // if we aren't master for the device associated with the ConnectPoint
203 + // we probably shouldn't be doing this.
204 + if ((deviceService.getRole(src.deviceId()) != MastershipRole.MASTER) ||
205 + (deviceService.getRole(dst.deviceId()) != MastershipRole.MASTER)) {
206 + return;
207 + }
192 LinkEvent event = store.createOrUpdateLink(provider().id(), 208 LinkEvent event = store.createOrUpdateLink(provider().id(),
193 linkDescription); 209 linkDescription);
194 if (event != null) { 210 if (event != null) {
...@@ -201,6 +217,15 @@ public class LinkManager ...@@ -201,6 +217,15 @@ public class LinkManager
201 public void linkVanished(LinkDescription linkDescription) { 217 public void linkVanished(LinkDescription linkDescription) {
202 checkNotNull(linkDescription, LINK_DESC_NULL); 218 checkNotNull(linkDescription, LINK_DESC_NULL);
203 checkValidity(); 219 checkValidity();
220 +
221 + ConnectPoint src = linkDescription.src();
222 + ConnectPoint dst = linkDescription.dst();
223 + // if we aren't master for the device associated with the ConnectPoint
224 + // we probably shouldn't be doing this.
225 + if ((deviceService.getRole(src.deviceId()) != MastershipRole.MASTER) ||
226 + (deviceService.getRole(dst.deviceId()) != MastershipRole.MASTER)) {
227 + return;
228 + }
204 LinkEvent event = store.removeLink(linkDescription.src(), 229 LinkEvent event = store.removeLink(linkDescription.src(),
205 linkDescription.dst()); 230 linkDescription.dst());
206 if (event != null) { 231 if (event != null) {
...@@ -213,7 +238,13 @@ public class LinkManager ...@@ -213,7 +238,13 @@ public class LinkManager
213 public void linksVanished(ConnectPoint connectPoint) { 238 public void linksVanished(ConnectPoint connectPoint) {
214 checkNotNull(connectPoint, "Connect point cannot be null"); 239 checkNotNull(connectPoint, "Connect point cannot be null");
215 checkValidity(); 240 checkValidity();
241 + // if we aren't master for the device associated with the ConnectPoint
242 + // we probably shouldn't be doing this.
243 + if (deviceService.getRole(connectPoint.deviceId()) != MastershipRole.MASTER) {
244 + return;
245 + }
216 log.info("Links for connection point {} vanished", connectPoint); 246 log.info("Links for connection point {} vanished", connectPoint);
247 + // FIXME: This will remove links registered by other providers
217 removeLinks(getLinks(connectPoint)); 248 removeLinks(getLinks(connectPoint));
218 } 249 }
219 250
...@@ -221,6 +252,11 @@ public class LinkManager ...@@ -221,6 +252,11 @@ public class LinkManager
221 public void linksVanished(DeviceId deviceId) { 252 public void linksVanished(DeviceId deviceId) {
222 checkNotNull(deviceId, DEVICE_ID_NULL); 253 checkNotNull(deviceId, DEVICE_ID_NULL);
223 checkValidity(); 254 checkValidity();
255 + // if we aren't master for the device associated with the ConnectPoint
256 + // we probably shouldn't be doing this.
257 + if (deviceService.getRole(deviceId) != MastershipRole.MASTER) {
258 + return;
259 + }
224 log.info("Links for device {} vanished", deviceId); 260 log.info("Links for device {} vanished", deviceId);
225 removeLinks(getDeviceLinks(deviceId)); 261 removeLinks(getDeviceLinks(deviceId));
226 } 262 }
......
...@@ -167,6 +167,7 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -167,6 +167,7 @@ public class ProxyArpManager implements ProxyArpService {
167 return; 167 return;
168 } 168 }
169 169
170 + // TODO find the correct IP address
170 Ethernet arpReply = buildArpReply(dst.ipAddresses().iterator().next(), 171 Ethernet arpReply = buildArpReply(dst.ipAddresses().iterator().next(),
171 dst.mac(), eth); 172 dst.mac(), eth);
172 // TODO: check send status with host service. 173 // TODO: check send status with host service.
...@@ -355,7 +356,7 @@ public class ProxyArpManager implements ProxyArpService { ...@@ -355,7 +356,7 @@ public class ProxyArpManager implements ProxyArpService {
355 356
356 arp.setTargetProtocolAddress(((ARP) request.getPayload()) 357 arp.setTargetProtocolAddress(((ARP) request.getPayload())
357 .getSenderProtocolAddress()); 358 .getSenderProtocolAddress());
358 - arp.setSenderProtocolAddress(srcIp.toRealInt()); 359 + arp.setSenderProtocolAddress(srcIp.toInt());
359 eth.setPayload(arp); 360 eth.setPayload(arp);
360 return eth; 361 return eth;
361 } 362 }
......
...@@ -23,6 +23,7 @@ import org.onlab.onos.net.topology.TopologyProviderRegistry; ...@@ -23,6 +23,7 @@ import org.onlab.onos.net.topology.TopologyProviderRegistry;
23 import org.onlab.onos.net.topology.TopologyProviderService; 23 import org.onlab.onos.net.topology.TopologyProviderService;
24 import org.slf4j.Logger; 24 import org.slf4j.Logger;
25 25
26 +import java.util.Collections;
26 import java.util.List; 27 import java.util.List;
27 import java.util.Timer; 28 import java.util.Timer;
28 import java.util.concurrent.ExecutorService; 29 import java.util.concurrent.ExecutorService;
...@@ -88,7 +89,7 @@ public class DefaultTopologyProvider extends AbstractProvider ...@@ -88,7 +89,7 @@ public class DefaultTopologyProvider extends AbstractProvider
88 linkService.addListener(linkListener); 89 linkService.addListener(linkListener);
89 90
90 isStarted = true; 91 isStarted = true;
91 - triggerTopologyBuild(null); 92 + triggerTopologyBuild(Collections.<Event>emptyList());
92 log.info("Started"); 93 log.info("Started");
93 } 94 }
94 95
......
...@@ -37,6 +37,7 @@ import org.onlab.onos.net.device.PortDescription; ...@@ -37,6 +37,7 @@ import org.onlab.onos.net.device.PortDescription;
37 import org.onlab.onos.net.provider.AbstractProvider; 37 import org.onlab.onos.net.provider.AbstractProvider;
38 import org.onlab.onos.net.provider.ProviderId; 38 import org.onlab.onos.net.provider.ProviderId;
39 import org.onlab.onos.store.trivial.impl.SimpleDeviceStore; 39 import org.onlab.onos.store.trivial.impl.SimpleDeviceStore;
40 +import org.onlab.packet.ChassisId;
40 import org.onlab.packet.IpPrefix; 41 import org.onlab.packet.IpPrefix;
41 42
42 import java.util.ArrayList; 43 import java.util.ArrayList;
...@@ -62,6 +63,7 @@ public class DeviceManagerTest { ...@@ -62,6 +63,7 @@ public class DeviceManagerTest {
62 private static final String SW1 = "3.8.1"; 63 private static final String SW1 = "3.8.1";
63 private static final String SW2 = "3.9.5"; 64 private static final String SW2 = "3.9.5";
64 private static final String SN = "43311-12345"; 65 private static final String SN = "43311-12345";
66 + private static final ChassisId CID = new ChassisId();
65 67
66 private static final PortNumber P1 = PortNumber.portNumber(1); 68 private static final PortNumber P1 = PortNumber.portNumber(1);
67 private static final PortNumber P2 = PortNumber.portNumber(2); 69 private static final PortNumber P2 = PortNumber.portNumber(2);
...@@ -111,7 +113,7 @@ public class DeviceManagerTest { ...@@ -111,7 +113,7 @@ public class DeviceManagerTest {
111 private void connectDevice(DeviceId deviceId, String swVersion) { 113 private void connectDevice(DeviceId deviceId, String swVersion) {
112 DeviceDescription description = 114 DeviceDescription description =
113 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR, 115 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
114 - HW, swVersion, SN); 116 + HW, swVersion, SN, CID);
115 providerService.deviceConnected(deviceId, description); 117 providerService.deviceConnected(deviceId, description);
116 assertNotNull("device should be found", service.getDevice(DID1)); 118 assertNotNull("device should be found", service.getDevice(DID1));
117 } 119 }
......
...@@ -8,7 +8,9 @@ import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_UPDATED; ...@@ -8,7 +8,9 @@ import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_UPDATED;
8 8
9 import java.util.ArrayList; 9 import java.util.ArrayList;
10 import java.util.Collections; 10 import java.util.Collections;
11 +import java.util.HashMap;
11 import java.util.List; 12 import java.util.List;
13 +import java.util.Map;
12 import java.util.Set; 14 import java.util.Set;
13 import java.util.concurrent.ExecutionException; 15 import java.util.concurrent.ExecutionException;
14 import java.util.concurrent.Future; 16 import java.util.concurrent.Future;
...@@ -44,6 +46,7 @@ import org.onlab.onos.net.flow.FlowRuleProvider; ...@@ -44,6 +46,7 @@ import org.onlab.onos.net.flow.FlowRuleProvider;
44 import org.onlab.onos.net.flow.FlowRuleProviderRegistry; 46 import org.onlab.onos.net.flow.FlowRuleProviderRegistry;
45 import org.onlab.onos.net.flow.FlowRuleProviderService; 47 import org.onlab.onos.net.flow.FlowRuleProviderService;
46 import org.onlab.onos.net.flow.FlowRuleService; 48 import org.onlab.onos.net.flow.FlowRuleService;
49 +import org.onlab.onos.net.flow.StoredFlowEntry;
47 import org.onlab.onos.net.flow.TrafficSelector; 50 import org.onlab.onos.net.flow.TrafficSelector;
48 import org.onlab.onos.net.flow.TrafficTreatment; 51 import org.onlab.onos.net.flow.TrafficTreatment;
49 import org.onlab.onos.net.flow.criteria.Criterion; 52 import org.onlab.onos.net.flow.criteria.Criterion;
...@@ -54,6 +57,7 @@ import org.onlab.onos.net.provider.ProviderId; ...@@ -54,6 +57,7 @@ import org.onlab.onos.net.provider.ProviderId;
54 import org.onlab.onos.store.trivial.impl.SimpleFlowRuleStore; 57 import org.onlab.onos.store.trivial.impl.SimpleFlowRuleStore;
55 58
56 import com.google.common.collect.ImmutableList; 59 import com.google.common.collect.ImmutableList;
60 +import com.google.common.collect.ImmutableMap;
57 import com.google.common.collect.Lists; 61 import com.google.common.collect.Lists;
58 import com.google.common.collect.Sets; 62 import com.google.common.collect.Sets;
59 63
...@@ -68,7 +72,7 @@ public class FlowRuleManagerTest { ...@@ -68,7 +72,7 @@ public class FlowRuleManagerTest {
68 private static final DeviceId DID = DeviceId.deviceId("of:001"); 72 private static final DeviceId DID = DeviceId.deviceId("of:001");
69 private static final int TIMEOUT = 10; 73 private static final int TIMEOUT = 10;
70 private static final Device DEV = new DefaultDevice( 74 private static final Device DEV = new DefaultDevice(
71 - PID, DID, Type.SWITCH, "", "", "", ""); 75 + PID, DID, Type.SWITCH, "", "", "", "", null);
72 76
73 private FlowRuleManager mgr; 77 private FlowRuleManager mgr;
74 78
...@@ -166,16 +170,17 @@ public class FlowRuleManagerTest { ...@@ -166,16 +170,17 @@ public class FlowRuleManagerTest {
166 } 170 }
167 171
168 172
173 + // TODO: If preserving iteration order is a requirement, redo FlowRuleStore.
169 //backing store is sensitive to the order of additions/removals 174 //backing store is sensitive to the order of additions/removals
170 - private boolean validateState(FlowEntryState... state) { 175 + private boolean validateState(Map<FlowRule, FlowEntryState> expected) {
176 + Map<FlowRule, FlowEntryState> expectedToCheck = new HashMap<>(expected);
171 Iterable<FlowEntry> rules = service.getFlowEntries(DID); 177 Iterable<FlowEntry> rules = service.getFlowEntries(DID);
172 - int i = 0;
173 for (FlowEntry f : rules) { 178 for (FlowEntry f : rules) {
174 - if (f.state() != state[i]) { 179 + assertTrue("Unexpected FlowRule " + f, expectedToCheck.containsKey(f));
175 - return false; 180 + assertEquals("FlowEntry" + f, expectedToCheck.get(f), f.state());
176 - } 181 + expectedToCheck.remove(f);
177 - i++;
178 } 182 }
183 + assertEquals(Collections.emptySet(), expectedToCheck.entrySet());
179 return true; 184 return true;
180 } 185 }
181 186
...@@ -191,8 +196,10 @@ public class FlowRuleManagerTest { ...@@ -191,8 +196,10 @@ public class FlowRuleManagerTest {
191 mgr.applyFlowRules(r1, r2, r3); 196 mgr.applyFlowRules(r1, r2, r3);
192 assertEquals("3 rules should exist", 3, flowCount()); 197 assertEquals("3 rules should exist", 3, flowCount());
193 assertTrue("Entries should be pending add.", 198 assertTrue("Entries should be pending add.",
194 - validateState(FlowEntryState.PENDING_ADD, FlowEntryState.PENDING_ADD, 199 + validateState(ImmutableMap.of(
195 - FlowEntryState.PENDING_ADD)); 200 + r1, FlowEntryState.PENDING_ADD,
201 + r2, FlowEntryState.PENDING_ADD,
202 + r3, FlowEntryState.PENDING_ADD)));
196 } 203 }
197 204
198 @Test 205 @Test
...@@ -213,8 +220,10 @@ public class FlowRuleManagerTest { ...@@ -213,8 +220,10 @@ public class FlowRuleManagerTest {
213 validateEvents(); 220 validateEvents();
214 assertEquals("3 rule should exist", 3, flowCount()); 221 assertEquals("3 rule should exist", 3, flowCount());
215 assertTrue("Entries should be pending remove.", 222 assertTrue("Entries should be pending remove.",
216 - validateState(FlowEntryState.PENDING_REMOVE, FlowEntryState.PENDING_REMOVE, 223 + validateState(ImmutableMap.of(
217 - FlowEntryState.ADDED)); 224 + f1, FlowEntryState.PENDING_REMOVE,
225 + f2, FlowEntryState.PENDING_REMOVE,
226 + f3, FlowEntryState.ADDED)));
218 227
219 mgr.removeFlowRules(f1); 228 mgr.removeFlowRules(f1);
220 assertEquals("3 rule should still exist", 3, flowCount()); 229 assertEquals("3 rule should still exist", 3, flowCount());
...@@ -224,7 +233,7 @@ public class FlowRuleManagerTest { ...@@ -224,7 +233,7 @@ public class FlowRuleManagerTest {
224 public void flowRemoved() { 233 public void flowRemoved() {
225 FlowRule f1 = addFlowRule(1); 234 FlowRule f1 = addFlowRule(1);
226 FlowRule f2 = addFlowRule(2); 235 FlowRule f2 = addFlowRule(2);
227 - FlowEntry fe1 = new DefaultFlowEntry(f1); 236 + StoredFlowEntry fe1 = new DefaultFlowEntry(f1);
228 FlowEntry fe2 = new DefaultFlowEntry(f2); 237 FlowEntry fe2 = new DefaultFlowEntry(f2);
229 providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2)); 238 providerService.pushFlowMetrics(DID, ImmutableList.of(fe1, fe2));
230 service.removeFlowRules(f1); 239 service.removeFlowRules(f1);
...@@ -263,8 +272,10 @@ public class FlowRuleManagerTest { ...@@ -263,8 +272,10 @@ public class FlowRuleManagerTest {
263 providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2)); 272 providerService.pushFlowMetrics(DID, Lists.newArrayList(fe1, fe2));
264 273
265 assertTrue("Entries should be added.", 274 assertTrue("Entries should be added.",
266 - validateState(FlowEntryState.ADDED, FlowEntryState.ADDED, 275 + validateState(ImmutableMap.of(
267 - FlowEntryState.PENDING_ADD)); 276 + f1, FlowEntryState.ADDED,
277 + f2, FlowEntryState.ADDED,
278 + f3, FlowEntryState.PENDING_ADD)));
268 279
269 validateEvents(RULE_ADDED, RULE_ADDED); 280 validateEvents(RULE_ADDED, RULE_ADDED);
270 } 281 }
...@@ -336,7 +347,9 @@ public class FlowRuleManagerTest { ...@@ -336,7 +347,9 @@ public class FlowRuleManagerTest {
336 347
337 //only check that we are in pending remove. Events and actual remove state will 348 //only check that we are in pending remove. Events and actual remove state will
338 // be set by flowRemoved call. 349 // be set by flowRemoved call.
339 - validateState(FlowEntryState.PENDING_REMOVE, FlowEntryState.PENDING_REMOVE); 350 + validateState(ImmutableMap.of(
351 + f1, FlowEntryState.PENDING_REMOVE,
352 + f2, FlowEntryState.PENDING_REMOVE));
340 } 353 }
341 354
342 @Test 355 @Test
...@@ -360,7 +373,9 @@ public class FlowRuleManagerTest { ...@@ -360,7 +373,9 @@ public class FlowRuleManagerTest {
360 Lists.newArrayList(fbe1, fbe2)); 373 Lists.newArrayList(fbe1, fbe2));
361 Future<CompletedBatchOperation> future = mgr.applyBatch(fbo); 374 Future<CompletedBatchOperation> future = mgr.applyBatch(fbo);
362 assertTrue("Entries in wrong state", 375 assertTrue("Entries in wrong state",
363 - validateState(FlowEntryState.PENDING_REMOVE, FlowEntryState.PENDING_ADD)); 376 + validateState(ImmutableMap.of(
377 + f1, FlowEntryState.PENDING_REMOVE,
378 + f2, FlowEntryState.PENDING_ADD)));
364 CompletedBatchOperation completed = null; 379 CompletedBatchOperation completed = null;
365 try { 380 try {
366 completed = future.get(); 381 completed = future.get();
...@@ -381,9 +396,18 @@ public class FlowRuleManagerTest { ...@@ -381,9 +396,18 @@ public class FlowRuleManagerTest {
381 396
382 mgr.applyFlowRules(f1); 397 mgr.applyFlowRules(f1);
383 398
399 + assertTrue("Entries in wrong state",
400 + validateState(ImmutableMap.of(
401 + f1, FlowEntryState.PENDING_ADD)));
402 +
384 FlowEntry fe1 = new DefaultFlowEntry(f1); 403 FlowEntry fe1 = new DefaultFlowEntry(f1);
385 providerService.pushFlowMetrics(DID, Collections.<FlowEntry>singletonList(fe1)); 404 providerService.pushFlowMetrics(DID, Collections.<FlowEntry>singletonList(fe1));
386 405
406 + assertTrue("Entries in wrong state",
407 + validateState(ImmutableMap.of(
408 + f1, FlowEntryState.ADDED)));
409 +
410 +
387 FlowRuleBatchEntry fbe1 = new FlowRuleBatchEntry( 411 FlowRuleBatchEntry fbe1 = new FlowRuleBatchEntry(
388 FlowRuleBatchEntry.FlowRuleOperation.REMOVE, f1); 412 FlowRuleBatchEntry.FlowRuleOperation.REMOVE, f1);
389 413
...@@ -403,9 +427,9 @@ public class FlowRuleManagerTest { ...@@ -403,9 +427,9 @@ public class FlowRuleManagerTest {
403 * state. 427 * state.
404 */ 428 */
405 assertTrue("Entries in wrong state", 429 assertTrue("Entries in wrong state",
406 - validateState(FlowEntryState.PENDING_REMOVE, 430 + validateState(ImmutableMap.of(
407 - FlowEntryState.PENDING_ADD)); 431 + f2, FlowEntryState.PENDING_REMOVE,
408 - 432 + f1, FlowEntryState.PENDING_ADD)));
409 433
410 434
411 } 435 }
......
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; 3 +import static org.hamcrest.CoreMatchers.not;
4 +import static org.hamcrest.MatcherAssert.assertThat;
5 +import static org.hamcrest.Matchers.equalTo;
6 +import static org.hamcrest.Matchers.is;
7 +import static org.onlab.onos.net.NetTestTools.link;
8 +
4 import java.util.HashSet; 9 import java.util.HashSet;
5 -import java.util.List;
6 import java.util.Set; 10 import java.util.Set;
7 11
12 +import org.junit.Before;
8 import org.junit.Test; 13 import org.junit.Test;
14 +import org.onlab.onos.net.ConnectPoint;
15 +import org.onlab.onos.net.DeviceId;
9 import org.onlab.onos.net.Link; 16 import org.onlab.onos.net.Link;
17 +import org.onlab.onos.net.PortNumber;
10 import org.onlab.onos.net.flow.TrafficSelector; 18 import org.onlab.onos.net.flow.TrafficSelector;
11 import org.onlab.onos.net.flow.TrafficTreatment; 19 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 -
15 -import static org.hamcrest.MatcherAssert.assertThat;
16 -import static org.hamcrest.Matchers.is;
17 20
21 +/**
22 + * Unit tests for the LinkCollectionIntent class.
23 + */
18 public class TestLinkCollectionIntent { 24 public class TestLinkCollectionIntent {
19 25
20 - private static class MockSelector implements TrafficSelector { 26 + private Link link1 = link("dev1", 1, "dev2", 2);
21 - @Override 27 + private Link link2 = link("dev1", 1, "dev3", 2);
22 - public Set<Criterion> criteria() { 28 + private Link link3 = link("dev2", 1, "dev3", 2);
23 - return new HashSet<Criterion>(); 29 +
24 - } 30 + private Set<Link> links1;
31 + private Set<Link> links2;
32 +
33 + private ConnectPoint egress1 = new ConnectPoint(DeviceId.deviceId("dev1"),
34 + PortNumber.portNumber(3));
35 + private ConnectPoint egress2 = new ConnectPoint(DeviceId.deviceId("dev2"),
36 + PortNumber.portNumber(3));
37 +
38 + private TrafficSelector selector = new IntentTestsMocks.MockSelector();
39 + private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment();
40 +
41 + private LinkCollectionIntent makeLinkCollection(long id, Set<Link> links,
42 + ConnectPoint egress) {
43 + return new LinkCollectionIntent(new IntentId(id),
44 + selector, treatment, links, egress);
45 + }
46 +
47 + @Before
48 + public void setup() {
49 + links1 = new HashSet<>();
50 + links2 = new HashSet<>();
51 + }
52 +
53 + /**
54 + * Tests the equals() method where two LinkCollectionIntents have references
55 + * to the same Links in different orders. These should compare equal.
56 + */
57 + @Test
58 + public void testSameEquals() {
59 + links1.add(link1);
60 + links1.add(link2);
61 + links1.add(link3);
62 +
63 + links2.add(link3);
64 + links2.add(link2);
65 + links2.add(link1);
66 +
67 + LinkCollectionIntent i1 = makeLinkCollection(12, links1, egress1);
68 + LinkCollectionIntent i2 = makeLinkCollection(12, links2, egress1);
69 +
70 + assertThat(i1, is(equalTo(i2)));
71 + }
72 +
73 + /**
74 + * Tests the equals() method where two LinkCollectionIntents have references
75 + * to different Links. These should compare not equal.
76 + */
77 + @Test
78 + public void testLinksDifferentEquals() {
79 + links1.add(link1);
80 + links1.add(link2);
81 +
82 + links2.add(link3);
83 + links2.add(link1);
84 +
85 + LinkCollectionIntent i1 = makeLinkCollection(12, links1, egress1);
86 + LinkCollectionIntent i2 = makeLinkCollection(12, links2, egress1);
87 +
88 + assertThat(i1, is(not(equalTo(i2))));
89 + }
90 +
91 + /**
92 + * Tests the equals() method where two LinkCollectionIntents have references
93 + * to the same Links but different egress points. These should compare not equal.
94 + */
95 + @Test
96 + public void testEgressDifferentEquals() {
97 + links1.add(link1);
98 + links1.add(link2);
99 + links1.add(link3);
100 +
101 + links2.add(link3);
102 + links2.add(link2);
103 + links2.add(link1);
104 +
105 + LinkCollectionIntent i1 = makeLinkCollection(12, links1, egress1);
106 + LinkCollectionIntent i2 = makeLinkCollection(12, links2, egress2);
107 +
108 + assertThat(i1, is(not(equalTo(i2))));
109 + }
110 +
111 + /**
112 + * Tests the equals() method where two LinkCollectionIntents have different
113 + * ids. These should compare not equal.
114 + */
115 + @Test
116 + public void testBaseDifferentEquals() {
117 + links1.add(link1);
118 + links1.add(link2);
119 +
120 + links2.add(link2);
121 + links2.add(link1);
122 +
123 + LinkCollectionIntent i1 = makeLinkCollection(1, links1, egress1);
124 + LinkCollectionIntent i2 = makeLinkCollection(2, links2, egress1);
125 +
126 + assertThat(i1, is(not(equalTo(i2))));
25 } 127 }
26 128
27 - private static class MockTreatment implements TrafficTreatment { 129 + /**
28 - @Override 130 + * Tests that the hashCode() values for two equivalent LinkCollectionIntent
29 - public List<Instruction> instructions() { 131 + * objects are the same.
30 - return new ArrayList<>(); 132 + */
31 - } 133 + @Test
134 + public void testHashCodeEquals() {
135 + links1.add(link1);
136 + links1.add(link2);
137 + links1.add(link3);
138 +
139 + links2.add(link3);
140 + links2.add(link2);
141 + links2.add(link1);
142 +
143 + LinkCollectionIntent i1 = makeLinkCollection(1, links1, egress1);
144 + LinkCollectionIntent i2 = makeLinkCollection(1, links2, egress1);
145 +
146 + assertThat(i1.hashCode(), is(equalTo(i2.hashCode())));
32 } 147 }
33 148
149 + /**
150 + * Tests that the hashCode() values for two distinct LinkCollectionIntent
151 + * objects are different.
152 + */
34 @Test 153 @Test
35 - public void testComparison() { 154 + public void testHashCodeDifferent() {
36 - TrafficSelector selector = new MockSelector(); 155 + links1.add(link1);
37 - TrafficTreatment treatment = new MockTreatment(); 156 + links1.add(link2);
38 - Set<Link> links = new HashSet<>(); 157 +
39 - LinkCollectionIntent i1 = new LinkCollectionIntent(new IntentId(12), 158 + links2.add(link1);
40 - selector, treatment, links); 159 + links2.add(link3);
41 - LinkCollectionIntent i2 = new LinkCollectionIntent(new IntentId(12), 160 +
42 - selector, treatment, links); 161 + LinkCollectionIntent i1 = makeLinkCollection(1, links1, egress1);
162 + LinkCollectionIntent i2 = makeLinkCollection(1, links2, egress2);
43 163
44 - assertThat(i1.equals(i2), is(true)); 164 + assertThat(i1.hashCode(), is(not(equalTo(i2.hashCode()))));
45 } 165 }
46 166
167 + /**
168 + * Checks that the HostToHostIntent class is immutable.
169 + */
170 + @Test
171 + public void checkImmutability() {
172 + ImmutableClassChecker.assertThatClassIsImmutable(LinkCollectionIntent.class);
173 + }
47 } 174 }
......
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;
2 +
3 +import org.junit.Test;
4 +import org.onlab.onos.net.ConnectPoint;
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.connectPoint;
13 +
14 +/**
15 + * Unit tests for the HostToHostIntent class.
16 + */
17 +public class TestPointToPointIntent {
18 +
19 + private TrafficSelector selector = new IntentTestsMocks.MockSelector();
20 + private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment();
21 +
22 + private ConnectPoint point1 = connectPoint("dev1", 1);
23 + private ConnectPoint point2 = connectPoint("dev2", 1);
24 +
25 + private PointToPointIntent makePointToPoint(long id,
26 + ConnectPoint ingress,
27 + ConnectPoint egress) {
28 + return new PointToPointIntent(new IntentId(id),
29 + selector,
30 + treatment,
31 + ingress,
32 + egress);
33 + }
34 +
35 + /**
36 + * Tests the equals() method where two PointToPointIntents have references
37 + * to the same ingress and egress points. These should compare equal.
38 + */
39 + @Test
40 + public void testSameEquals() {
41 + PointToPointIntent i1 = makePointToPoint(12, point1, point2);
42 + PointToPointIntent i2 = makePointToPoint(12, point1, point2);
43 +
44 + assertThat(i1, is(equalTo(i2)));
45 + }
46 +
47 + /**
48 + * Tests the equals() method where two HostToHostIntents have references
49 + * to different Hosts. These should compare not equal.
50 + */
51 + @Test
52 + public void testLinksDifferentEquals() {
53 +
54 + PointToPointIntent i1 = makePointToPoint(12, point1, point2);
55 + PointToPointIntent i2 = makePointToPoint(12, point2, point1);
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 + @Test
65 + public void testBaseDifferentEquals() {
66 + PointToPointIntent i1 = makePointToPoint(12, point1, point2);
67 + PointToPointIntent i2 = makePointToPoint(11, point1, point2);
68 +
69 +
70 + assertThat(i1, is(not(equalTo(i2))));
71 + }
72 +
73 + /**
74 + * Tests that the hashCode() values for two equivalent HostToHostIntent
75 + * objects are the same.
76 + */
77 + @Test
78 + public void testHashCodeEquals() {
79 + PointToPointIntent i1 = makePointToPoint(12, point1, point2);
80 + PointToPointIntent i2 = makePointToPoint(12, point1, point2);
81 +
82 + assertThat(i1.hashCode(), is(equalTo(i2.hashCode())));
83 + }
84 +
85 + /**
86 + * Tests that the hashCode() values for two distinct LinkCollectionIntent
87 + * objects are different.
88 + */
89 + @Test
90 + public void testHashCodeDifferent() {
91 + PointToPointIntent i1 = makePointToPoint(12, point1, point2);
92 + PointToPointIntent i2 = makePointToPoint(22, point1, point2);
93 +
94 + assertThat(i1.hashCode(), is(not(equalTo(i2.hashCode()))));
95 + }
96 +}
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 +}
1 +package org.onlab.onos.net.intent.impl;
2 +
3 +import java.util.List;
4 +
5 +import org.hamcrest.Matchers;
6 +import org.junit.Test;
7 +import org.onlab.onos.net.flow.TrafficSelector;
8 +import org.onlab.onos.net.flow.TrafficTreatment;
9 +import org.onlab.onos.net.intent.Intent;
10 +import org.onlab.onos.net.intent.IntentId;
11 +import org.onlab.onos.net.intent.IntentTestsMocks;
12 +import org.onlab.onos.net.intent.PathIntent;
13 +import org.onlab.onos.net.intent.PointToPointIntent;
14 +
15 +import static org.hamcrest.CoreMatchers.notNullValue;
16 +import static org.hamcrest.MatcherAssert.assertThat;
17 +import static org.hamcrest.Matchers.hasSize;
18 +import static org.hamcrest.Matchers.is;
19 +import static org.onlab.onos.net.NetTestTools.connectPoint;
20 +import static org.onlab.onos.net.intent.LinksHaveEntryWithSourceDestinationPairMatcher.linksHasPath;
21 +
22 +/**
23 + * Unit tests for the HostToHost intent compiler.
24 + */
25 +public class TestPointToPointIntentCompiler {
26 +
27 + private TrafficSelector selector = new IntentTestsMocks.MockSelector();
28 + private TrafficTreatment treatment = new IntentTestsMocks.MockTreatment();
29 +
30 + /**
31 + * Creates a PointToPoint intent based on ingress and egress device Ids.
32 + *
33 + * @param ingressIdString string for id of ingress device
34 + * @param egressIdString string for id of egress device
35 + * @return PointToPointIntent for the two devices
36 + */
37 + private PointToPointIntent makeIntent(String ingressIdString,
38 + String egressIdString) {
39 + return new PointToPointIntent(new IntentId(12),
40 + selector,
41 + treatment,
42 + connectPoint(ingressIdString, 1),
43 + connectPoint(egressIdString, 1));
44 + }
45 +
46 + /**
47 + * Creates a compiler for HostToHost intents.
48 + *
49 + * @param hops string array describing the path hops to use when compiling
50 + * @return HostToHost intent compiler
51 + */
52 + private PointToPointIntentCompiler makeCompiler(String[] hops) {
53 + PointToPointIntentCompiler compiler =
54 + new PointToPointIntentCompiler();
55 + compiler.pathService = new IntentTestsMocks.MockPathService(hops);
56 + IdBlockAllocator idBlockAllocator = new DummyIdBlockAllocator();
57 + compiler.intentIdGenerator =
58 + new IdBlockAllocatorBasedIntentIdGenerator(idBlockAllocator);
59 + return compiler;
60 + }
61 +
62 +
63 + /**
64 + * Tests a pair of devices in an 8 hop path, forward direction.
65 + */
66 + @Test
67 + public void testForwardPathCompilation() {
68 +
69 + PointToPointIntent intent = makeIntent("d1", "d8");
70 + assertThat(intent, is(notNullValue()));
71 +
72 + String[] hops = {"d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8"};
73 + PointToPointIntentCompiler compiler = makeCompiler(hops);
74 + assertThat(compiler, is(notNullValue()));
75 +
76 + List<Intent> result = compiler.compile(intent);
77 + assertThat(result, is(Matchers.notNullValue()));
78 + assertThat(result, hasSize(1));
79 + Intent forwardResultIntent = result.get(0);
80 + assertThat(forwardResultIntent instanceof PathIntent, is(true));
81 +
82 + if (forwardResultIntent instanceof PathIntent) {
83 + PathIntent forwardPathIntent = (PathIntent) forwardResultIntent;
84 + // 7 links for the hops, plus one default lnk on ingress and egress
85 + assertThat(forwardPathIntent.path().links(), hasSize(hops.length + 1));
86 + assertThat(forwardPathIntent.path().links(), linksHasPath("d1", "d2"));
87 + assertThat(forwardPathIntent.path().links(), linksHasPath("d2", "d3"));
88 + assertThat(forwardPathIntent.path().links(), linksHasPath("d3", "d4"));
89 + assertThat(forwardPathIntent.path().links(), linksHasPath("d4", "d5"));
90 + assertThat(forwardPathIntent.path().links(), linksHasPath("d5", "d6"));
91 + assertThat(forwardPathIntent.path().links(), linksHasPath("d6", "d7"));
92 + assertThat(forwardPathIntent.path().links(), linksHasPath("d7", "d8"));
93 + }
94 + }
95 +
96 + /**
97 + * Tests a pair of devices in an 8 hop path, forward direction.
98 + */
99 + @Test
100 + public void testReversePathCompilation() {
101 +
102 + PointToPointIntent intent = makeIntent("d8", "d1");
103 + assertThat(intent, is(notNullValue()));
104 +
105 + String[] hops = {"d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8"};
106 + PointToPointIntentCompiler compiler = makeCompiler(hops);
107 + assertThat(compiler, is(notNullValue()));
108 +
109 + List<Intent> result = compiler.compile(intent);
110 + assertThat(result, is(Matchers.notNullValue()));
111 + assertThat(result, hasSize(1));
112 + Intent reverseResultIntent = result.get(0);
113 + assertThat(reverseResultIntent instanceof PathIntent, is(true));
114 +
115 + if (reverseResultIntent instanceof PathIntent) {
116 + PathIntent reversePathIntent = (PathIntent) reverseResultIntent;
117 + assertThat(reversePathIntent.path().links(), hasSize(hops.length + 1));
118 + assertThat(reversePathIntent.path().links(), linksHasPath("d2", "d1"));
119 + assertThat(reversePathIntent.path().links(), linksHasPath("d3", "d2"));
120 + assertThat(reversePathIntent.path().links(), linksHasPath("d4", "d3"));
121 + assertThat(reversePathIntent.path().links(), linksHasPath("d5", "d4"));
122 + assertThat(reversePathIntent.path().links(), linksHasPath("d6", "d5"));
123 + assertThat(reversePathIntent.path().links(), linksHasPath("d7", "d6"));
124 + assertThat(reversePathIntent.path().links(), linksHasPath("d8", "d7"));
125 + }
126 + }
127 +}
...@@ -59,6 +59,7 @@ public class LinkManagerTest { ...@@ -59,6 +59,7 @@ public class LinkManagerTest {
59 protected LinkProviderService providerService; 59 protected LinkProviderService providerService;
60 protected TestProvider provider; 60 protected TestProvider provider;
61 protected TestListener listener = new TestListener(); 61 protected TestListener listener = new TestListener();
62 + protected DeviceManager devmgr = new TestDeviceManager();
62 63
63 @Before 64 @Before
64 public void setUp() { 65 public void setUp() {
...@@ -68,7 +69,7 @@ public class LinkManagerTest { ...@@ -68,7 +69,7 @@ public class LinkManagerTest {
68 registry = mgr; 69 registry = mgr;
69 mgr.store = new SimpleLinkStore(); 70 mgr.store = new SimpleLinkStore();
70 mgr.eventDispatcher = new TestEventDispatcher(); 71 mgr.eventDispatcher = new TestEventDispatcher();
71 - mgr.deviceService = new DeviceManager(); 72 + mgr.deviceService = devmgr;
72 mgr.activate(); 73 mgr.activate();
73 74
74 service.addListener(listener); 75 service.addListener(listener);
...@@ -259,4 +260,11 @@ public class LinkManagerTest { ...@@ -259,4 +260,11 @@ public class LinkManagerTest {
259 } 260 }
260 } 261 }
261 262
263 + private static class TestDeviceManager extends DeviceManager {
264 + @Override
265 + public MastershipRole getRole(DeviceId deviceId) {
266 + return MastershipRole.MASTER;
267 + }
268 + }
269 +
262 } 270 }
......
...@@ -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>
...@@ -67,15 +52,12 @@ ...@@ -67,15 +52,12 @@
67 <artifactId>easymock</artifactId> 52 <artifactId>easymock</artifactId>
68 <scope>test</scope> 53 <scope>test</scope>
69 </dependency> 54 </dependency>
55 + <dependency>
56 + <groupId>org.onlab.onos</groupId>
57 + <artifactId>onos-api</artifactId>
58 + <classifier>tests</classifier>
59 + <scope>test</scope>
60 + </dependency>
70 </dependencies> 61 </dependencies>
71 62
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> 63 </project>
......
...@@ -53,7 +53,7 @@ public class DistributedClusterStore ...@@ -53,7 +53,7 @@ public class DistributedClusterStore
53 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 53 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
54 private ClusterCommunicationAdminService clusterCommunicationAdminService; 54 private ClusterCommunicationAdminService clusterCommunicationAdminService;
55 55
56 - private final ClusterNodesDelegate nodesDelegate = new InnerNodesDelegate(); 56 + private final ClusterNodesDelegate nodesDelegate = new InternalNodesDelegate();
57 57
58 @Activate 58 @Activate
59 public void activate() throws IOException { 59 public void activate() throws IOException {
...@@ -150,7 +150,7 @@ public class DistributedClusterStore ...@@ -150,7 +150,7 @@ public class DistributedClusterStore
150 } 150 }
151 151
152 // Entity to handle back calls from the connection manager. 152 // Entity to handle back calls from the connection manager.
153 - private class InnerNodesDelegate implements ClusterNodesDelegate { 153 + private class InternalNodesDelegate implements ClusterNodesDelegate {
154 @Override 154 @Override
155 public DefaultControllerNode nodeDetected(NodeId nodeId, IpPrefix ip, int tcpPort) { 155 public DefaultControllerNode nodeDetected(NodeId nodeId, IpPrefix ip, int tcpPort) {
156 DefaultControllerNode node = nodes.get(nodeId); 156 DefaultControllerNode node = nodes.get(nodeId);
......
...@@ -4,6 +4,9 @@ import static com.google.common.base.Preconditions.checkArgument; ...@@ -4,6 +4,9 @@ import static com.google.common.base.Preconditions.checkArgument;
4 4
5 import java.io.IOException; 5 import java.io.IOException;
6 import java.util.Set; 6 import java.util.Set;
7 +import java.util.concurrent.TimeUnit;
8 +import java.util.concurrent.TimeoutException;
9 +
7 import org.apache.felix.scr.annotations.Activate; 10 import org.apache.felix.scr.annotations.Activate;
8 import org.apache.felix.scr.annotations.Component; 11 import org.apache.felix.scr.annotations.Component;
9 import org.apache.felix.scr.annotations.Deactivate; 12 import org.apache.felix.scr.annotations.Deactivate;
...@@ -17,17 +20,19 @@ import org.onlab.onos.store.cluster.impl.ClusterMembershipEvent; ...@@ -17,17 +20,19 @@ import org.onlab.onos.store.cluster.impl.ClusterMembershipEvent;
17 import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; 20 import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
18 import org.onlab.onos.store.cluster.messaging.ClusterMessage; 21 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
19 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler; 22 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
23 +import org.onlab.onos.store.cluster.messaging.ClusterMessageResponse;
20 import org.onlab.onos.store.cluster.messaging.MessageSubject; 24 import org.onlab.onos.store.cluster.messaging.MessageSubject;
21 import org.onlab.onos.store.serializers.ClusterMessageSerializer; 25 import org.onlab.onos.store.serializers.ClusterMessageSerializer;
22 -import org.onlab.onos.store.serializers.KryoPoolUtil; 26 +import org.onlab.onos.store.serializers.KryoNamespaces;
23 import org.onlab.onos.store.serializers.KryoSerializer; 27 import org.onlab.onos.store.serializers.KryoSerializer;
24 import org.onlab.onos.store.serializers.MessageSubjectSerializer; 28 import org.onlab.onos.store.serializers.MessageSubjectSerializer;
25 -import org.onlab.util.KryoPool; 29 +import org.onlab.util.KryoNamespace;
26 import org.onlab.netty.Endpoint; 30 import org.onlab.netty.Endpoint;
27 import org.onlab.netty.Message; 31 import org.onlab.netty.Message;
28 import org.onlab.netty.MessageHandler; 32 import org.onlab.netty.MessageHandler;
29 import org.onlab.netty.MessagingService; 33 import org.onlab.netty.MessagingService;
30 import org.onlab.netty.NettyMessagingService; 34 import org.onlab.netty.NettyMessagingService;
35 +import org.onlab.netty.Response;
31 import org.slf4j.Logger; 36 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory; 37 import org.slf4j.LoggerFactory;
33 38
...@@ -47,8 +52,8 @@ public class ClusterCommunicationManager ...@@ -47,8 +52,8 @@ public class ClusterCommunicationManager
47 private static final KryoSerializer SERIALIZER = new KryoSerializer() { 52 private static final KryoSerializer SERIALIZER = new KryoSerializer() {
48 @Override 53 @Override
49 protected void setupKryoPool() { 54 protected void setupKryoPool() {
50 - serializerPool = KryoPool.newBuilder() 55 + serializerPool = KryoNamespace.newBuilder()
51 - .register(KryoPoolUtil.API) 56 + .register(KryoNamespaces.API)
52 .register(ClusterMessage.class, new ClusterMessageSerializer()) 57 .register(ClusterMessage.class, new ClusterMessageSerializer())
53 .register(ClusterMembershipEvent.class) 58 .register(ClusterMembershipEvent.class)
54 .register(byte[].class) 59 .register(byte[].class)
...@@ -114,7 +119,23 @@ public class ClusterCommunicationManager ...@@ -114,7 +119,23 @@ public class ClusterCommunicationManager
114 message.subject().value(), SERIALIZER.encode(message)); 119 message.subject().value(), SERIALIZER.encode(message));
115 return true; 120 return true;
116 } catch (IOException e) { 121 } catch (IOException e) {
117 - log.error("Failed to send cluster message to nodeId: " + toNodeId, e); 122 + log.trace("Failed to send cluster message to nodeId: " + toNodeId, e);
123 + throw e;
124 + }
125 + }
126 +
127 + @Override
128 + public ClusterMessageResponse sendAndReceive(ClusterMessage message, NodeId toNodeId) throws IOException {
129 + ControllerNode node = clusterService.getNode(toNodeId);
130 + checkArgument(node != null, "Unknown nodeId: %s", toNodeId);
131 + Endpoint nodeEp = new Endpoint(node.ip().toString(), node.tcpPort());
132 + try {
133 + Response responseFuture =
134 + messagingService.sendAndReceive(nodeEp, message.subject().value(), SERIALIZER.encode(message));
135 + return new InternalClusterMessageResponse(toNodeId, responseFuture);
136 +
137 + } catch (IOException e) {
138 + log.error("Failed interaction with remote nodeId: " + toNodeId, e);
118 throw e; 139 throw e;
119 } 140 }
120 } 141 }
...@@ -137,11 +158,52 @@ public class ClusterCommunicationManager ...@@ -137,11 +158,52 @@ public class ClusterCommunicationManager
137 public void handle(Message message) { 158 public void handle(Message message) {
138 try { 159 try {
139 ClusterMessage clusterMessage = SERIALIZER.decode(message.payload()); 160 ClusterMessage clusterMessage = SERIALIZER.decode(message.payload());
140 - handler.handle(clusterMessage); 161 + handler.handle(new InternalClusterMessage(clusterMessage, message));
141 } catch (Exception e) { 162 } catch (Exception e) {
142 log.error("Exception caught during ClusterMessageHandler", e); 163 log.error("Exception caught during ClusterMessageHandler", e);
143 throw e; 164 throw e;
144 } 165 }
145 } 166 }
146 } 167 }
168 +
169 + public static final class InternalClusterMessage extends ClusterMessage {
170 +
171 + private final Message rawMessage;
172 +
173 + public InternalClusterMessage(ClusterMessage clusterMessage, Message rawMessage) {
174 + super(clusterMessage.sender(), clusterMessage.subject(), clusterMessage.payload());
175 + this.rawMessage = rawMessage;
176 + }
177 +
178 + @Override
179 + public void respond(byte[] response) throws IOException {
180 + rawMessage.respond(response);
181 + }
182 + }
183 +
184 + private static final class InternalClusterMessageResponse implements ClusterMessageResponse {
185 +
186 + private final NodeId sender;
187 + private final Response responseFuture;
188 +
189 + public InternalClusterMessageResponse(NodeId sender, Response responseFuture) {
190 + this.sender = sender;
191 + this.responseFuture = responseFuture;
192 + }
193 + @Override
194 + public NodeId sender() {
195 + return sender;
196 + }
197 +
198 + @Override
199 + public byte[] get(long timeout, TimeUnit timeunit)
200 + throws TimeoutException {
201 + return responseFuture.get(timeout, timeunit);
202 + }
203 +
204 + @Override
205 + public byte[] get(long timeout) throws InterruptedException {
206 + return responseFuture.get();
207 + }
208 + }
147 } 209 }
......
1 -/**
2 - * Common abstractions and facilities for implementing distributed store
3 - * using gossip protocol.
4 - */
5 -package org.onlab.onos.store.common.impl;
...@@ -44,6 +44,8 @@ public class DeviceClockManager implements DeviceClockService, DeviceClockProvid ...@@ -44,6 +44,8 @@ public class DeviceClockManager implements DeviceClockService, DeviceClockProvid
44 @Override 44 @Override
45 public Timestamp getTimestamp(DeviceId deviceId) { 45 public Timestamp getTimestamp(DeviceId deviceId) {
46 MastershipTerm term = deviceMastershipTerms.get(deviceId); 46 MastershipTerm term = deviceMastershipTerms.get(deviceId);
47 + log.trace("term info for {} is: {}", deviceId, term);
48 +
47 if (term == null) { 49 if (term == null) {
48 throw new IllegalStateException("Requesting timestamp for a deviceId without mastership"); 50 throw new IllegalStateException("Requesting timestamp for a deviceId without mastership");
49 } 51 }
...@@ -52,6 +54,7 @@ public class DeviceClockManager implements DeviceClockService, DeviceClockProvid ...@@ -52,6 +54,7 @@ public class DeviceClockManager implements DeviceClockService, DeviceClockProvid
52 54
53 @Override 55 @Override
54 public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) { 56 public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
57 + log.info("adding term info {} {}", deviceId, term.master());
55 deviceMastershipTerms.put(deviceId, term); 58 deviceMastershipTerms.put(deviceId, term);
56 } 59 }
57 } 60 }
......
...@@ -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,10 +38,11 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; ...@@ -38,10 +38,11 @@ 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.util.KryoPool; 44 +import org.onlab.packet.ChassisId;
45 +import org.onlab.util.KryoNamespace;
45 import org.onlab.util.NewConcurrentHashMap; 46 import org.onlab.util.NewConcurrentHashMap;
46 import org.slf4j.Logger; 47 import org.slf4j.Logger;
47 48
...@@ -116,7 +117,7 @@ public class GossipDeviceStore ...@@ -116,7 +117,7 @@ public class GossipDeviceStore
116 protected static final KryoSerializer SERIALIZER = new KryoSerializer() { 117 protected static final KryoSerializer SERIALIZER = new KryoSerializer() {
117 @Override 118 @Override
118 protected void setupKryoPool() { 119 protected void setupKryoPool() {
119 - serializerPool = KryoPool.newBuilder() 120 + serializerPool = KryoNamespace.newBuilder()
120 .register(DistributedStoreSerializers.COMMON) 121 .register(DistributedStoreSerializers.COMMON)
121 122
122 .register(InternalDeviceEvent.class, new InternalDeviceEventSerializer()) 123 .register(InternalDeviceEvent.class, new InternalDeviceEventSerializer())
...@@ -390,6 +391,7 @@ public class GossipDeviceStore ...@@ -390,6 +391,7 @@ public class GossipDeviceStore
390 List<PortDescription> portDescriptions) { 391 List<PortDescription> portDescriptions) {
391 392
392 final Timestamp newTimestamp = deviceClockService.getTimestamp(deviceId); 393 final Timestamp newTimestamp = deviceClockService.getTimestamp(deviceId);
394 + log.info("timestamp for {} {}", deviceId, newTimestamp);
393 395
394 final Timestamped<List<PortDescription>> timestampedInput 396 final Timestamped<List<PortDescription>> timestampedInput
395 = new Timestamped<>(portDescriptions, newTimestamp); 397 = new Timestamped<>(portDescriptions, newTimestamp);
...@@ -515,12 +517,12 @@ public class GossipDeviceStore ...@@ -515,12 +517,12 @@ public class GossipDeviceStore
515 Map<PortNumber, Port> ports, 517 Map<PortNumber, Port> ports,
516 Set<PortNumber> processed) { 518 Set<PortNumber> processed) {
517 List<DeviceEvent> events = new ArrayList<>(); 519 List<DeviceEvent> events = new ArrayList<>();
518 - Iterator<PortNumber> iterator = ports.keySet().iterator(); 520 + Iterator<Entry<PortNumber, Port>> iterator = ports.entrySet().iterator();
519 while (iterator.hasNext()) { 521 while (iterator.hasNext()) {
520 - PortNumber portNumber = iterator.next(); 522 + Entry<PortNumber, Port> e = iterator.next();
523 + PortNumber portNumber = e.getKey();
521 if (!processed.contains(portNumber)) { 524 if (!processed.contains(portNumber)) {
522 - events.add(new DeviceEvent(PORT_REMOVED, device, 525 + events.add(new DeviceEvent(PORT_REMOVED, device, e.getValue()));
523 - ports.get(portNumber)));
524 iterator.remove(); 526 iterator.remove();
525 } 527 }
526 } 528 }
...@@ -745,6 +747,7 @@ public class GossipDeviceStore ...@@ -745,6 +747,7 @@ public class GossipDeviceStore
745 String hwVersion = base.hwVersion(); 747 String hwVersion = base.hwVersion();
746 String swVersion = base.swVersion(); 748 String swVersion = base.swVersion();
747 String serialNumber = base.serialNumber(); 749 String serialNumber = base.serialNumber();
750 + ChassisId chassisId = base.chassisId();
748 DefaultAnnotations annotations = DefaultAnnotations.builder().build(); 751 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
749 annotations = merge(annotations, base.annotations()); 752 annotations = merge(annotations, base.annotations());
750 753
...@@ -762,7 +765,8 @@ public class GossipDeviceStore ...@@ -762,7 +765,8 @@ public class GossipDeviceStore
762 } 765 }
763 766
764 return new DefaultDevice(primary, deviceId , type, manufacturer, 767 return new DefaultDevice(primary, deviceId , type, manufacturer,
765 - hwVersion, swVersion, serialNumber, annotations); 768 + hwVersion, swVersion, serialNumber,
769 + chassisId, annotations);
766 } 770 }
767 771
768 /** 772 /**
...@@ -1136,7 +1140,7 @@ public class GossipDeviceStore ...@@ -1136,7 +1140,7 @@ public class GossipDeviceStore
1136 try { 1140 try {
1137 unicastMessage(peer, DEVICE_ADVERTISE, ad); 1141 unicastMessage(peer, DEVICE_ADVERTISE, ad);
1138 } catch (IOException e) { 1142 } catch (IOException e) {
1139 - log.error("Failed to send anti-entropy advertisement", e); 1143 + log.debug("Failed to send anti-entropy advertisement to {}", peer);
1140 return; 1144 return;
1141 } 1145 }
1142 } catch (Exception e) { 1146 } 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 +
19 + /**
20 + * Adds the specified replica placement info change listener.
21 + *
22 + * @param listener the replica placement info change listener
23 + */
24 + void addListener(ReplicaInfoEventListener listener);
25 +
26 + /**
27 + * Removes the specified replica placement info change listener.
28 + *
29 + * @param listener the replica placement info change listener
30 + */
31 + void removeListener(ReplicaInfoEventListener listener);
32 +
33 +}
...@@ -3,14 +3,20 @@ package org.onlab.onos.store.flow.impl; ...@@ -3,14 +3,20 @@ package org.onlab.onos.store.flow.impl;
3 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED; 3 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
4 import static org.slf4j.LoggerFactory.getLogger; 4 import static org.slf4j.LoggerFactory.getLogger;
5 5
6 +import java.io.IOException;
6 import java.util.Collection; 7 import java.util.Collection;
7 import java.util.Collections; 8 import java.util.Collections;
9 +import java.util.concurrent.TimeUnit;
10 +import java.util.concurrent.TimeoutException;
8 11
9 import org.apache.felix.scr.annotations.Activate; 12 import org.apache.felix.scr.annotations.Activate;
10 import org.apache.felix.scr.annotations.Component; 13 import org.apache.felix.scr.annotations.Component;
11 import org.apache.felix.scr.annotations.Deactivate; 14 import org.apache.felix.scr.annotations.Deactivate;
15 +import org.apache.felix.scr.annotations.Reference;
16 +import org.apache.felix.scr.annotations.ReferenceCardinality;
12 import org.apache.felix.scr.annotations.Service; 17 import org.apache.felix.scr.annotations.Service;
13 import org.onlab.onos.ApplicationId; 18 import org.onlab.onos.ApplicationId;
19 +import org.onlab.onos.cluster.ClusterService;
14 import org.onlab.onos.net.DeviceId; 20 import org.onlab.onos.net.DeviceId;
15 import org.onlab.onos.net.flow.DefaultFlowEntry; 21 import org.onlab.onos.net.flow.DefaultFlowEntry;
16 import org.onlab.onos.net.flow.FlowEntry; 22 import org.onlab.onos.net.flow.FlowEntry;
...@@ -20,7 +26,16 @@ import org.onlab.onos.net.flow.FlowRuleEvent; ...@@ -20,7 +26,16 @@ import org.onlab.onos.net.flow.FlowRuleEvent;
20 import org.onlab.onos.net.flow.FlowRuleEvent.Type; 26 import org.onlab.onos.net.flow.FlowRuleEvent.Type;
21 import org.onlab.onos.net.flow.FlowRuleStore; 27 import org.onlab.onos.net.flow.FlowRuleStore;
22 import org.onlab.onos.net.flow.FlowRuleStoreDelegate; 28 import org.onlab.onos.net.flow.FlowRuleStoreDelegate;
29 +import org.onlab.onos.net.flow.StoredFlowEntry;
23 import org.onlab.onos.store.AbstractStore; 30 import org.onlab.onos.store.AbstractStore;
31 +import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
32 +import org.onlab.onos.store.cluster.messaging.ClusterMessage;
33 +import org.onlab.onos.store.cluster.messaging.ClusterMessageResponse;
34 +import org.onlab.onos.store.flow.ReplicaInfo;
35 +import org.onlab.onos.store.flow.ReplicaInfoService;
36 +import org.onlab.onos.store.serializers.DistributedStoreSerializers;
37 +import org.onlab.onos.store.serializers.KryoSerializer;
38 +import org.onlab.util.KryoNamespace;
24 import org.slf4j.Logger; 39 import org.slf4j.Logger;
25 40
26 import com.google.common.collect.ArrayListMultimap; 41 import com.google.common.collect.ArrayListMultimap;
...@@ -28,9 +43,8 @@ import com.google.common.collect.ImmutableSet; ...@@ -28,9 +43,8 @@ import com.google.common.collect.ImmutableSet;
28 import com.google.common.collect.Multimap; 43 import com.google.common.collect.Multimap;
29 44
30 /** 45 /**
31 - * Manages inventory of flow rules using trivial in-memory implementation. 46 + * Manages inventory of flow rules using a distributed state management protocol.
32 */ 47 */
33 -//FIXME I LIE. I AIN'T DISTRIBUTED
34 @Component(immediate = true) 48 @Component(immediate = true)
35 @Service 49 @Service
36 public class DistributedFlowRuleStore 50 public class DistributedFlowRuleStore
...@@ -40,12 +54,34 @@ public class DistributedFlowRuleStore ...@@ -40,12 +54,34 @@ public class DistributedFlowRuleStore
40 private final Logger log = getLogger(getClass()); 54 private final Logger log = getLogger(getClass());
41 55
42 // store entries as a pile of rules, no info about device tables 56 // store entries as a pile of rules, no info about device tables
43 - private final Multimap<DeviceId, FlowEntry> flowEntries = 57 + private final Multimap<DeviceId, StoredFlowEntry> flowEntries =
44 - ArrayListMultimap.<DeviceId, FlowEntry>create(); 58 + ArrayListMultimap.<DeviceId, StoredFlowEntry>create();
45 59
46 private final Multimap<Short, FlowRule> flowEntriesById = 60 private final Multimap<Short, FlowRule> flowEntriesById =
47 ArrayListMultimap.<Short, FlowRule>create(); 61 ArrayListMultimap.<Short, FlowRule>create();
48 62
63 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
64 + private ReplicaInfoService replicaInfoManager;
65 +
66 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
67 + private ClusterCommunicationService clusterCommunicator;
68 +
69 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
70 + private ClusterService clusterService;
71 +
72 + protected static final KryoSerializer SERIALIZER = new KryoSerializer() {
73 + @Override
74 + protected void setupKryoPool() {
75 + serializerPool = KryoNamespace.newBuilder()
76 + .register(DistributedStoreSerializers.COMMON)
77 + .build()
78 + .populate(1);
79 + }
80 + };
81 +
82 + // TODO: make this configurable
83 + private static final long FLOW_RULE_STORE_TIMEOUT_MILLIS = 1000;
84 +
49 @Activate 85 @Activate
50 public void activate() { 86 public void activate() {
51 log.info("Started"); 87 log.info("Started");
...@@ -64,7 +100,11 @@ public class DistributedFlowRuleStore ...@@ -64,7 +100,11 @@ public class DistributedFlowRuleStore
64 100
65 @Override 101 @Override
66 public synchronized FlowEntry getFlowEntry(FlowRule rule) { 102 public synchronized FlowEntry getFlowEntry(FlowRule rule) {
67 - for (FlowEntry f : flowEntries.get(rule.deviceId())) { 103 + return getFlowEntryInternal(rule);
104 + }
105 +
106 + private synchronized StoredFlowEntry getFlowEntryInternal(FlowRule rule) {
107 + for (StoredFlowEntry f : flowEntries.get(rule.deviceId())) {
68 if (f.equals(rule)) { 108 if (f.equals(rule)) {
69 return f; 109 return f;
70 } 110 }
...@@ -74,7 +114,7 @@ public class DistributedFlowRuleStore ...@@ -74,7 +114,7 @@ public class DistributedFlowRuleStore
74 114
75 @Override 115 @Override
76 public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) { 116 public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
77 - Collection<FlowEntry> rules = flowEntries.get(deviceId); 117 + Collection<? extends FlowEntry> rules = flowEntries.get(deviceId);
78 if (rules == null) { 118 if (rules == null) {
79 return Collections.emptyList(); 119 return Collections.emptyList();
80 } 120 }
...@@ -91,30 +131,96 @@ public class DistributedFlowRuleStore ...@@ -91,30 +131,96 @@ public class DistributedFlowRuleStore
91 } 131 }
92 132
93 @Override 133 @Override
94 - public synchronized void storeFlowRule(FlowRule rule) { 134 + public void storeFlowRule(FlowRule rule) {
95 - FlowEntry f = new DefaultFlowEntry(rule); 135 + ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(rule.deviceId());
96 - DeviceId did = f.deviceId(); 136 + if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
97 - if (!flowEntries.containsEntry(did, f)) { 137 + storeFlowEntryInternal(rule);
98 - flowEntries.put(did, f); 138 + return;
99 - flowEntriesById.put(rule.appId(), f); 139 + }
140 +
141 + ClusterMessage message = new ClusterMessage(
142 + clusterService.getLocalNode().id(),
143 + FlowStoreMessageSubjects.STORE_FLOW_RULE,
144 + SERIALIZER.encode(rule));
145 +
146 + try {
147 + ClusterMessageResponse response = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
148 + response.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
149 + } catch (IOException | TimeoutException e) {
150 + // FIXME: throw a FlowStoreException
151 + throw new RuntimeException(e);
100 } 152 }
101 } 153 }
102 154
155 + private synchronized void storeFlowEntryInternal(FlowRule flowRule) {
156 + StoredFlowEntry flowEntry = new DefaultFlowEntry(flowRule);
157 + DeviceId deviceId = flowRule.deviceId();
158 + // write to local copy.
159 + if (!flowEntries.containsEntry(deviceId, flowEntry)) {
160 + flowEntries.put(deviceId, flowEntry);
161 + flowEntriesById.put(flowRule.appId(), flowEntry);
162 + }
163 + // write to backup.
164 + // TODO: write to a hazelcast map.
165 + }
166 +
103 @Override 167 @Override
104 public synchronized void deleteFlowRule(FlowRule rule) { 168 public synchronized void deleteFlowRule(FlowRule rule) {
105 - FlowEntry entry = getFlowEntry(rule); 169 + ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(rule.deviceId());
170 + if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
171 + deleteFlowRuleInternal(rule);
172 + return;
173 + }
174 +
175 + ClusterMessage message = new ClusterMessage(
176 + clusterService.getLocalNode().id(),
177 + FlowStoreMessageSubjects.DELETE_FLOW_RULE,
178 + SERIALIZER.encode(rule));
179 +
180 + try {
181 + ClusterMessageResponse response = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
182 + response.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
183 + } catch (IOException | TimeoutException e) {
184 + // FIXME: throw a FlowStoreException
185 + throw new RuntimeException(e);
186 + }
187 + }
188 +
189 + private synchronized void deleteFlowRuleInternal(FlowRule flowRule) {
190 + StoredFlowEntry entry = getFlowEntryInternal(flowRule);
106 if (entry == null) { 191 if (entry == null) {
107 return; 192 return;
108 } 193 }
109 entry.setState(FlowEntryState.PENDING_REMOVE); 194 entry.setState(FlowEntryState.PENDING_REMOVE);
195 + // TODO: also update backup.
110 } 196 }
111 197
112 @Override 198 @Override
113 - public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) { 199 + public FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
200 + ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(rule.deviceId());
201 + if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
202 + return addOrUpdateFlowRuleInternal(rule);
203 + }
204 +
205 + ClusterMessage message = new ClusterMessage(
206 + clusterService.getLocalNode().id(),
207 + FlowStoreMessageSubjects.ADD_OR_UPDATE_FLOW_RULE,
208 + SERIALIZER.encode(rule));
209 +
210 + try {
211 + ClusterMessageResponse response = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
212 + return SERIALIZER.decode(response.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
213 + } catch (IOException | TimeoutException e) {
214 + // FIXME: throw a FlowStoreException
215 + throw new RuntimeException(e);
216 + }
217 + }
218 +
219 + private synchronized FlowRuleEvent addOrUpdateFlowRuleInternal(FlowEntry rule) {
114 DeviceId did = rule.deviceId(); 220 DeviceId did = rule.deviceId();
115 221
116 // check if this new rule is an update to an existing entry 222 // check if this new rule is an update to an existing entry
117 - FlowEntry stored = getFlowEntry(rule); 223 + StoredFlowEntry stored = getFlowEntryInternal(rule);
118 if (stored != null) { 224 if (stored != null) {
119 stored.setBytes(rule.bytes()); 225 stored.setBytes(rule.bytes());
120 stored.setLife(rule.life()); 226 stored.setLife(rule.life());
...@@ -126,17 +232,42 @@ public class DistributedFlowRuleStore ...@@ -126,17 +232,42 @@ public class DistributedFlowRuleStore
126 return new FlowRuleEvent(Type.RULE_UPDATED, rule); 232 return new FlowRuleEvent(Type.RULE_UPDATED, rule);
127 } 233 }
128 234
129 - flowEntries.put(did, rule); 235 + // TODO: Confirm if this behavior is correct. See SimpleFlowRuleStore
236 + flowEntries.put(did, new DefaultFlowEntry(rule));
130 return null; 237 return null;
238 +
239 + // TODO: also update backup.
131 } 240 }
132 241
133 @Override 242 @Override
134 - public synchronized FlowRuleEvent removeFlowRule(FlowEntry rule) { 243 + public FlowRuleEvent removeFlowRule(FlowEntry rule) {
244 + ReplicaInfo replicaInfo = replicaInfoManager.getReplicaInfoFor(rule.deviceId());
245 + if (replicaInfo.master().get().equals(clusterService.getLocalNode().id())) {
246 + // bypass and handle it locally
247 + return removeFlowRuleInternal(rule);
248 + }
249 +
250 + ClusterMessage message = new ClusterMessage(
251 + clusterService.getLocalNode().id(),
252 + FlowStoreMessageSubjects.REMOVE_FLOW_RULE,
253 + SERIALIZER.encode(rule));
254 +
255 + try {
256 + ClusterMessageResponse response = clusterCommunicator.sendAndReceive(message, replicaInfo.master().get());
257 + return SERIALIZER.decode(response.get(FLOW_RULE_STORE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
258 + } catch (IOException | TimeoutException e) {
259 + // FIXME: throw a FlowStoreException
260 + throw new RuntimeException(e);
261 + }
262 + }
263 +
264 + private synchronized FlowRuleEvent removeFlowRuleInternal(FlowEntry rule) {
135 // This is where one could mark a rule as removed and still keep it in the store. 265 // This is where one could mark a rule as removed and still keep it in the store.
136 if (flowEntries.remove(rule.deviceId(), rule)) { 266 if (flowEntries.remove(rule.deviceId(), rule)) {
137 return new FlowRuleEvent(RULE_REMOVED, rule); 267 return new FlowRuleEvent(RULE_REMOVED, rule);
138 } else { 268 } else {
139 return null; 269 return null;
140 } 270 }
271 + // TODO: also update backup.
141 } 272 }
142 } 273 }
......
1 +package org.onlab.onos.store.flow.impl;
2 +
3 +import org.onlab.onos.store.cluster.messaging.MessageSubject;
4 +
5 +/**
6 + * MessageSubjects used by DistributedFlowRuleStore peer-peer communication.
7 + */
8 +public final class FlowStoreMessageSubjects {
9 + private FlowStoreMessageSubjects() {}
10 + public static final MessageSubject STORE_FLOW_RULE = new MessageSubject("peer-forward-store-flow-rule");
11 + public static final MessageSubject DELETE_FLOW_RULE = new MessageSubject("peer-forward-delete-flow-rule");
12 + public static final MessageSubject ADD_OR_UPDATE_FLOW_RULE =
13 + new MessageSubject("peer-forward-add-or-update-flow-rule");
14 + public static final MessageSubject REMOVE_FLOW_RULE = new MessageSubject("peer-forward-remove-flow-rule");
15 +}
1 +package org.onlab.onos.store.flow.impl;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +import static org.slf4j.LoggerFactory.getLogger;
5 +import static org.onlab.onos.store.flow.ReplicaInfoEvent.Type.MASTER_CHANGED;
6 +
7 +import java.util.Collections;
8 +import java.util.List;
9 +
10 +import org.apache.felix.scr.annotations.Activate;
11 +import org.apache.felix.scr.annotations.Component;
12 +import org.apache.felix.scr.annotations.Deactivate;
13 +import org.apache.felix.scr.annotations.Reference;
14 +import org.apache.felix.scr.annotations.ReferenceCardinality;
15 +import org.apache.felix.scr.annotations.Service;
16 +import org.onlab.onos.cluster.NodeId;
17 +import org.onlab.onos.event.AbstractListenerRegistry;
18 +import org.onlab.onos.event.EventDeliveryService;
19 +import org.onlab.onos.mastership.MastershipEvent;
20 +import org.onlab.onos.mastership.MastershipListener;
21 +import org.onlab.onos.mastership.MastershipService;
22 +import org.onlab.onos.net.DeviceId;
23 +import org.onlab.onos.store.flow.ReplicaInfo;
24 +import org.onlab.onos.store.flow.ReplicaInfoEvent;
25 +import org.onlab.onos.store.flow.ReplicaInfoEventListener;
26 +import org.onlab.onos.store.flow.ReplicaInfoService;
27 +import org.slf4j.Logger;
28 +
29 +/**
30 + * Manages replica placement information.
31 + */
32 +@Component(immediate = true)
33 +@Service
34 +public class ReplicaInfoManager implements ReplicaInfoService {
35 +
36 + private final Logger log = getLogger(getClass());
37 +
38 + private final MastershipListener mastershipListener = new InternalMastershipListener();
39 +
40 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
41 + protected EventDeliveryService eventDispatcher;
42 +
43 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
44 + protected MastershipService mastershipService;
45 +
46 + protected final AbstractListenerRegistry<ReplicaInfoEvent, ReplicaInfoEventListener>
47 + listenerRegistry = new AbstractListenerRegistry<>();
48 +
49 + @Activate
50 + public void activate() {
51 + eventDispatcher.addSink(ReplicaInfoEvent.class, listenerRegistry);
52 + mastershipService.addListener(mastershipListener);
53 + log.info("Started");
54 + }
55 +
56 + @Deactivate
57 + public void deactivate() {
58 + eventDispatcher.removeSink(ReplicaInfoEvent.class);
59 + mastershipService.removeListener(mastershipListener);
60 + log.info("Stopped");
61 + }
62 +
63 + @Override
64 + public ReplicaInfo getReplicaInfoFor(DeviceId deviceId) {
65 + // TODO: populate backup List when we reach the point we need them.
66 + return new ReplicaInfo(mastershipService.getMasterFor(deviceId),
67 + Collections.<NodeId>emptyList());
68 + }
69 +
70 + @Override
71 + public void addListener(ReplicaInfoEventListener listener) {
72 + listenerRegistry.addListener(checkNotNull(listener));
73 + }
74 +
75 + @Override
76 + public void removeListener(ReplicaInfoEventListener listener) {
77 + listenerRegistry.removeListener(checkNotNull(listener));
78 + }
79 +
80 + final class InternalMastershipListener implements MastershipListener {
81 +
82 + @Override
83 + public void event(MastershipEvent event) {
84 + // TODO: distinguish stby list update, when MastershipService,
85 + // start publishing them
86 + final List<NodeId> standbyList = Collections.<NodeId>emptyList();
87 + eventDispatcher.post(new ReplicaInfoEvent(MASTER_CHANGED,
88 + event.subject(),
89 + new ReplicaInfo(event.master(), standbyList)));
90 + }
91 + }
92 +
93 +}
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,22 +38,29 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; ...@@ -32,22 +38,29 @@ 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;
39 import org.onlab.packet.MacAddress; 45 import org.onlab.packet.MacAddress;
40 import org.onlab.packet.VlanId; 46 import org.onlab.packet.VlanId;
41 -import org.onlab.util.KryoPool; 47 +import org.onlab.util.KryoNamespace;
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.
...@@ -86,26 +99,60 @@ public class GossipHostStore ...@@ -86,26 +99,60 @@ public class GossipHostStore
86 private static final KryoSerializer SERIALIZER = new KryoSerializer() { 99 private static final KryoSerializer SERIALIZER = new KryoSerializer() {
87 @Override 100 @Override
88 protected void setupKryoPool() { 101 protected void setupKryoPool() {
89 - serializerPool = KryoPool.newBuilder() 102 + serializerPool = KryoNamespace.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,10 +39,10 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; ...@@ -41,10 +39,10 @@ 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.KryoNamespace;
48 import org.slf4j.Logger; 46 import org.slf4j.Logger;
49 47
50 import java.io.IOException; 48 import java.io.IOException;
...@@ -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
...@@ -110,7 +110,7 @@ public class GossipLinkStore ...@@ -110,7 +110,7 @@ public class GossipLinkStore
110 private static final KryoSerializer SERIALIZER = new KryoSerializer() { 110 private static final KryoSerializer SERIALIZER = new KryoSerializer() {
111 @Override 111 @Override
112 protected void setupKryoPool() { 112 protected void setupKryoPool() {
113 - serializerPool = KryoPool.newBuilder() 113 + serializerPool = KryoNamespace.newBuilder()
114 .register(DistributedStoreSerializers.COMMON) 114 .register(DistributedStoreSerializers.COMMON)
115 .register(InternalLinkEvent.class) 115 .register(InternalLinkEvent.class)
116 .register(InternalLinkRemovedEvent.class) 116 .register(InternalLinkRemovedEvent.class)
...@@ -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;
...@@ -360,7 +360,14 @@ public class GossipLinkStore ...@@ -360,7 +360,14 @@ public class GossipLinkStore
360 final LinkKey key = linkKey(src, dst); 360 final LinkKey key = linkKey(src, dst);
361 361
362 DeviceId dstDeviceId = dst.deviceId(); 362 DeviceId dstDeviceId = dst.deviceId();
363 - Timestamp timestamp = deviceClockService.getTimestamp(dstDeviceId); 363 + Timestamp timestamp = null;
364 + try {
365 + timestamp = deviceClockService.getTimestamp(dstDeviceId);
366 + } catch (IllegalStateException e) {
367 + //there are times when this is called before mastership
368 + // handoff correctly completes.
369 + return null;
370 + }
364 371
365 LinkEvent event = removeLinkInternal(key, timestamp); 372 LinkEvent event = removeLinkInternal(key, timestamp);
366 373
...@@ -377,14 +384,54 @@ public class GossipLinkStore ...@@ -377,14 +384,54 @@ public class GossipLinkStore
377 return event; 384 return event;
378 } 385 }
379 386
387 + private static Timestamped<LinkDescription> getPrimaryDescription(
388 + Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions) {
389 +
390 + synchronized (linkDescriptions) {
391 + for (Entry<ProviderId, Timestamped<LinkDescription>>
392 + e : linkDescriptions.entrySet()) {
393 +
394 + if (!e.getKey().isAncillary()) {
395 + return e.getValue();
396 + }
397 + }
398 + }
399 + return null;
400 + }
401 +
402 +
403 + // TODO: consider slicing out as Timestamp utils
404 + /**
405 + * Checks is timestamp is more recent than timestamped object.
406 + *
407 + * @param timestamp to check if this is more recent then other
408 + * @param timestamped object to be tested against
409 + * @return true if {@code timestamp} is more recent than {@code timestamped}
410 + * or {@code timestamped is null}
411 + */
412 + private static boolean isMoreRecent(Timestamp timestamp, Timestamped<?> timestamped) {
413 + checkNotNull(timestamp);
414 + if (timestamped == null) {
415 + return true;
416 + }
417 + return timestamp.compareTo(timestamped.timestamp()) > 0;
418 + }
419 +
380 private LinkEvent removeLinkInternal(LinkKey key, Timestamp timestamp) { 420 private LinkEvent removeLinkInternal(LinkKey key, Timestamp timestamp) {
381 - Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions = 421 + Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions
382 - getLinkDescriptions(key); 422 + = getOrCreateLinkDescriptions(key);
423 +
383 synchronized (linkDescriptions) { 424 synchronized (linkDescriptions) {
425 + if (linkDescriptions.isEmpty()) {
426 + // never seen such link before. keeping timestamp for record
427 + removedLinks.put(key, timestamp);
428 + return null;
429 + }
384 // accept removal request if given timestamp is newer than 430 // accept removal request if given timestamp is newer than
385 // the latest Timestamp from Primary provider 431 // the latest Timestamp from Primary provider
386 - ProviderId primaryProviderId = pickPrimaryProviderId(linkDescriptions); 432 + Timestamped<LinkDescription> prim = getPrimaryDescription(linkDescriptions);
387 - if (linkDescriptions.get(primaryProviderId).isNewer(timestamp)) { 433 + if (!isMoreRecent(timestamp, prim)) {
434 + // outdated remove request, ignore
388 return null; 435 return null;
389 } 436 }
390 removedLinks.put(key, timestamp); 437 removedLinks.put(key, timestamp);
...@@ -406,12 +453,13 @@ public class GossipLinkStore ...@@ -406,12 +453,13 @@ public class GossipLinkStore
406 /** 453 /**
407 * @return primary ProviderID, or randomly chosen one if none exists 454 * @return primary ProviderID, or randomly chosen one if none exists
408 */ 455 */
409 - private ProviderId pickPrimaryProviderId( 456 + private static ProviderId pickBaseProviderId(
410 Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions) { 457 Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions) {
411 458
412 ProviderId fallBackPrimary = null; 459 ProviderId fallBackPrimary = null;
413 for (Entry<ProviderId, Timestamped<LinkDescription>> e : linkDescriptions.entrySet()) { 460 for (Entry<ProviderId, Timestamped<LinkDescription>> e : linkDescriptions.entrySet()) {
414 if (!e.getKey().isAncillary()) { 461 if (!e.getKey().isAncillary()) {
462 + // found primary
415 return e.getKey(); 463 return e.getKey();
416 } else if (fallBackPrimary == null) { 464 } else if (fallBackPrimary == null) {
417 // pick randomly as a fallback in case there is no primary 465 // pick randomly as a fallback in case there is no primary
...@@ -421,9 +469,10 @@ public class GossipLinkStore ...@@ -421,9 +469,10 @@ public class GossipLinkStore
421 return fallBackPrimary; 469 return fallBackPrimary;
422 } 470 }
423 471
472 + // Guarded by linkDescs value (=locking each Link)
424 private Link composeLink(Map<ProviderId, Timestamped<LinkDescription>> descs) { 473 private Link composeLink(Map<ProviderId, Timestamped<LinkDescription>> descs) {
425 - ProviderId primaryProviderId = pickPrimaryProviderId(descs); 474 + ProviderId baseProviderId = pickBaseProviderId(descs);
426 - Timestamped<LinkDescription> base = descs.get(primaryProviderId); 475 + Timestamped<LinkDescription> base = descs.get(baseProviderId);
427 476
428 ConnectPoint src = base.value().src(); 477 ConnectPoint src = base.value().src();
429 ConnectPoint dst = base.value().dst(); 478 ConnectPoint dst = base.value().dst();
...@@ -432,7 +481,7 @@ public class GossipLinkStore ...@@ -432,7 +481,7 @@ public class GossipLinkStore
432 annotations = merge(annotations, base.value().annotations()); 481 annotations = merge(annotations, base.value().annotations());
433 482
434 for (Entry<ProviderId, Timestamped<LinkDescription>> e : descs.entrySet()) { 483 for (Entry<ProviderId, Timestamped<LinkDescription>> e : descs.entrySet()) {
435 - if (primaryProviderId.equals(e.getKey())) { 484 + if (baseProviderId.equals(e.getKey())) {
436 continue; 485 continue;
437 } 486 }
438 487
...@@ -445,10 +494,10 @@ public class GossipLinkStore ...@@ -445,10 +494,10 @@ public class GossipLinkStore
445 annotations = merge(annotations, e.getValue().value().annotations()); 494 annotations = merge(annotations, e.getValue().value().annotations());
446 } 495 }
447 496
448 - return new DefaultLink(primaryProviderId , src, dst, type, annotations); 497 + return new DefaultLink(baseProviderId, src, dst, type, annotations);
449 } 498 }
450 499
451 - private Map<ProviderId, Timestamped<LinkDescription>> getLinkDescriptions(LinkKey key) { 500 + private Map<ProviderId, Timestamped<LinkDescription>> getOrCreateLinkDescriptions(LinkKey key) {
452 Map<ProviderId, Timestamped<LinkDescription>> r; 501 Map<ProviderId, Timestamped<LinkDescription>> r;
453 r = linkDescs.get(key); 502 r = linkDescs.get(key);
454 if (r != null) { 503 if (r != null) {
...@@ -464,11 +513,11 @@ public class GossipLinkStore ...@@ -464,11 +513,11 @@ public class GossipLinkStore
464 } 513 }
465 } 514 }
466 515
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(); 516 private final Function<LinkKey, Link> lookupLink = new LookupLink();
517 + /**
518 + * Returns a Function to lookup Link instance using LinkKey from cache.
519 + * @return
520 + */
472 private Function<LinkKey, Link> lookupLink() { 521 private Function<LinkKey, Link> lookupLink() {
473 return lookupLink; 522 return lookupLink;
474 } 523 }
...@@ -476,20 +525,11 @@ public class GossipLinkStore ...@@ -476,20 +525,11 @@ public class GossipLinkStore
476 private final class LookupLink implements Function<LinkKey, Link> { 525 private final class LookupLink implements Function<LinkKey, Link> {
477 @Override 526 @Override
478 public Link apply(LinkKey input) { 527 public Link apply(LinkKey input) {
479 - return links.get(input); 528 + if (input == null) {
480 - } 529 + return null;
481 - } 530 + } else {
482 - 531 + return links.get(input);
483 - private static final class IsPrimary implements Predicate<Provided> { 532 + }
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 } 533 }
494 } 534 }
495 535
...@@ -499,7 +539,6 @@ public class GossipLinkStore ...@@ -499,7 +539,6 @@ public class GossipLinkStore
499 } 539 }
500 } 540 }
501 541
502 - // TODO: should we be throwing exception?
503 private void broadcastMessage(MessageSubject subject, Object event) throws IOException { 542 private void broadcastMessage(MessageSubject subject, Object event) throws IOException {
504 ClusterMessage message = new ClusterMessage( 543 ClusterMessage message = new ClusterMessage(
505 clusterService.getLocalNode().id(), 544 clusterService.getLocalNode().id(),
...@@ -508,17 +547,12 @@ public class GossipLinkStore ...@@ -508,17 +547,12 @@ public class GossipLinkStore
508 clusterCommunicator.broadcast(message); 547 clusterCommunicator.broadcast(message);
509 } 548 }
510 549
511 - // TODO: should we be throwing exception? 550 + private void unicastMessage(NodeId recipient, MessageSubject subject, Object event) throws IOException {
512 - private void unicastMessage(NodeId recipient, MessageSubject subject, Object event) { 551 + ClusterMessage message = new ClusterMessage(
513 - try { 552 + clusterService.getLocalNode().id(),
514 - ClusterMessage message = new ClusterMessage( 553 + subject,
515 - clusterService.getLocalNode().id(), 554 + SERIALIZER.encode(event));
516 - subject, 555 + 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 } 556 }
523 557
524 private void notifyPeers(InternalLinkEvent event) throws IOException { 558 private void notifyPeers(InternalLinkEvent event) throws IOException {
...@@ -529,12 +563,22 @@ public class GossipLinkStore ...@@ -529,12 +563,22 @@ public class GossipLinkStore
529 broadcastMessage(GossipLinkStoreMessageSubjects.LINK_REMOVED, event); 563 broadcastMessage(GossipLinkStoreMessageSubjects.LINK_REMOVED, event);
530 } 564 }
531 565
566 + // notify peer, silently ignoring error
532 private void notifyPeer(NodeId peer, InternalLinkEvent event) { 567 private void notifyPeer(NodeId peer, InternalLinkEvent event) {
533 - unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_UPDATE, event); 568 + try {
569 + unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_UPDATE, event);
570 + } catch (IOException e) {
571 + log.debug("Failed to notify peer {} with message {}", peer, event);
572 + }
534 } 573 }
535 574
575 + // notify peer, silently ignoring error
536 private void notifyPeer(NodeId peer, InternalLinkRemovedEvent event) { 576 private void notifyPeer(NodeId peer, InternalLinkRemovedEvent event) {
537 - unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_REMOVED, event); 577 + try {
578 + unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_REMOVED, event);
579 + } catch (IOException e) {
580 + log.debug("Failed to notify peer {} with message {}", peer, event);
581 + }
538 } 582 }
539 583
540 private final class SendAdvertisementTask implements Runnable { 584 private final class SendAdvertisementTask implements Runnable {
...@@ -573,9 +617,9 @@ public class GossipLinkStore ...@@ -573,9 +617,9 @@ public class GossipLinkStore
573 } 617 }
574 618
575 try { 619 try {
576 - unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_ANTI_ENTROPY_ADVERTISEMENT, ad); 620 + unicastMessage(peer, LINK_ANTI_ENTROPY_ADVERTISEMENT, ad);
577 - } catch (Exception e) { 621 + } catch (IOException e) {
578 - log.error("Failed to send anti-entropy advertisement", e); 622 + log.debug("Failed to send anti-entropy advertisement to {}", peer);
579 return; 623 return;
580 } 624 }
581 } catch (Exception e) { 625 } catch (Exception e) {
...@@ -608,42 +652,75 @@ public class GossipLinkStore ...@@ -608,42 +652,75 @@ public class GossipLinkStore
608 return new LinkAntiEntropyAdvertisement(self, linkTimestamps, linkTombstones); 652 return new LinkAntiEntropyAdvertisement(self, linkTimestamps, linkTombstones);
609 } 653 }
610 654
611 - private void handleAntiEntropyAdvertisement(LinkAntiEntropyAdvertisement advertisement) { 655 + private void handleAntiEntropyAdvertisement(LinkAntiEntropyAdvertisement ad) {
612 -
613 - NodeId peer = advertisement.sender();
614 656
615 - Map<LinkFragmentId, Timestamp> linkTimestamps = advertisement.linkTimestamps(); 657 + final NodeId sender = ad.sender();
616 - Map<LinkKey, Timestamp> linkTombstones = advertisement.linkTombstones(); 658 + boolean localOutdated = false;
617 - for (Map.Entry<LinkFragmentId, Timestamp> entry : linkTimestamps.entrySet()) {
618 - LinkFragmentId linkFragmentId = entry.getKey();
619 - Timestamp peerTimestamp = entry.getValue();
620 659
621 - LinkKey key = linkFragmentId.linkKey(); 660 + for (Entry<LinkKey, Map<ProviderId, Timestamped<LinkDescription>>>
622 - ProviderId providerId = linkFragmentId.providerId(); 661 + l : linkDescs.entrySet()) {
623 - 662 +
624 - Timestamped<LinkDescription> linkDescription = getLinkDescription(key, providerId); 663 + final LinkKey key = l.getKey();
625 - if (linkDescription.isNewer(peerTimestamp)) { 664 + final Map<ProviderId, Timestamped<LinkDescription>> link = l.getValue();
626 - // I have more recent link description. update peer. 665 + synchronized (link) {
627 - notifyPeer(peer, new InternalLinkEvent(providerId, linkDescription)); 666 + Timestamp localLatest = removedLinks.get(key);
628 - } 667 +
629 - // else TODO: Peer has more recent link description. request it. 668 + for (Entry<ProviderId, Timestamped<LinkDescription>> p : link.entrySet()) {
630 - 669 + final ProviderId providerId = p.getKey();
631 - Timestamp linkRemovedTimestamp = removedLinks.get(key); 670 + final Timestamped<LinkDescription> pDesc = p.getValue();
632 - if (linkRemovedTimestamp != null && linkRemovedTimestamp.compareTo(peerTimestamp) > 0) { 671 +
633 - // peer has a zombie link. update peer. 672 + final LinkFragmentId fragId = new LinkFragmentId(key, providerId);
634 - notifyPeer(peer, new InternalLinkRemovedEvent(key, linkRemovedTimestamp)); 673 + // remote
674 + Timestamp remoteTimestamp = ad.linkTimestamps().get(fragId);
675 + if (remoteTimestamp == null) {
676 + remoteTimestamp = ad.linkTombstones().get(key);
677 + }
678 + if (remoteTimestamp == null ||
679 + pDesc.isNewer(remoteTimestamp)) {
680 + // I have more recent link description. update peer.
681 + notifyPeer(sender, new InternalLinkEvent(providerId, pDesc));
682 + } else {
683 + final Timestamp remoteLive = ad.linkTimestamps().get(fragId);
684 + if (remoteLive != null &&
685 + remoteLive.compareTo(pDesc.timestamp()) > 0) {
686 + // I have something outdated
687 + localOutdated = true;
688 + }
689 + }
690 +
691 + // search local latest along the way
692 + if (localLatest == null ||
693 + pDesc.isNewer(localLatest)) {
694 + localLatest = pDesc.timestamp();
695 + }
696 + }
697 + // Tests if remote remove is more recent then local latest.
698 + final Timestamp remoteRemove = ad.linkTombstones().get(key);
699 + if (remoteRemove != null) {
700 + if (localLatest != null &&
701 + localLatest.compareTo(remoteRemove) < 0) {
702 + // remote remove is more recent
703 + notifyDelegateIfNotNull(removeLinkInternal(key, remoteRemove));
704 + }
705 + }
635 } 706 }
636 } 707 }
637 708
638 - for (Map.Entry<LinkKey, Timestamp> entry : linkTombstones.entrySet()) { 709 + // populate remove info if not known locally
639 - LinkKey key = entry.getKey(); 710 + for (Entry<LinkKey, Timestamp> remoteRm : ad.linkTombstones().entrySet()) {
640 - Timestamp peerTimestamp = entry.getValue(); 711 + final LinkKey key = remoteRm.getKey();
712 + final Timestamp remoteRemove = remoteRm.getValue();
713 + // relying on removeLinkInternal to ignore stale info
714 + notifyDelegateIfNotNull(removeLinkInternal(key, remoteRemove));
715 + }
641 716
642 - ProviderId primaryProviderId = pickPrimaryProviderId(getLinkDescriptions(key)); 717 + if (localOutdated) {
643 - if (primaryProviderId != null) { 718 + // send back advertisement to speed up convergence
644 - if (!getLinkDescription(key, primaryProviderId).isNewer(peerTimestamp)) { 719 + try {
645 - notifyDelegateIfNotNull(removeLinkInternal(key, peerTimestamp)); 720 + unicastMessage(sender, LINK_ANTI_ENTROPY_ADVERTISEMENT,
646 - } 721 + createAdvertisement());
722 + } catch (IOException e) {
723 + log.debug("Failed to send back active advertisement");
647 } 724 }
648 } 725 }
649 } 726 }
...@@ -652,7 +729,7 @@ public class GossipLinkStore ...@@ -652,7 +729,7 @@ public class GossipLinkStore
652 @Override 729 @Override
653 public void handle(ClusterMessage message) { 730 public void handle(ClusterMessage message) {
654 731
655 - log.info("Received link event from peer: {}", message.sender()); 732 + log.trace("Received link event from peer: {}", message.sender());
656 InternalLinkEvent event = (InternalLinkEvent) SERIALIZER.decode(message.payload()); 733 InternalLinkEvent event = (InternalLinkEvent) SERIALIZER.decode(message.payload());
657 734
658 ProviderId providerId = event.providerId(); 735 ProviderId providerId = event.providerId();
...@@ -666,7 +743,7 @@ public class GossipLinkStore ...@@ -666,7 +743,7 @@ public class GossipLinkStore
666 @Override 743 @Override
667 public void handle(ClusterMessage message) { 744 public void handle(ClusterMessage message) {
668 745
669 - log.info("Received link removed event from peer: {}", message.sender()); 746 + log.trace("Received link removed event from peer: {}", message.sender());
670 InternalLinkRemovedEvent event = (InternalLinkRemovedEvent) SERIALIZER.decode(message.payload()); 747 InternalLinkRemovedEvent event = (InternalLinkRemovedEvent) SERIALIZER.decode(message.payload());
671 748
672 LinkKey linkKey = event.linkKey(); 749 LinkKey linkKey = event.linkKey();
......
...@@ -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.KryoNamespace;
7 7
8 public final class DistributedStoreSerializers { 8 public final class DistributedStoreSerializers {
9 9
10 /** 10 /**
11 - * KryoPool which can serialize ON.lab misc classes. 11 + * KryoNamespace which can serialize ON.lab misc classes.
12 */ 12 */
13 - public static final KryoPool COMMON = KryoPool.newBuilder() 13 + public static final KryoNamespace COMMON = KryoNamespace.newBuilder()
14 - .register(KryoPoolUtil.API) 14 + .register(KryoNamespaces.API)
15 .register(Timestamped.class) 15 .register(Timestamped.class)
16 .register(MastershipBasedTimestamp.class, new MastershipBasedTimestampSerializer()) 16 .register(MastershipBasedTimestamp.class, new MastershipBasedTimestampSerializer())
17 .register(WallClockTimestamp.class) 17 .register(WallClockTimestamp.class)
......
1 +/**
2 + * Implementation of distributed topology store using p2p synchronization protocol.
3 + */
4 +package org.onlab.onos.store.topology.impl;
...@@ -53,6 +53,7 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; ...@@ -53,6 +53,7 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
53 import org.onlab.onos.store.cluster.messaging.ClusterMessage; 53 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
54 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler; 54 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
55 import org.onlab.onos.store.cluster.messaging.MessageSubject; 55 import org.onlab.onos.store.cluster.messaging.MessageSubject;
56 +import org.onlab.packet.ChassisId;
56 import org.onlab.packet.IpPrefix; 57 import org.onlab.packet.IpPrefix;
57 58
58 import com.google.common.collect.Iterables; 59 import com.google.common.collect.Iterables;
...@@ -74,6 +75,7 @@ public class GossipDeviceStoreTest { ...@@ -74,6 +75,7 @@ public class GossipDeviceStoreTest {
74 private static final String SW1 = "3.8.1"; 75 private static final String SW1 = "3.8.1";
75 private static final String SW2 = "3.9.5"; 76 private static final String SW2 = "3.9.5";
76 private static final String SN = "43311-12345"; 77 private static final String SN = "43311-12345";
78 + private static final ChassisId CID = new ChassisId();
77 79
78 private static final PortNumber P1 = PortNumber.portNumber(1); 80 private static final PortNumber P1 = PortNumber.portNumber(1);
79 private static final PortNumber P2 = PortNumber.portNumber(2); 81 private static final PortNumber P2 = PortNumber.portNumber(2);
...@@ -158,7 +160,7 @@ public class GossipDeviceStoreTest { ...@@ -158,7 +160,7 @@ public class GossipDeviceStoreTest {
158 SparseAnnotations... annotations) { 160 SparseAnnotations... annotations) {
159 DeviceDescription description = 161 DeviceDescription description =
160 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR, 162 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
161 - HW, swVersion, SN, annotations); 163 + HW, swVersion, SN, CID, annotations);
162 reset(clusterCommunicator); 164 reset(clusterCommunicator);
163 try { 165 try {
164 expect(clusterCommunicator.broadcast(anyObject(ClusterMessage.class))) 166 expect(clusterCommunicator.broadcast(anyObject(ClusterMessage.class)))
...@@ -175,7 +177,7 @@ public class GossipDeviceStoreTest { ...@@ -175,7 +177,7 @@ public class GossipDeviceStoreTest {
175 SparseAnnotations... annotations) { 177 SparseAnnotations... annotations) {
176 DeviceDescription description = 178 DeviceDescription description =
177 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR, 179 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
178 - HW, swVersion, SN, annotations); 180 + HW, swVersion, SN, CID, annotations);
179 deviceStore.createOrUpdateDevice(PIDA, deviceId, description); 181 deviceStore.createOrUpdateDevice(PIDA, deviceId, description);
180 } 182 }
181 183
...@@ -315,7 +317,7 @@ public class GossipDeviceStoreTest { ...@@ -315,7 +317,7 @@ public class GossipDeviceStoreTest {
315 public final void testCreateOrUpdateDevice() throws IOException { 317 public final void testCreateOrUpdateDevice() throws IOException {
316 DeviceDescription description = 318 DeviceDescription description =
317 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, 319 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
318 - HW, SW1, SN); 320 + HW, SW1, SN, CID);
319 Capture<ClusterMessage> bcast = new Capture<>(); 321 Capture<ClusterMessage> bcast = new Capture<>();
320 322
321 resetCommunicatorExpectingSingleBroadcast(bcast); 323 resetCommunicatorExpectingSingleBroadcast(bcast);
...@@ -328,7 +330,7 @@ public class GossipDeviceStoreTest { ...@@ -328,7 +330,7 @@ public class GossipDeviceStoreTest {
328 330
329 DeviceDescription description2 = 331 DeviceDescription description2 =
330 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, 332 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
331 - HW, SW2, SN); 333 + HW, SW2, SN, CID);
332 resetCommunicatorExpectingSingleBroadcast(bcast); 334 resetCommunicatorExpectingSingleBroadcast(bcast);
333 DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2); 335 DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
334 assertEquals(DEVICE_UPDATED, event2.type()); 336 assertEquals(DEVICE_UPDATED, event2.type());
...@@ -346,7 +348,7 @@ public class GossipDeviceStoreTest { ...@@ -346,7 +348,7 @@ public class GossipDeviceStoreTest {
346 // add 348 // add
347 DeviceDescription description = 349 DeviceDescription description =
348 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, 350 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
349 - HW, SW1, SN, A2); 351 + HW, SW1, SN, CID, A2);
350 Capture<ClusterMessage> bcast = new Capture<>(); 352 Capture<ClusterMessage> bcast = new Capture<>();
351 353
352 resetCommunicatorExpectingSingleBroadcast(bcast); 354 resetCommunicatorExpectingSingleBroadcast(bcast);
...@@ -362,7 +364,7 @@ public class GossipDeviceStoreTest { ...@@ -362,7 +364,7 @@ public class GossipDeviceStoreTest {
362 // update from primary 364 // update from primary
363 DeviceDescription description2 = 365 DeviceDescription description2 =
364 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, 366 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
365 - HW, SW2, SN, A1); 367 + HW, SW2, SN, CID, A1);
366 resetCommunicatorExpectingSingleBroadcast(bcast); 368 resetCommunicatorExpectingSingleBroadcast(bcast);
367 369
368 DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2); 370 DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
...@@ -392,7 +394,7 @@ public class GossipDeviceStoreTest { ...@@ -392,7 +394,7 @@ public class GossipDeviceStoreTest {
392 // But, Ancillary annotations will be in effect 394 // But, Ancillary annotations will be in effect
393 DeviceDescription description3 = 395 DeviceDescription description3 =
394 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, 396 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
395 - HW, SW1, SN, A2_2); 397 + HW, SW1, SN, CID, A2_2);
396 resetCommunicatorExpectingSingleBroadcast(bcast); 398 resetCommunicatorExpectingSingleBroadcast(bcast);
397 399
398 DeviceEvent event3 = deviceStore.createOrUpdateDevice(PIDA, DID1, description3); 400 DeviceEvent event3 = deviceStore.createOrUpdateDevice(PIDA, DID1, description3);
...@@ -775,7 +777,7 @@ public class GossipDeviceStoreTest { ...@@ -775,7 +777,7 @@ public class GossipDeviceStoreTest {
775 777
776 DeviceDescription description = 778 DeviceDescription description =
777 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, 779 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
778 - HW, SW1, SN); 780 + HW, SW1, SN, CID);
779 deviceStore.setDelegate(checkAdd); 781 deviceStore.setDelegate(checkAdd);
780 deviceStore.createOrUpdateDevice(PID, DID1, description); 782 deviceStore.createOrUpdateDevice(PID, DID1, description);
781 assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS)); 783 assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
...@@ -783,7 +785,7 @@ public class GossipDeviceStoreTest { ...@@ -783,7 +785,7 @@ public class GossipDeviceStoreTest {
783 785
784 DeviceDescription description2 = 786 DeviceDescription description2 =
785 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, 787 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
786 - HW, SW2, SN); 788 + HW, SW2, SN, CID);
787 deviceStore.unsetDelegate(checkAdd); 789 deviceStore.unsetDelegate(checkAdd);
788 deviceStore.setDelegate(checkUpdate); 790 deviceStore.setDelegate(checkUpdate);
789 deviceStore.createOrUpdateDevice(PID, DID1, description2); 791 deviceStore.createOrUpdateDevice(PID, DID1, description2);
......
1 +package org.onlab.onos.store.flow.impl;
2 +
3 +import static com.google.common.base.Preconditions.checkState;
4 +import static org.junit.Assert.*;
5 +
6 +import java.util.Collections;
7 +import java.util.Map;
8 +import java.util.concurrent.CountDownLatch;
9 +import java.util.concurrent.TimeUnit;
10 +
11 +import org.junit.After;
12 +import org.junit.Before;
13 +import org.junit.Test;
14 +import org.onlab.onos.cluster.NodeId;
15 +import org.onlab.onos.event.AbstractListenerRegistry;
16 +import org.onlab.onos.event.DefaultEventSinkRegistry;
17 +import org.onlab.onos.event.Event;
18 +import org.onlab.onos.event.EventDeliveryService;
19 +import org.onlab.onos.event.EventSink;
20 +import org.onlab.onos.mastership.MastershipEvent;
21 +import org.onlab.onos.mastership.MastershipEvent.Type;
22 +import org.onlab.onos.mastership.MastershipListener;
23 +import org.onlab.onos.mastership.MastershipService;
24 +import org.onlab.onos.mastership.MastershipServiceAdapter;
25 +import org.onlab.onos.net.DeviceId;
26 +import org.onlab.onos.store.flow.ReplicaInfo;
27 +import org.onlab.onos.store.flow.ReplicaInfoEvent;
28 +import org.onlab.onos.store.flow.ReplicaInfoEventListener;
29 +import org.onlab.onos.store.flow.ReplicaInfoService;
30 +
31 +import com.google.common.base.Optional;
32 +import com.google.common.collect.Maps;
33 +
34 +public class ReplicaInfoManagerTest {
35 +
36 +
37 + private static final DeviceId DID1 = DeviceId.deviceId("of:1");
38 + private static final DeviceId DID2 = DeviceId.deviceId("of:2");
39 + private static final NodeId NID1 = new NodeId("foo");
40 +
41 + private ReplicaInfoManager mgr;
42 + private ReplicaInfoService service;
43 +
44 + private AbstractListenerRegistry<MastershipEvent, MastershipListener>
45 + mastershipListenerRegistry;
46 + private TestEventDispatcher eventDispatcher;
47 +
48 +
49 + @Before
50 + public void setUp() throws Exception {
51 + mastershipListenerRegistry = new AbstractListenerRegistry<>();
52 +
53 + mgr = new ReplicaInfoManager();
54 + service = mgr;
55 +
56 + eventDispatcher = new TestEventDispatcher();
57 + mgr.eventDispatcher = eventDispatcher;
58 + mgr.mastershipService = new TestMastershipService();
59 +
60 + // register dummy mastership event source
61 + mgr.eventDispatcher.addSink(MastershipEvent.class, mastershipListenerRegistry);
62 +
63 + mgr.activate();
64 + }
65 +
66 + @After
67 + public void tearDown() throws Exception {
68 + mgr.deactivate();
69 + }
70 +
71 + @Test
72 + public void testGetReplicaInfoFor() {
73 + ReplicaInfo info1 = service.getReplicaInfoFor(DID1);
74 + assertEquals(Optional.of(NID1), info1.master());
75 + // backups are always empty for now
76 + assertEquals(Collections.emptyList(), info1.backups());
77 +
78 + ReplicaInfo info2 = service.getReplicaInfoFor(DID2);
79 + assertEquals("There's no master", Optional.absent(), info2.master());
80 + // backups are always empty for now
81 + assertEquals(Collections.emptyList(), info2.backups());
82 + }
83 +
84 + @Test
85 + public void testReplicaInfoEvent() throws InterruptedException {
86 + final CountDownLatch latch = new CountDownLatch(1);
87 + service.addListener(new MasterNodeCheck(latch, DID1, NID1));
88 +
89 + // fake MastershipEvent
90 + eventDispatcher.post(new MastershipEvent(Type.MASTER_CHANGED, DID1, NID1));
91 +
92 + assertTrue(latch.await(1, TimeUnit.SECONDS));
93 + }
94 +
95 +
96 + private final class MasterNodeCheck implements ReplicaInfoEventListener {
97 + private final CountDownLatch latch;
98 + private Optional<NodeId> expectedMaster;
99 + private DeviceId expectedDevice;
100 +
101 +
102 + MasterNodeCheck(CountDownLatch latch, DeviceId did,
103 + NodeId nid) {
104 + this.latch = latch;
105 + this.expectedMaster = Optional.fromNullable(nid);
106 + this.expectedDevice = did;
107 + }
108 +
109 + @Override
110 + public void event(ReplicaInfoEvent event) {
111 + assertEquals(expectedDevice, event.subject());
112 + assertEquals(expectedMaster, event.replicaInfo().master());
113 + // backups are always empty for now
114 + assertEquals(Collections.emptyList(), event.replicaInfo().backups());
115 + latch.countDown();
116 + }
117 + }
118 +
119 +
120 + private final class TestMastershipService
121 + extends MastershipServiceAdapter
122 + implements MastershipService {
123 +
124 + private Map<DeviceId, NodeId> masters;
125 +
126 + TestMastershipService() {
127 + masters = Maps.newHashMap();
128 + masters.put(DID1, NID1);
129 + // DID2 has no master
130 + }
131 +
132 + @Override
133 + public NodeId getMasterFor(DeviceId deviceId) {
134 + return masters.get(deviceId);
135 + }
136 +
137 + @Override
138 + public void addListener(MastershipListener listener) {
139 + mastershipListenerRegistry.addListener(listener);
140 + }
141 +
142 + @Override
143 + public void removeListener(MastershipListener listener) {
144 + mastershipListenerRegistry.removeListener(listener);
145 + }
146 + }
147 +
148 +
149 + // code clone
150 + /**
151 + * Implements event delivery system that delivers events synchronously, or
152 + * in-line with the post method invocation.
153 + */
154 + private static class TestEventDispatcher extends DefaultEventSinkRegistry
155 + implements EventDeliveryService {
156 +
157 + @SuppressWarnings({ "rawtypes", "unchecked" })
158 + @Override
159 + public void post(Event event) {
160 + EventSink sink = getSink(event.getClass());
161 + checkState(sink != null, "No sink for event %s", event);
162 + sink.process(event);
163 + }
164 + }
165 +}
...@@ -7,7 +7,7 @@ import java.nio.ByteBuffer; ...@@ -7,7 +7,7 @@ import java.nio.ByteBuffer;
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.serializers.MastershipBasedTimestampSerializer; 9 import org.onlab.onos.store.serializers.MastershipBasedTimestampSerializer;
10 -import org.onlab.util.KryoPool; 10 +import org.onlab.util.KryoNamespace;
11 11
12 import com.google.common.testing.EqualsTester; 12 import com.google.common.testing.EqualsTester;
13 13
...@@ -63,7 +63,7 @@ public class MastershipBasedTimestampTest { ...@@ -63,7 +63,7 @@ public class MastershipBasedTimestampTest {
63 @Test 63 @Test
64 public final void testKryoSerializable() { 64 public final void testKryoSerializable() {
65 final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024); 65 final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
66 - final KryoPool kryos = KryoPool.newBuilder() 66 + final KryoNamespace kryos = KryoNamespace.newBuilder()
67 .register(MastershipBasedTimestamp.class) 67 .register(MastershipBasedTimestamp.class)
68 .build(); 68 .build();
69 69
...@@ -79,7 +79,7 @@ public class MastershipBasedTimestampTest { ...@@ -79,7 +79,7 @@ public class MastershipBasedTimestampTest {
79 @Test 79 @Test
80 public final void testKryoSerializableWithHandcraftedSerializer() { 80 public final void testKryoSerializableWithHandcraftedSerializer() {
81 final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024); 81 final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
82 - final KryoPool kryos = KryoPool.newBuilder() 82 + final KryoNamespace kryos = KryoNamespace.newBuilder()
83 .register(MastershipBasedTimestamp.class, new MastershipBasedTimestampSerializer()) 83 .register(MastershipBasedTimestamp.class, new MastershipBasedTimestampSerializer())
84 .build(); 84 .build();
85 85
......
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,8 +6,7 @@ import java.nio.ByteBuffer; ...@@ -6,8 +6,7 @@ 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; 9 +import org.onlab.util.KryoNamespace;
10 -import org.onlab.util.KryoPool;
11 10
12 import com.google.common.testing.EqualsTester; 11 import com.google.common.testing.EqualsTester;
13 12
...@@ -78,7 +77,7 @@ public class TimestampedTest { ...@@ -78,7 +77,7 @@ public class TimestampedTest {
78 @Test 77 @Test
79 public final void testKryoSerializable() { 78 public final void testKryoSerializable() {
80 final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024); 79 final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
81 - final KryoPool kryos = KryoPool.newBuilder() 80 + final KryoNamespace kryos = KryoNamespace.newBuilder()
82 .register(Timestamped.class, 81 .register(Timestamped.class,
83 MastershipBasedTimestamp.class) 82 MastershipBasedTimestamp.class)
84 .build(); 83 .build();
......
...@@ -6,7 +6,7 @@ import java.nio.ByteBuffer; ...@@ -6,7 +6,7 @@ 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.util.KryoPool; 9 +import org.onlab.util.KryoNamespace;
10 10
11 import com.google.common.testing.EqualsTester; 11 import com.google.common.testing.EqualsTester;
12 12
...@@ -30,7 +30,7 @@ public class WallClockTimestampTest { ...@@ -30,7 +30,7 @@ public class WallClockTimestampTest {
30 public final void testKryoSerializable() { 30 public final void testKryoSerializable() {
31 WallClockTimestamp ts1 = new WallClockTimestamp(); 31 WallClockTimestamp ts1 = new WallClockTimestamp();
32 final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024); 32 final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
33 - final KryoPool kryos = KryoPool.newBuilder() 33 + final KryoNamespace kryos = KryoNamespace.newBuilder()
34 .register(WallClockTimestamp.class) 34 .register(WallClockTimestamp.class)
35 .build(); 35 .build();
36 36
......
...@@ -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>
......
...@@ -2,6 +2,9 @@ package org.onlab.onos.store.mastership.impl; ...@@ -2,6 +2,9 @@ package org.onlab.onos.store.mastership.impl;
2 2
3 import static org.onlab.onos.mastership.MastershipEvent.Type.MASTER_CHANGED; 3 import static org.onlab.onos.mastership.MastershipEvent.Type.MASTER_CHANGED;
4 4
5 +import java.util.Collections;
6 +import java.util.LinkedList;
7 +import java.util.List;
5 import java.util.Map; 8 import java.util.Map;
6 import java.util.Set; 9 import java.util.Set;
7 10
...@@ -20,11 +23,18 @@ import org.onlab.onos.mastership.MastershipTerm; ...@@ -20,11 +23,18 @@ import org.onlab.onos.mastership.MastershipTerm;
20 import org.onlab.onos.net.DeviceId; 23 import org.onlab.onos.net.DeviceId;
21 import org.onlab.onos.net.MastershipRole; 24 import org.onlab.onos.net.MastershipRole;
22 import org.onlab.onos.store.common.AbstractHazelcastStore; 25 import org.onlab.onos.store.common.AbstractHazelcastStore;
26 +import org.onlab.onos.store.common.SMap;
27 +import org.onlab.onos.store.serializers.KryoNamespaces;
28 +import org.onlab.onos.store.serializers.KryoSerializer;
29 +import org.onlab.util.KryoNamespace;
23 30
24 import com.google.common.collect.ImmutableSet; 31 import com.google.common.collect.ImmutableSet;
25 -import com.hazelcast.core.ILock; 32 +import com.hazelcast.core.EntryEvent;
26 -import com.hazelcast.core.IMap; 33 +import com.hazelcast.core.EntryListener;
27 -import com.hazelcast.core.MultiMap; 34 +import com.hazelcast.core.IAtomicLong;
35 +import com.hazelcast.core.MapEvent;
36 +
37 +import static org.onlab.onos.net.MastershipRole.*;
28 38
29 /** 39 /**
30 * Distributed implementation of the mastership store. The store is 40 * Distributed implementation of the mastership store. The store is
...@@ -36,36 +46,42 @@ public class DistributedMastershipStore ...@@ -36,36 +46,42 @@ public class DistributedMastershipStore
36 extends AbstractHazelcastStore<MastershipEvent, MastershipStoreDelegate> 46 extends AbstractHazelcastStore<MastershipEvent, MastershipStoreDelegate>
37 implements MastershipStore { 47 implements MastershipStore {
38 48
39 - //arbitrary lock name
40 - private static final String LOCK = "lock";
41 //initial term/TTL value 49 //initial term/TTL value
42 private static final Integer INIT = 0; 50 private static final Integer INIT = 0;
43 51
44 - //devices to masters 52 + //device to node roles
45 - protected IMap<byte[], byte[]> masters; 53 + protected SMap<DeviceId, RoleValue> roleMap;
46 //devices to terms 54 //devices to terms
47 - protected IMap<byte[], Integer> terms; 55 + protected SMap<DeviceId, Integer> terms;
56 + //last-known cluster size, used for tie-breaking when partitioning occurs
57 + protected IAtomicLong clusterSize;
48 58
49 - //re-election related, disjoint-set structures:
50 - //device-nodes multiset of available nodes
51 - protected MultiMap<byte[], byte[]> standbys;
52 - //device-nodes multiset for nodes that have given up on device
53 - protected MultiMap<byte[], byte[]> unusable;
54 59
55 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 60 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
56 protected ClusterService clusterService; 61 protected ClusterService clusterService;
57 62
63 + @SuppressWarnings({ "unchecked", "rawtypes" })
58 @Override 64 @Override
59 @Activate 65 @Activate
60 public void activate() { 66 public void activate() {
61 super.activate(); 67 super.activate();
62 68
63 - masters = theInstance.getMap("masters"); 69 + this.serializer = new KryoSerializer() {
64 - terms = theInstance.getMap("terms"); 70 + @Override
65 - standbys = theInstance.getMultiMap("backups"); 71 + protected void setupKryoPool() {
66 - unusable = theInstance.getMultiMap("unusable"); 72 + serializerPool = KryoNamespace.newBuilder()
73 + .register(KryoNamespaces.API)
74 +
75 + .register(RoleValue.class, new RoleValueSerializer())
76 + .build()
77 + .populate(1);
78 + }
79 + };
67 80
68 - masters.addEntryListener(new RemoteMasterShipEventHandler(), true); 81 + roleMap = new SMap(theInstance.getMap("nodeRoles"), this.serializer);
82 + terms = new SMap(theInstance.getMap("terms"), this.serializer);
83 + clusterSize = theInstance.getAtomicLong("clustersize");
84 + roleMap.addEntryListener((new RemoteMasterShipEventHandler()), true);
69 85
70 log.info("Started"); 86 log.info("Started");
71 } 87 }
...@@ -77,12 +93,9 @@ implements MastershipStore { ...@@ -77,12 +93,9 @@ implements MastershipStore {
77 93
78 @Override 94 @Override
79 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) { 95 public MastershipRole getRole(NodeId nodeId, DeviceId deviceId) {
80 - byte[] did = serialize(deviceId); 96 + NodeId current = getNode(MASTER, deviceId);
81 - byte[] nid = serialize(nodeId);
82 -
83 - NodeId current = deserialize(masters.get(did));
84 if (current == null) { 97 if (current == null) {
85 - if (standbys.containsEntry(did, nid)) { 98 + if (isRole(STANDBY, nodeId, deviceId)) {
86 //was previously standby, or set to standby from master 99 //was previously standby, or set to standby from master
87 return MastershipRole.STANDBY; 100 return MastershipRole.STANDBY;
88 } else { 101 } else {
...@@ -101,55 +114,79 @@ implements MastershipStore { ...@@ -101,55 +114,79 @@ implements MastershipStore {
101 114
102 @Override 115 @Override
103 public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) { 116 public MastershipEvent setMaster(NodeId nodeId, DeviceId deviceId) {
104 - byte [] did = serialize(deviceId);
105 - byte [] nid = serialize(nodeId);
106 117
107 - ILock lock = theInstance.getLock(LOCK); 118 + MastershipRole role = getRole(nodeId, deviceId);
108 - lock.lock(); 119 + roleMap.lock(deviceId);
109 try { 120 try {
110 - MastershipRole role = getRole(nodeId, deviceId); 121 + RoleValue rv = getRoleValue(deviceId);
111 switch (role) { 122 switch (role) {
112 case MASTER: 123 case MASTER:
113 //reinforce mastership 124 //reinforce mastership
114 - evict(nid, did); 125 + rv.reassign(nodeId, STANDBY, NONE);
126 + roleMap.put(deviceId, rv);
115 return null; 127 return null;
116 case STANDBY: 128 case STANDBY:
117 - //make current master standby 129 + NodeId current = rv.get(MASTER);
118 - byte [] current = masters.get(did);
119 if (current != null) { 130 if (current != null) {
120 - backup(current, did); 131 + //backup and replace current master
132 + rv.reassign(nodeId, NONE, STANDBY);
133 + rv.replace(current, nodeId, MASTER);
134 + } else {
135 + //no master before so just add.
136 + rv.add(MASTER, nodeId);
121 } 137 }
122 - //assign specified node as new master 138 + rv.reassign(nodeId, STANDBY, NONE);
123 - masters.put(did, nid); 139 + roleMap.put(deviceId, rv);
124 - evict(nid, did); 140 + updateTerm(deviceId);
125 - updateTerm(did);
126 return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId); 141 return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
127 case NONE: 142 case NONE:
128 - masters.put(did, nid); 143 + rv.add(MASTER, nodeId);
129 - evict(nid, did); 144 + rv.reassign(nodeId, STANDBY, NONE);
130 - updateTerm(did); 145 + roleMap.put(deviceId, rv);
146 + updateTerm(deviceId);
131 return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId); 147 return new MastershipEvent(MASTER_CHANGED, deviceId, nodeId);
132 default: 148 default:
133 log.warn("unknown Mastership Role {}", role); 149 log.warn("unknown Mastership Role {}", role);
134 return null; 150 return null;
135 } 151 }
136 } finally { 152 } finally {
137 - lock.unlock(); 153 + roleMap.unlock(deviceId);
138 } 154 }
139 } 155 }
140 156
141 @Override 157 @Override
142 public NodeId getMaster(DeviceId deviceId) { 158 public NodeId getMaster(DeviceId deviceId) {
143 - return deserialize(masters.get(serialize(deviceId))); 159 + return getNode(MASTER, deviceId);
160 + }
161 +
162 +
163 + @Override
164 + public List<NodeId> getNodes(DeviceId deviceId) {
165 + List<NodeId> nodes = new LinkedList<>();
166 +
167 + //add current master to head - if there is one.
168 + roleMap.lock(deviceId);
169 + try {
170 + RoleValue rv = getRoleValue(deviceId);
171 + NodeId master = rv.get(MASTER);
172 + if (master != null) {
173 + nodes.add(master);
174 + }
175 + //We ignore NONE nodes.
176 + nodes.addAll(rv.nodesOfRole(STANDBY));
177 + return Collections.unmodifiableList(nodes);
178 + } finally {
179 + roleMap.unlock(deviceId);
180 + }
144 } 181 }
145 182
146 @Override 183 @Override
147 public Set<DeviceId> getDevices(NodeId nodeId) { 184 public Set<DeviceId> getDevices(NodeId nodeId) {
148 ImmutableSet.Builder<DeviceId> builder = ImmutableSet.builder(); 185 ImmutableSet.Builder<DeviceId> builder = ImmutableSet.builder();
149 186
150 - for (Map.Entry<byte[], byte[]> entry : masters.entrySet()) { 187 + for (Map.Entry<DeviceId, RoleValue> el : roleMap.entrySet()) {
151 - if (nodeId.equals(deserialize(entry.getValue()))) { 188 + if (nodeId.equals(el.getValue().get(MASTER))) {
152 - builder.add((DeviceId) deserialize(entry.getKey())); 189 + builder.add(el.getKey());
153 } 190 }
154 } 191 }
155 192
...@@ -159,26 +196,28 @@ implements MastershipStore { ...@@ -159,26 +196,28 @@ implements MastershipStore {
159 @Override 196 @Override
160 public MastershipRole requestRole(DeviceId deviceId) { 197 public MastershipRole requestRole(DeviceId deviceId) {
161 NodeId local = clusterService.getLocalNode().id(); 198 NodeId local = clusterService.getLocalNode().id();
162 - byte [] did = serialize(deviceId);
163 - byte [] lnid = serialize(local);
164 199
165 - ILock lock = theInstance.getLock(LOCK); 200 + roleMap.lock(deviceId);
166 - lock.lock();
167 try { 201 try {
202 + RoleValue rv = getRoleValue(deviceId);
168 MastershipRole role = getRole(local, deviceId); 203 MastershipRole role = getRole(local, deviceId);
169 switch (role) { 204 switch (role) {
170 case MASTER: 205 case MASTER:
171 - evict(lnid, did); 206 + rv.reassign(local, STANDBY, NONE);
207 + roleMap.put(deviceId, rv);
172 break; 208 break;
173 case STANDBY: 209 case STANDBY:
174 - backup(lnid, did); 210 + rv.reassign(local, NONE, STANDBY);
175 - terms.putIfAbsent(did, INIT); 211 + roleMap.put(deviceId, rv);
212 + terms.putIfAbsent(deviceId, INIT);
213 +
176 break; 214 break;
177 case NONE: 215 case NONE:
178 //claim mastership 216 //claim mastership
179 - masters.put(did, lnid); 217 + rv.add(MASTER, local);
180 - evict(lnid, did); 218 + rv.reassign(local, STANDBY, NONE);
181 - updateTerm(did); 219 + roleMap.put(deviceId, rv);
220 + updateTerm(deviceId);
182 role = MastershipRole.MASTER; 221 role = MastershipRole.MASTER;
183 break; 222 break;
184 default: 223 default:
...@@ -186,152 +225,178 @@ implements MastershipStore { ...@@ -186,152 +225,178 @@ implements MastershipStore {
186 } 225 }
187 return role; 226 return role;
188 } finally { 227 } finally {
189 - lock.unlock(); 228 + roleMap.unlock(deviceId);
190 } 229 }
191 } 230 }
192 231
193 @Override 232 @Override
194 public MastershipTerm getTermFor(DeviceId deviceId) { 233 public MastershipTerm getTermFor(DeviceId deviceId) {
195 - byte[] did = serialize(deviceId); 234 + RoleValue rv = getRoleValue(deviceId);
196 - if ((masters.get(did) == null) || 235 + if ((rv.get(MASTER) == null) || (terms.get(deviceId) == null)) {
197 - (terms.get(did) == null)) {
198 return null; 236 return null;
199 } 237 }
200 - return MastershipTerm.of( 238 + return MastershipTerm.of(rv.get(MASTER), terms.get(deviceId));
201 - (NodeId) deserialize(masters.get(did)), terms.get(did));
202 } 239 }
203 240
204 @Override 241 @Override
205 public MastershipEvent setStandby(NodeId nodeId, DeviceId deviceId) { 242 public MastershipEvent setStandby(NodeId nodeId, DeviceId deviceId) {
206 - byte [] did = serialize(deviceId);
207 - byte [] nid = serialize(nodeId);
208 MastershipEvent event = null; 243 MastershipEvent event = null;
209 244
210 - ILock lock = theInstance.getLock(LOCK); 245 + roleMap.lock(deviceId);
211 - lock.lock();
212 try { 246 try {
247 + RoleValue rv = getRoleValue(deviceId);
213 MastershipRole role = getRole(nodeId, deviceId); 248 MastershipRole role = getRole(nodeId, deviceId);
214 switch (role) { 249 switch (role) {
215 case MASTER: 250 case MASTER:
216 - event = reelect(nodeId, deviceId); 251 + event = reelect(nodeId, deviceId, rv);
217 - backup(nid, did); 252 + //fall through to reinforce role
218 - break;
219 case STANDBY: 253 case STANDBY:
220 //fall through to reinforce role 254 //fall through to reinforce role
221 case NONE: 255 case NONE:
222 - backup(nid, did); 256 + rv.reassign(nodeId, NONE, STANDBY);
257 + roleMap.put(deviceId, rv);
223 break; 258 break;
224 default: 259 default:
225 log.warn("unknown Mastership Role {}", role); 260 log.warn("unknown Mastership Role {}", role);
226 } 261 }
227 return event; 262 return event;
228 } finally { 263 } finally {
229 - lock.unlock(); 264 + roleMap.unlock(deviceId);
230 } 265 }
231 } 266 }
232 267
233 @Override 268 @Override
234 public MastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId) { 269 public MastershipEvent relinquishRole(NodeId nodeId, DeviceId deviceId) {
235 - byte [] did = serialize(deviceId);
236 - byte [] nid = serialize(nodeId);
237 MastershipEvent event = null; 270 MastershipEvent event = null;
238 271
239 - ILock lock = theInstance.getLock(LOCK); 272 + roleMap.lock(deviceId);
240 - lock.lock();
241 try { 273 try {
274 + RoleValue rv = getRoleValue(deviceId);
242 MastershipRole role = getRole(nodeId, deviceId); 275 MastershipRole role = getRole(nodeId, deviceId);
243 switch (role) { 276 switch (role) {
244 case MASTER: 277 case MASTER:
245 - event = reelect(nodeId, deviceId); 278 + event = reelect(nodeId, deviceId, rv);
246 - evict(nid, did); 279 + //fall through to reinforce relinquishment
247 - break;
248 case STANDBY: 280 case STANDBY:
249 //fall through to reinforce relinquishment 281 //fall through to reinforce relinquishment
250 case NONE: 282 case NONE:
251 - evict(nid, did); 283 + rv.reassign(nodeId, STANDBY, NONE);
284 + roleMap.put(deviceId, rv);
252 break; 285 break;
253 default: 286 default:
254 log.warn("unknown Mastership Role {}", role); 287 log.warn("unknown Mastership Role {}", role);
255 } 288 }
256 return event; 289 return event;
257 } finally { 290 } finally {
258 - lock.unlock(); 291 + roleMap.unlock(deviceId);
259 } 292 }
260 } 293 }
261 294
262 //helper to fetch a new master candidate for a given device. 295 //helper to fetch a new master candidate for a given device.
263 - private MastershipEvent reelect(NodeId current, DeviceId deviceId) { 296 + private MastershipEvent reelect(
264 - byte [] did = serialize(deviceId); 297 + NodeId current, DeviceId deviceId, RoleValue rv) {
265 - byte [] nid = serialize(current);
266 298
267 //if this is an queue it'd be neater. 299 //if this is an queue it'd be neater.
268 - byte [] backup = null; 300 + NodeId backup = null;
269 - for (byte [] n : standbys.get(serialize(deviceId))) { 301 + for (NodeId n : rv.nodesOfRole(STANDBY)) {
270 - if (!current.equals(deserialize(n))) { 302 + if (!current.equals(n)) {
271 backup = n; 303 backup = n;
272 break; 304 break;
273 } 305 }
274 } 306 }
275 307
276 if (backup == null) { 308 if (backup == null) {
277 - masters.remove(did, nid); 309 + log.info("{} giving up and going to NONE for {}", current, deviceId);
310 + rv.remove(MASTER, current);
311 + roleMap.put(deviceId, rv);
278 return null; 312 return null;
279 } else { 313 } else {
280 - masters.put(did, backup); 314 + log.info("{} trying to pass mastership for {} to {}", current, deviceId, backup);
281 - evict(backup, did); 315 + rv.replace(current, backup, MASTER);
282 - Integer term = terms.get(did); 316 + rv.reassign(backup, STANDBY, NONE);
283 - terms.put(did, ++term); 317 + roleMap.put(deviceId, rv);
284 - return new MastershipEvent( 318 + Integer term = terms.get(deviceId);
285 - MASTER_CHANGED, deviceId, (NodeId) deserialize(backup)); 319 + terms.put(deviceId, ++term);
320 + return new MastershipEvent(MASTER_CHANGED, deviceId, backup);
286 } 321 }
287 } 322 }
288 323
289 - //adds node to pool(s) of backups and moves them from unusable. 324 + //return the RoleValue structure for a device, or create one
290 - private void backup(byte [] nodeId, byte [] deviceId) { 325 + private RoleValue getRoleValue(DeviceId deviceId) {
291 - if (!standbys.containsEntry(deviceId, nodeId)) { 326 + RoleValue value = roleMap.get(deviceId);
292 - standbys.put(deviceId, nodeId); 327 + if (value == null) {
293 - } 328 + value = new RoleValue();
294 - if (unusable.containsEntry(deviceId, nodeId)) { 329 + roleMap.put(deviceId, value);
295 - unusable.remove(deviceId, nodeId);
296 } 330 }
331 + return value;
297 } 332 }
298 333
299 - //adds node to unusable and evicts it from backup pool. 334 + //get first applicable node out of store-unique structure.
300 - private void evict(byte [] nodeId, byte [] deviceId) { 335 + private NodeId getNode(MastershipRole role, DeviceId deviceId) {
301 - if (!unusable.containsEntry(deviceId, nodeId)) { 336 + RoleValue value = roleMap.get(deviceId);
302 - unusable.put(deviceId, nodeId); 337 + if (value != null) {
338 + return value.get(role);
303 } 339 }
304 - if (standbys.containsEntry(deviceId, nodeId)) { 340 + return null;
305 - standbys.remove(deviceId, nodeId); 341 + }
342 +
343 + //check if node is a certain role given a device
344 + private boolean isRole(
345 + MastershipRole role, NodeId nodeId, DeviceId deviceId) {
346 + RoleValue value = roleMap.get(deviceId);
347 + if (value != null) {
348 + return value.contains(role, nodeId);
306 } 349 }
350 + return false;
307 } 351 }
308 352
309 //adds or updates term information. 353 //adds or updates term information.
310 - private void updateTerm(byte [] deviceId) { 354 + private void updateTerm(DeviceId deviceId) {
311 - Integer term = terms.get(deviceId); 355 + terms.lock(deviceId);
312 - if (term == null) { 356 + try {
313 - terms.put(deviceId, INIT); 357 + Integer term = terms.get(deviceId);
314 - } else { 358 + if (term == null) {
315 - terms.put(deviceId, ++term); 359 + terms.put(deviceId, INIT);
360 + } else {
361 + terms.put(deviceId, ++term);
362 + }
363 + } finally {
364 + terms.unlock(deviceId);
316 } 365 }
317 } 366 }
318 367
319 - private class RemoteMasterShipEventHandler extends RemoteEventHandler<DeviceId, NodeId> { 368 + private class RemoteMasterShipEventHandler implements EntryListener<DeviceId, RoleValue> {
369 +
370 + @Override
371 + public void entryAdded(EntryEvent<DeviceId, RoleValue> event) {
372 + }
373 +
374 + @Override
375 + public void entryRemoved(EntryEvent<DeviceId, RoleValue> event) {
376 + }
377 +
378 + @Override
379 + public void entryUpdated(EntryEvent<DeviceId, RoleValue> event) {
380 + NodeId myId = clusterService.getLocalNode().id();
381 + NodeId node = event.getValue().get(MASTER);
382 + if (myId.equals(node)) {
383 + // XXX or do we just let it get sent and caught by ourself?
384 + return;
385 + }
386 + notifyDelegate(new MastershipEvent(
387 + MASTER_CHANGED, event.getKey(), event.getValue().get(MASTER)));
388 + }
320 389
321 @Override 390 @Override
322 - protected void onAdd(DeviceId deviceId, NodeId nodeId) { 391 + public void entryEvicted(EntryEvent<DeviceId, RoleValue> event) {
323 - notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId));
324 } 392 }
325 393
326 @Override 394 @Override
327 - protected void onRemove(DeviceId deviceId, NodeId nodeId) { 395 + public void mapEvicted(MapEvent event) {
328 - //notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId));
329 } 396 }
330 397
331 @Override 398 @Override
332 - protected void onUpdate(DeviceId deviceId, NodeId oldNodeId, NodeId nodeId) { 399 + public void mapCleared(MapEvent event) {
333 - //only addition indicates a change in mastership
334 - //notifyDelegate(new MastershipEvent(MASTER_CHANGED, deviceId, nodeId));
335 } 400 }
336 } 401 }
337 402
......
1 +package org.onlab.onos.store.mastership.impl;
2 +
3 +import java.util.Collections;
4 +import java.util.HashMap;
5 +import java.util.LinkedList;
6 +import java.util.List;
7 +import java.util.Map;
8 +
9 +import org.onlab.onos.cluster.NodeId;
10 +import org.onlab.onos.net.MastershipRole;
11 +
12 +/**
13 + * A structure that holds node mastership roles associated with a
14 + * {@link DeviceId}. This structure needs to be locked through IMap.
15 + */
16 +public class RoleValue {
17 +
18 + protected Map<MastershipRole, List<NodeId>> value = new HashMap<>();
19 +
20 + public RoleValue() {
21 + value.put(MastershipRole.MASTER, new LinkedList<NodeId>());
22 + value.put(MastershipRole.STANDBY, new LinkedList<NodeId>());
23 + value.put(MastershipRole.NONE, new LinkedList<NodeId>());
24 + }
25 +
26 + public Map<MastershipRole, List<NodeId>> value() {
27 + return Collections.unmodifiableMap(value);
28 + }
29 +
30 + public List<NodeId> nodesOfRole(MastershipRole type) {
31 + return value.get(type);
32 + }
33 +
34 + public NodeId get(MastershipRole type) {
35 + return value.get(type).isEmpty() ? null : value.get(type).get(0);
36 + }
37 +
38 + public boolean contains(MastershipRole type, NodeId nodeId) {
39 + return value.get(type).contains(nodeId);
40 + }
41 +
42 + /**
43 + * Associates a node to a certain role.
44 + *
45 + * @param type the role
46 + * @param nodeId the node ID of the node to associate
47 + */
48 + public void add(MastershipRole type, NodeId nodeId) {
49 + List<NodeId> nodes = value.get(type);
50 +
51 + if (!nodes.contains(nodeId)) {
52 + nodes.add(nodeId);
53 + }
54 + }
55 +
56 + /**
57 + * Removes a node from a certain role.
58 + *
59 + * @param type the role
60 + * @param nodeId the ID of the node to remove
61 + * @return
62 + */
63 + public boolean remove(MastershipRole type, NodeId nodeId) {
64 + List<NodeId> nodes = value.get(type);
65 + if (!nodes.isEmpty()) {
66 + return nodes.remove(nodeId);
67 + } else {
68 + return false;
69 + }
70 + }
71 +
72 + /**
73 + * Reassigns a node from one role to another. If the node was not of the
74 + * old role, it will still be assigned the new role.
75 + *
76 + * @param nodeId the Node ID of node changing roles
77 + * @param from the old role
78 + * @param to the new role
79 + */
80 + // might want to add anyways as default behavior
81 + public void reassign(NodeId nodeId, MastershipRole from, MastershipRole to) {
82 + remove(from, nodeId);
83 + add(to, nodeId);
84 + }
85 +
86 + /**
87 + * Replaces a node in one role with another node. Even if there is no node to
88 + * replace, the new node is associated to the role.
89 + *
90 + * @param from the old NodeId to replace
91 + * @param to the new NodeId
92 + * @param type the role associated with the old NodeId
93 + */
94 + // might want to add anyways as default behavior
95 + public void replace(NodeId from, NodeId to, MastershipRole type) {
96 + remove(type, from);
97 + add(type, to);
98 + }
99 +
100 + @Override
101 + public String toString() {
102 + final StringBuilder builder = new StringBuilder();
103 + for (Map.Entry<MastershipRole, List<NodeId>> el : value.entrySet()) {
104 + builder.append(el.getKey().toString()).append(": [");
105 + for (NodeId n : el.getValue()) {
106 + builder.append(n);
107 + }
108 + builder.append("]\n");
109 + }
110 + return builder.toString();
111 + }
112 +}
1 +package org.onlab.onos.store.mastership.impl;
2 +
3 +import java.util.List;
4 +import java.util.Map;
5 +
6 +import org.onlab.onos.cluster.NodeId;
7 +import org.onlab.onos.net.MastershipRole;
8 +
9 +import com.esotericsoftware.kryo.Kryo;
10 +import com.esotericsoftware.kryo.Serializer;
11 +import com.esotericsoftware.kryo.io.Input;
12 +import com.esotericsoftware.kryo.io.Output;
13 +
14 +/**
15 + * Serializer for RoleValues used by {@link DistributedMastershipStore}.
16 + */
17 +public class RoleValueSerializer extends Serializer<RoleValue> {
18 +
19 + //RoleValues are assumed to hold a Map of MastershipRoles (an enum)
20 + //to a List of NodeIds.
21 +
22 + @Override
23 + public RoleValue read(Kryo kryo, Input input, Class<RoleValue> type) {
24 + RoleValue rv = new RoleValue();
25 + int size = input.readInt();
26 + for (int i = 0; i < size; i++) {
27 + MastershipRole role = MastershipRole.values()[input.readInt()];
28 + int s = input.readInt();
29 + for (int j = 0; j < s; j++) {
30 + rv.add(role, new NodeId(input.readString()));
31 + }
32 + }
33 + return rv;
34 + }
35 +
36 + @Override
37 + public void write(Kryo kryo, Output output, RoleValue type) {
38 + output.writeInt(type.value().size());
39 +
40 + for (Map.Entry<MastershipRole, List<NodeId>> el :
41 + type.value().entrySet()) {
42 + output.writeInt(el.getKey().ordinal());
43 +
44 + List<NodeId> nodes = el.getValue();
45 + output.writeInt(nodes.size());
46 + for (NodeId n : nodes) {
47 + output.writeString(n.toString());
48 + }
49 + }
50 + }
51 +
52 +}
...@@ -27,6 +27,7 @@ import org.onlab.onos.mastership.MastershipStoreDelegate; ...@@ -27,6 +27,7 @@ import org.onlab.onos.mastership.MastershipStoreDelegate;
27 import org.onlab.onos.mastership.MastershipTerm; 27 import org.onlab.onos.mastership.MastershipTerm;
28 import org.onlab.onos.mastership.MastershipEvent.Type; 28 import org.onlab.onos.mastership.MastershipEvent.Type;
29 import org.onlab.onos.net.DeviceId; 29 import org.onlab.onos.net.DeviceId;
30 +import org.onlab.onos.net.MastershipRole;
30 import org.onlab.onos.store.common.StoreManager; 31 import org.onlab.onos.store.common.StoreManager;
31 import org.onlab.onos.store.common.StoreService; 32 import org.onlab.onos.store.common.StoreService;
32 import org.onlab.onos.store.common.TestStoreManager; 33 import org.onlab.onos.store.common.TestStoreManager;
...@@ -101,7 +102,7 @@ public class DistributedMastershipStoreTest { ...@@ -101,7 +102,7 @@ public class DistributedMastershipStoreTest {
101 102
102 @Test 103 @Test
103 public void getMaster() { 104 public void getMaster() {
104 - assertTrue("wrong store state:", dms.masters.isEmpty()); 105 + assertTrue("wrong store state:", dms.roleMap.isEmpty());
105 106
106 testStore.put(DID1, N1, true, false, false); 107 testStore.put(DID1, N1, true, false, false);
107 assertEquals("wrong master:", N1, dms.getMaster(DID1)); 108 assertEquals("wrong master:", N1, dms.getMaster(DID1));
...@@ -110,12 +111,11 @@ public class DistributedMastershipStoreTest { ...@@ -110,12 +111,11 @@ public class DistributedMastershipStoreTest {
110 111
111 @Test 112 @Test
112 public void getDevices() { 113 public void getDevices() {
113 - assertTrue("wrong store state:", dms.masters.isEmpty()); 114 + assertTrue("wrong store state:", dms.roleMap.isEmpty());
114 115
115 testStore.put(DID1, N1, true, false, false); 116 testStore.put(DID1, N1, true, false, false);
116 testStore.put(DID2, N1, true, false, false); 117 testStore.put(DID2, N1, true, false, false);
117 testStore.put(DID3, N2, true, false, false); 118 testStore.put(DID3, N2, true, false, false);
118 -
119 assertEquals("wrong devices", 119 assertEquals("wrong devices",
120 Sets.newHashSet(DID1, DID2), dms.getDevices(N1)); 120 Sets.newHashSet(DID1, DID2), dms.getDevices(N1));
121 } 121 }
...@@ -161,7 +161,7 @@ public class DistributedMastershipStoreTest { ...@@ -161,7 +161,7 @@ public class DistributedMastershipStoreTest {
161 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID2).type()); 161 assertEquals("wrong event:", Type.MASTER_CHANGED, dms.setMaster(N2, DID2).type());
162 assertEquals("wrong term", MastershipTerm.of(N2, 0), dms.getTermFor(DID2)); 162 assertEquals("wrong term", MastershipTerm.of(N2, 0), dms.getTermFor(DID2));
163 //disconnect and reconnect - sign of failing re-election or single-instance channel 163 //disconnect and reconnect - sign of failing re-election or single-instance channel
164 - testStore.reset(true, false, false); 164 + dms.roleMap.clear();
165 dms.setMaster(N2, DID2); 165 dms.setMaster(N2, DID2);
166 assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID2)); 166 assertEquals("wrong term", MastershipTerm.of(N2, 1), dms.getTermFor(DID2));
167 } 167 }
...@@ -191,13 +191,15 @@ public class DistributedMastershipStoreTest { ...@@ -191,13 +191,15 @@ public class DistributedMastershipStoreTest {
191 assertEquals("wrong role for node:", NONE, dms.getRole(N2, DID1)); 191 assertEquals("wrong role for node:", NONE, dms.getRole(N2, DID1));
192 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID1)); 192 assertEquals("wrong role for node:", NONE, dms.getRole(N1, DID1));
193 193
194 - assertEquals("wrong number of retired nodes", 2, dms.unusable.size()); 194 + assertEquals("wrong number of retired nodes", 2,
195 + dms.roleMap.get(DID1).nodesOfRole(NONE).size());
195 196
196 //bring nodes back 197 //bring nodes back
197 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1)); 198 assertEquals("wrong role for NONE:", MASTER, dms.requestRole(DID1));
198 testStore.setCurrent(CN1); 199 testStore.setCurrent(CN1);
199 assertEquals("wrong role for NONE:", STANDBY, dms.requestRole(DID1)); 200 assertEquals("wrong role for NONE:", STANDBY, dms.requestRole(DID1));
200 - assertEquals("wrong number of backup nodes", 1, dms.standbys.size()); 201 + assertEquals("wrong number of backup nodes", 1,
202 + dms.roleMap.get(DID1).nodesOfRole(STANDBY).size());
201 203
202 //NONE - nothing happens 204 //NONE - nothing happens
203 assertNull("wrong event:", dms.relinquishRole(N1, DID2)); 205 assertNull("wrong event:", dms.relinquishRole(N1, DID2));
...@@ -238,55 +240,44 @@ public class DistributedMastershipStoreTest { ...@@ -238,55 +240,44 @@ public class DistributedMastershipStoreTest {
238 //helper to populate master/backup structures 240 //helper to populate master/backup structures
239 public void put(DeviceId dev, NodeId node, 241 public void put(DeviceId dev, NodeId node,
240 boolean master, boolean backup, boolean term) { 242 boolean master, boolean backup, boolean term) {
241 - byte [] n = serialize(node); 243 + RoleValue rv = dms.roleMap.get(dev);
242 - byte [] d = serialize(dev); 244 + if (rv == null) {
245 + rv = new RoleValue();
246 + }
243 247
244 if (master) { 248 if (master) {
245 - dms.masters.put(d, n); 249 + rv.add(MASTER, node);
246 - dms.unusable.put(d, n); 250 + rv.reassign(node, STANDBY, NONE);
247 - dms.standbys.remove(d, n);
248 } 251 }
249 if (backup) { 252 if (backup) {
250 - dms.standbys.put(d, n); 253 + rv.add(STANDBY, node);
251 - dms.masters.remove(d, n); 254 + rv.remove(MASTER, node);
252 - dms.unusable.remove(d, n); 255 + rv.remove(NONE, node);
253 } 256 }
254 if (term) { 257 if (term) {
255 - dms.terms.put(d, 0); 258 + dms.terms.put(dev, 0);
256 } 259 }
260 + dms.roleMap.put(dev, rv);
257 } 261 }
258 262
259 //a dumb utility function. 263 //a dumb utility function.
260 public void dump() { 264 public void dump() {
261 - System.out.println("standbys"); 265 + for (Map.Entry<DeviceId, RoleValue> el : dms.roleMap.entrySet()) {
262 - for (Map.Entry<byte [], byte []> e : standbys.entrySet()) { 266 + System.out.println("DID: " + el.getKey());
263 - System.out.println(deserialize(e.getKey()) + ":" + deserialize(e.getValue())); 267 + for (MastershipRole role : MastershipRole.values()) {
264 - } 268 + System.out.println("\t" + role.toString() + ":");
265 - System.out.println("unusable"); 269 + for (NodeId n : el.getValue().nodesOfRole(role)) {
266 - for (Map.Entry<byte [], byte []> e : unusable.entrySet()) { 270 + System.out.println("\t\t" + n);
267 - System.out.println(deserialize(e.getKey()) + ":" + deserialize(e.getValue())); 271 + }
268 - } 272 + }
269 - }
270 -
271 - //clears structures
272 - public void reset(boolean store, boolean backup, boolean term) {
273 - if (store) {
274 - dms.masters.clear();
275 - dms.unusable.clear();
276 - }
277 - if (backup) {
278 - dms.standbys.clear();
279 - }
280 - if (term) {
281 - dms.terms.clear();
282 } 273 }
283 } 274 }
284 275
285 //increment term for a device 276 //increment term for a device
286 public void increment(DeviceId dev) { 277 public void increment(DeviceId dev) {
287 - Integer t = dms.terms.get(serialize(dev)); 278 + Integer t = dms.terms.get(dev);
288 if (t != null) { 279 if (t != null) {
289 - dms.terms.put(serialize(dev), ++t); 280 + dms.terms.put(dev, ++t);
290 } 281 }
291 } 282 }
292 283
......
1 +package org.onlab.onos.store.mastership.impl;
2 +
3 +import static org.junit.Assert.assertEquals;
4 +import static org.junit.Assert.assertTrue;
5 +import static org.onlab.onos.net.MastershipRole.*;
6 +
7 +import org.junit.Test;
8 +import org.onlab.onos.cluster.NodeId;
9 +
10 +import com.google.common.collect.Sets;
11 +
12 +public class RoleValueTest {
13 +
14 + private static final RoleValue RV = new RoleValue();
15 +
16 + private static final NodeId NID1 = new NodeId("node1");
17 + private static final NodeId NID2 = new NodeId("node2");
18 + private static final NodeId NID3 = new NodeId("node3");
19 +
20 + @Test
21 + public void add() {
22 + assertEquals("faulty initialization: ", 3, RV.value.size());
23 + RV.add(MASTER, NID1);
24 + RV.add(STANDBY, NID2);
25 + RV.add(STANDBY, NID3);
26 +
27 + assertEquals("wrong nodeID: ", NID1, RV.get(MASTER));
28 + assertTrue("wrong nodeIDs: ",
29 + Sets.newHashSet(NID3, NID2).containsAll(RV.nodesOfRole(STANDBY)));
30 + }
31 +}
...@@ -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 static com.google.common.base.Predicates.notNull;
4 -
5 -import com.google.common.base.Optional;
6 -import com.google.common.cache.LoadingCache;
7 -import com.google.common.collect.FluentIterable;
8 -import com.google.common.collect.ImmutableList;
9 -import com.google.common.collect.ImmutableSet;
10 -import com.google.common.collect.ImmutableSet.Builder;
11 -import com.hazelcast.core.IMap;
12 -import com.hazelcast.core.ISet;
13 -
14 -import org.apache.felix.scr.annotations.Activate;
15 -import org.apache.felix.scr.annotations.Component;
16 -import org.apache.felix.scr.annotations.Deactivate;
17 -import org.apache.felix.scr.annotations.Service;
18 -import org.onlab.onos.net.DefaultDevice;
19 -import org.onlab.onos.net.DefaultPort;
20 -import org.onlab.onos.net.Device;
21 -import org.onlab.onos.net.DeviceId;
22 -import org.onlab.onos.net.Port;
23 -import org.onlab.onos.net.PortNumber;
24 -import org.onlab.onos.net.device.DeviceDescription;
25 -import org.onlab.onos.net.device.DeviceEvent;
26 -import org.onlab.onos.net.device.DeviceStore;
27 -import org.onlab.onos.net.device.DeviceStoreDelegate;
28 -import org.onlab.onos.net.device.PortDescription;
29 -import org.onlab.onos.net.provider.ProviderId;
30 -import org.onlab.onos.store.common.AbsentInvalidatingLoadingCache;
31 -import org.onlab.onos.store.common.AbstractHazelcastStore;
32 -import org.onlab.onos.store.common.OptionalCacheLoader;
33 -import org.slf4j.Logger;
34 -
35 -import java.util.ArrayList;
36 -import java.util.Collections;
37 -import java.util.HashMap;
38 -import java.util.HashSet;
39 -import java.util.Iterator;
40 -import java.util.List;
41 -import java.util.Map;
42 -import java.util.Objects;
43 -import java.util.Set;
44 -
45 -import static com.google.common.base.Preconditions.checkArgument;
46 -import static com.google.common.cache.CacheBuilder.newBuilder;
47 -import static org.onlab.onos.net.device.DeviceEvent.Type.*;
48 -import static org.slf4j.LoggerFactory.getLogger;
49 -
50 -//TODO: Add support for multiple provider and annotations
51 -/**
52 - * Manages inventory of infrastructure devices using Hazelcast-backed map.
53 - */
54 -@Component(immediate = true)
55 -@Service
56 -public class DistributedDeviceStore
57 - extends AbstractHazelcastStore<DeviceEvent, DeviceStoreDelegate>
58 - implements DeviceStore {
59 -
60 - private final Logger log = getLogger(getClass());
61 -
62 - public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
63 -
64 - // private IMap<DeviceId, DefaultDevice> cache;
65 - private IMap<byte[], byte[]> rawDevices;
66 - private LoadingCache<DeviceId, Optional<DefaultDevice>> devices;
67 -
68 - // private ISet<DeviceId> availableDevices;
69 - private ISet<byte[]> availableDevices;
70 -
71 - // TODO DevicePorts is very inefficient consider restructuring.
72 - // private IMap<DeviceId, Map<PortNumber, Port>> devicePorts;
73 - private IMap<byte[], byte[]> rawDevicePorts;
74 - private LoadingCache<DeviceId, Optional<Map<PortNumber, Port>>> devicePorts;
75 -
76 - private String devicesListener;
77 -
78 - private String portsListener;
79 -
80 - @Override
81 - @Activate
82 - public void activate() {
83 - super.activate();
84 -
85 - // IMap event handler needs value
86 - final boolean includeValue = true;
87 -
88 - // TODO decide on Map name scheme to avoid collision
89 - rawDevices = theInstance.getMap("devices");
90 - final OptionalCacheLoader<DeviceId, DefaultDevice> deviceLoader
91 - = new OptionalCacheLoader<>(serializer, rawDevices);
92 - devices = new AbsentInvalidatingLoadingCache<>(newBuilder().build(deviceLoader));
93 - // refresh/populate cache based on notification from other instance
94 - devicesListener = rawDevices.addEntryListener(new RemoteDeviceEventHandler(devices), includeValue);
95 -
96 - // TODO cache availableDevices
97 - availableDevices = theInstance.getSet("availableDevices");
98 -
99 - rawDevicePorts = theInstance.getMap("devicePorts");
100 - final OptionalCacheLoader<DeviceId, Map<PortNumber, Port>> devicePortLoader
101 - = new OptionalCacheLoader<>(serializer, rawDevicePorts);
102 - devicePorts = new AbsentInvalidatingLoadingCache<>(newBuilder().build(devicePortLoader));
103 - // refresh/populate cache based on notification from other instance
104 - portsListener = rawDevicePorts.addEntryListener(new RemotePortEventHandler(devicePorts), includeValue);
105 -
106 - loadDeviceCache();
107 - loadDevicePortsCache();
108 -
109 - log.info("Started");
110 - }
111 -
112 - @Deactivate
113 - public void deactivate() {
114 - rawDevicePorts.removeEntryListener(portsListener);
115 - rawDevices.removeEntryListener(devicesListener);
116 - log.info("Stopped");
117 - }
118 -
119 - @Override
120 - public int getDeviceCount() {
121 - return devices.asMap().size();
122 - }
123 -
124 - @Override
125 - public Iterable<Device> getDevices() {
126 - // TODO builder v.s. copyOf. Guava semms to be using copyOf?
127 - Builder<Device> builder = ImmutableSet.builder();
128 - for (Optional<DefaultDevice> e : devices.asMap().values()) {
129 - if (e.isPresent()) {
130 - builder.add(e.get());
131 - }
132 - }
133 - return builder.build();
134 - }
135 -
136 - private void loadDeviceCache() {
137 - for (byte[] keyBytes : rawDevices.keySet()) {
138 - final DeviceId id = deserialize(keyBytes);
139 - devices.refresh(id);
140 - }
141 - }
142 -
143 - private void loadDevicePortsCache() {
144 - for (byte[] keyBytes : rawDevicePorts.keySet()) {
145 - final DeviceId id = deserialize(keyBytes);
146 - devicePorts.refresh(id);
147 - }
148 - }
149 -
150 - @Override
151 - public Device getDevice(DeviceId deviceId) {
152 - // TODO revisit if ignoring exception is safe.
153 - return devices.getUnchecked(deviceId).orNull();
154 - }
155 -
156 - @Override
157 - public DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
158 - DeviceDescription deviceDescription) {
159 - DefaultDevice device = devices.getUnchecked(deviceId).orNull();
160 - if (device == null) {
161 - return createDevice(providerId, deviceId, deviceDescription);
162 - }
163 - return updateDevice(providerId, device, deviceDescription);
164 - }
165 -
166 - // Creates the device and returns the appropriate event if necessary.
167 - private DeviceEvent createDevice(ProviderId providerId, DeviceId deviceId,
168 - DeviceDescription desc) {
169 - DefaultDevice device = new DefaultDevice(providerId, deviceId, desc.type(),
170 - desc.manufacturer(),
171 - desc.hwVersion(), desc.swVersion(),
172 - desc.serialNumber());
173 -
174 - synchronized (this) {
175 - final byte[] deviceIdBytes = serialize(deviceId);
176 - rawDevices.put(deviceIdBytes, serialize(device));
177 - devices.put(deviceId, Optional.of(device));
178 -
179 - availableDevices.add(deviceIdBytes);
180 - }
181 - return new DeviceEvent(DEVICE_ADDED, device, null);
182 - }
183 -
184 - // Updates the device and returns the appropriate event if necessary.
185 - private DeviceEvent updateDevice(ProviderId providerId, DefaultDevice device,
186 - DeviceDescription desc) {
187 - // We allow only certain attributes to trigger update
188 - if (!Objects.equals(device.hwVersion(), desc.hwVersion()) ||
189 - !Objects.equals(device.swVersion(), desc.swVersion())) {
190 -
191 - DefaultDevice updated = new DefaultDevice(providerId, device.id(),
192 - desc.type(),
193 - desc.manufacturer(),
194 - desc.hwVersion(),
195 - desc.swVersion(),
196 - desc.serialNumber());
197 - synchronized (this) {
198 - final byte[] deviceIdBytes = serialize(device.id());
199 - rawDevices.put(deviceIdBytes, serialize(updated));
200 - devices.put(device.id(), Optional.of(updated));
201 - availableDevices.add(serialize(device.id()));
202 - }
203 - return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, updated, null);
204 - }
205 -
206 - // Otherwise merely attempt to change availability
207 - synchronized (this) {
208 - boolean added = availableDevices.add(serialize(device.id()));
209 - return !added ? null :
210 - new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
211 - }
212 - }
213 -
214 - @Override
215 - public DeviceEvent markOffline(DeviceId deviceId) {
216 - synchronized (this) {
217 - Device device = devices.getUnchecked(deviceId).orNull();
218 - boolean removed = device != null && availableDevices.remove(serialize(deviceId));
219 - return !removed ? null :
220 - new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
221 - }
222 - }
223 -
224 - @Override
225 - public List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
226 - List<PortDescription> portDescriptions) {
227 - List<DeviceEvent> events = new ArrayList<>();
228 - synchronized (this) {
229 - Device device = devices.getUnchecked(deviceId).orNull();
230 - checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
231 - Map<PortNumber, Port> ports = getPortMap(deviceId);
232 -
233 - // Add new ports
234 - Set<PortNumber> processed = new HashSet<>();
235 - for (PortDescription portDescription : portDescriptions) {
236 - Port port = ports.get(portDescription.portNumber());
237 - events.add(port == null ?
238 - createPort(device, portDescription, ports) :
239 - updatePort(device, port, portDescription, ports));
240 - processed.add(portDescription.portNumber());
241 - }
242 -
243 - updatePortMap(deviceId, ports);
244 -
245 - events.addAll(pruneOldPorts(device, ports, processed));
246 - }
247 - return FluentIterable.from(events).filter(notNull()).toList();
248 - }
249 -
250 - // Creates a new port based on the port description adds it to the map and
251 - // Returns corresponding event.
252 - //@GuardedBy("this")
253 - private DeviceEvent createPort(Device device, PortDescription portDescription,
254 - Map<PortNumber, Port> ports) {
255 - DefaultPort port = new DefaultPort(device, portDescription.portNumber(),
256 - portDescription.isEnabled());
257 - ports.put(port.number(), port);
258 - updatePortMap(device.id(), ports);
259 - return new DeviceEvent(PORT_ADDED, device, port);
260 - }
261 -
262 - // Checks if the specified port requires update and if so, it replaces the
263 - // existing entry in the map and returns corresponding event.
264 - //@GuardedBy("this")
265 - private DeviceEvent updatePort(Device device, Port port,
266 - PortDescription portDescription,
267 - Map<PortNumber, Port> ports) {
268 - if (port.isEnabled() != portDescription.isEnabled()) {
269 - DefaultPort updatedPort =
270 - new DefaultPort(device, portDescription.portNumber(),
271 - portDescription.isEnabled());
272 - ports.put(port.number(), updatedPort);
273 - updatePortMap(device.id(), ports);
274 - return new DeviceEvent(PORT_UPDATED, device, updatedPort);
275 - }
276 - return null;
277 - }
278 -
279 - // Prunes the specified list of ports based on which ports are in the
280 - // processed list and returns list of corresponding events.
281 - //@GuardedBy("this")
282 - private List<DeviceEvent> pruneOldPorts(Device device,
283 - Map<PortNumber, Port> ports,
284 - Set<PortNumber> processed) {
285 - List<DeviceEvent> events = new ArrayList<>();
286 - Iterator<PortNumber> iterator = ports.keySet().iterator();
287 - while (iterator.hasNext()) {
288 - PortNumber portNumber = iterator.next();
289 - if (!processed.contains(portNumber)) {
290 - events.add(new DeviceEvent(PORT_REMOVED, device,
291 - ports.get(portNumber)));
292 - iterator.remove();
293 - }
294 - }
295 - if (!events.isEmpty()) {
296 - updatePortMap(device.id(), ports);
297 - }
298 - return events;
299 - }
300 -
301 - // Gets the map of ports for the specified device; if one does not already
302 - // exist, it creates and registers a new one.
303 - // WARN: returned value is a copy, changes made to the Map
304 - // needs to be written back using updatePortMap
305 - //@GuardedBy("this")
306 - private Map<PortNumber, Port> getPortMap(DeviceId deviceId) {
307 - Map<PortNumber, Port> ports = devicePorts.getUnchecked(deviceId).orNull();
308 - if (ports == null) {
309 - ports = new HashMap<>();
310 - // this probably is waste of time in most cases.
311 - updatePortMap(deviceId, ports);
312 - }
313 - return ports;
314 - }
315 -
316 - //@GuardedBy("this")
317 - private void updatePortMap(DeviceId deviceId, Map<PortNumber, Port> ports) {
318 - rawDevicePorts.put(serialize(deviceId), serialize(ports));
319 - devicePorts.put(deviceId, Optional.of(ports));
320 - }
321 -
322 - @Override
323 - public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
324 - PortDescription portDescription) {
325 - synchronized (this) {
326 - Device device = devices.getUnchecked(deviceId).orNull();
327 - checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
328 - Map<PortNumber, Port> ports = getPortMap(deviceId);
329 - Port port = ports.get(portDescription.portNumber());
330 - return updatePort(device, port, portDescription, ports);
331 - }
332 - }
333 -
334 - @Override
335 - public List<Port> getPorts(DeviceId deviceId) {
336 - Map<PortNumber, Port> ports = devicePorts.getUnchecked(deviceId).orNull();
337 - return ports == null ? Collections.<Port>emptyList() : ImmutableList.copyOf(ports.values());
338 - }
339 -
340 - @Override
341 - public Port getPort(DeviceId deviceId, PortNumber portNumber) {
342 - Map<PortNumber, Port> ports = devicePorts.getUnchecked(deviceId).orNull();
343 - return ports == null ? null : ports.get(portNumber);
344 - }
345 -
346 - @Override
347 - public boolean isAvailable(DeviceId deviceId) {
348 - return availableDevices.contains(serialize(deviceId));
349 - }
350 -
351 - @Override
352 - public DeviceEvent removeDevice(DeviceId deviceId) {
353 - synchronized (this) {
354 - byte[] deviceIdBytes = serialize(deviceId);
355 -
356 - // TODO conditional remove?
357 - Device device = deserialize(rawDevices.remove(deviceIdBytes));
358 - devices.invalidate(deviceId);
359 - return device == null ? null :
360 - new DeviceEvent(DEVICE_REMOVED, device, null);
361 - }
362 - }
363 -
364 - private class RemoteDeviceEventHandler extends RemoteCacheEventHandler<DeviceId, DefaultDevice> {
365 - public RemoteDeviceEventHandler(LoadingCache<DeviceId, Optional<DefaultDevice>> cache) {
366 - super(cache);
367 - }
368 -
369 - @Override
370 - protected void onAdd(DeviceId deviceId, DefaultDevice device) {
371 - notifyDelegate(new DeviceEvent(DEVICE_ADDED, device));
372 - }
373 -
374 - @Override
375 - protected void onRemove(DeviceId deviceId, DefaultDevice device) {
376 - notifyDelegate(new DeviceEvent(DEVICE_REMOVED, device));
377 - }
378 -
379 - @Override
380 - protected void onUpdate(DeviceId deviceId, DefaultDevice oldDevice, DefaultDevice device) {
381 - notifyDelegate(new DeviceEvent(DEVICE_UPDATED, device));
382 - }
383 - }
384 -
385 - private class RemotePortEventHandler extends RemoteCacheEventHandler<DeviceId, Map<PortNumber, Port>> {
386 - public RemotePortEventHandler(LoadingCache<DeviceId, Optional<Map<PortNumber, Port>>> cache) {
387 - super(cache);
388 - }
389 -
390 - @Override
391 - protected void onAdd(DeviceId deviceId, Map<PortNumber, Port> ports) {
392 -// notifyDelegate(new DeviceEvent(PORT_ADDED, getDevice(deviceId)));
393 - }
394 -
395 - @Override
396 - protected void onRemove(DeviceId deviceId, Map<PortNumber, Port> ports) {
397 -// notifyDelegate(new DeviceEvent(PORT_REMOVED, getDevice(deviceId)));
398 - }
399 -
400 - @Override
401 - protected void onUpdate(DeviceId deviceId, Map<PortNumber, Port> oldPorts, Map<PortNumber, Port> ports) {
402 -// notifyDelegate(new DeviceEvent(PORT_UPDATED, getDevice(deviceId)));
403 - }
404 - }
405 -
406 -
407 - // TODO cache serialized DeviceID if we suffer from serialization cost
408 -}
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 -/**
2 - *
3 - */
4 -package org.onlab.onos.store.device.impl;
5 -
6 -import static org.junit.Assert.*;
7 -import static org.onlab.onos.net.Device.Type.SWITCH;
8 -import static org.onlab.onos.net.DeviceId.deviceId;
9 -import static org.onlab.onos.net.device.DeviceEvent.Type.*;
10 -
11 -import java.util.Arrays;
12 -import java.util.HashMap;
13 -import java.util.List;
14 -import java.util.Map;
15 -import java.util.Set;
16 -import java.util.concurrent.CountDownLatch;
17 -import java.util.concurrent.TimeUnit;
18 -
19 -import org.junit.After;
20 -import org.junit.AfterClass;
21 -import org.junit.Before;
22 -import org.junit.BeforeClass;
23 -import org.junit.Ignore;
24 -import org.junit.Test;
25 -import org.onlab.onos.net.Device;
26 -import org.onlab.onos.net.DeviceId;
27 -import org.onlab.onos.net.Port;
28 -import org.onlab.onos.net.PortNumber;
29 -import org.onlab.onos.net.device.DefaultDeviceDescription;
30 -import org.onlab.onos.net.device.DefaultPortDescription;
31 -import org.onlab.onos.net.device.DeviceDescription;
32 -import org.onlab.onos.net.device.DeviceEvent;
33 -import org.onlab.onos.net.device.DeviceStoreDelegate;
34 -import org.onlab.onos.net.device.PortDescription;
35 -import org.onlab.onos.net.provider.ProviderId;
36 -import org.onlab.onos.store.common.StoreManager;
37 -import org.onlab.onos.store.common.StoreService;
38 -import org.onlab.onos.store.common.TestStoreManager;
39 -import com.google.common.collect.Iterables;
40 -import com.google.common.collect.Sets;
41 -import com.hazelcast.config.Config;
42 -import com.hazelcast.core.Hazelcast;
43 -
44 -/**
45 - * Test of the Hazelcast based distributed DeviceStore implementation.
46 - */
47 -public class DistributedDeviceStoreTest {
48 -
49 - private static final ProviderId PID = new ProviderId("of", "foo");
50 - private static final DeviceId DID1 = deviceId("of:foo");
51 - private static final DeviceId DID2 = deviceId("of:bar");
52 - private static final String MFR = "whitebox";
53 - private static final String HW = "1.1.x";
54 - private static final String SW1 = "3.8.1";
55 - private static final String SW2 = "3.9.5";
56 - private static final String SN = "43311-12345";
57 -
58 - private static final PortNumber P1 = PortNumber.portNumber(1);
59 - private static final PortNumber P2 = PortNumber.portNumber(2);
60 - private static final PortNumber P3 = PortNumber.portNumber(3);
61 -
62 - private DistributedDeviceStore deviceStore;
63 -
64 - private StoreManager storeManager;
65 -
66 -
67 - @BeforeClass
68 - public static void setUpBeforeClass() throws Exception {
69 - }
70 -
71 - @AfterClass
72 - public static void tearDownAfterClass() throws Exception {
73 - }
74 -
75 -
76 - @Before
77 - public void setUp() throws Exception {
78 - // TODO should find a way to clean Hazelcast instance without shutdown.
79 - Config config = TestStoreManager.getTestConfig();
80 -
81 - storeManager = new TestStoreManager(Hazelcast.newHazelcastInstance(config));
82 - storeManager.activate();
83 -
84 - deviceStore = new TestDistributedDeviceStore(storeManager);
85 - deviceStore.activate();
86 - }
87 -
88 - @After
89 - public void tearDown() throws Exception {
90 - deviceStore.deactivate();
91 -
92 - storeManager.deactivate();
93 - }
94 -
95 - private void putDevice(DeviceId deviceId, String swVersion) {
96 - DeviceDescription description =
97 - new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
98 - HW, swVersion, SN);
99 - deviceStore.createOrUpdateDevice(PID, deviceId, description);
100 - }
101 -
102 - private static void assertDevice(DeviceId id, String swVersion, Device device) {
103 - assertNotNull(device);
104 - assertEquals(id, device.id());
105 - assertEquals(MFR, device.manufacturer());
106 - assertEquals(HW, device.hwVersion());
107 - assertEquals(swVersion, device.swVersion());
108 - assertEquals(SN, device.serialNumber());
109 - }
110 -
111 - @Test
112 - public final void testGetDeviceCount() {
113 - assertEquals("initialy empty", 0, deviceStore.getDeviceCount());
114 -
115 - putDevice(DID1, SW1);
116 - putDevice(DID2, SW2);
117 - putDevice(DID1, SW1);
118 -
119 - assertEquals("expect 2 uniq devices", 2, deviceStore.getDeviceCount());
120 - }
121 -
122 - @Test
123 - public final void testGetDevices() {
124 - assertEquals("initialy empty", 0, Iterables.size(deviceStore.getDevices()));
125 -
126 - putDevice(DID1, SW1);
127 - putDevice(DID2, SW2);
128 - putDevice(DID1, SW1);
129 -
130 - assertEquals("expect 2 uniq devices",
131 - 2, Iterables.size(deviceStore.getDevices()));
132 -
133 - Map<DeviceId, Device> devices = new HashMap<>();
134 - for (Device device : deviceStore.getDevices()) {
135 - devices.put(device.id(), device);
136 - }
137 -
138 - assertDevice(DID1, SW1, devices.get(DID1));
139 - assertDevice(DID2, SW2, devices.get(DID2));
140 -
141 - // add case for new node?
142 - }
143 -
144 - @Test
145 - public final void testGetDevice() {
146 -
147 - putDevice(DID1, SW1);
148 -
149 - assertDevice(DID1, SW1, deviceStore.getDevice(DID1));
150 - assertNull("DID2 shouldn't be there", deviceStore.getDevice(DID2));
151 - }
152 -
153 - @Test
154 - public final void testCreateOrUpdateDevice() {
155 - DeviceDescription description =
156 - new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
157 - HW, SW1, SN);
158 - DeviceEvent event = deviceStore.createOrUpdateDevice(PID, DID1, description);
159 - assertEquals(DEVICE_ADDED, event.type());
160 - assertDevice(DID1, SW1, event.subject());
161 -
162 - DeviceDescription description2 =
163 - new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
164 - HW, SW2, SN);
165 - DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
166 - assertEquals(DEVICE_UPDATED, event2.type());
167 - assertDevice(DID1, SW2, event2.subject());
168 -
169 - assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2));
170 - }
171 -
172 - @Test
173 - public final void testMarkOffline() {
174 -
175 - putDevice(DID1, SW1);
176 - assertTrue(deviceStore.isAvailable(DID1));
177 -
178 - DeviceEvent event = deviceStore.markOffline(DID1);
179 - assertEquals(DEVICE_AVAILABILITY_CHANGED, event.type());
180 - assertDevice(DID1, SW1, event.subject());
181 - assertFalse(deviceStore.isAvailable(DID1));
182 -
183 - DeviceEvent event2 = deviceStore.markOffline(DID1);
184 - assertNull("No change, no event", event2);
185 -}
186 -
187 - @Test
188 - public final void testUpdatePorts() {
189 - putDevice(DID1, SW1);
190 - List<PortDescription> pds = Arrays.<PortDescription>asList(
191 - new DefaultPortDescription(P1, true),
192 - new DefaultPortDescription(P2, true)
193 - );
194 -
195 - List<DeviceEvent> events = deviceStore.updatePorts(PID, DID1, pds);
196 -
197 - Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
198 - for (DeviceEvent event : events) {
199 - assertEquals(PORT_ADDED, event.type());
200 - assertDevice(DID1, SW1, event.subject());
201 - assertTrue("PortNumber is one of expected",
202 - expectedPorts.remove(event.port().number()));
203 - assertTrue("Port is enabled", event.port().isEnabled());
204 - }
205 - assertTrue("Event for all expectedport appeared", expectedPorts.isEmpty());
206 -
207 -
208 - List<PortDescription> pds2 = Arrays.<PortDescription>asList(
209 - new DefaultPortDescription(P1, false),
210 - new DefaultPortDescription(P2, true),
211 - new DefaultPortDescription(P3, true)
212 - );
213 -
214 - events = deviceStore.updatePorts(PID, DID1, pds2);
215 - assertFalse("event should be triggered", events.isEmpty());
216 - for (DeviceEvent event : events) {
217 - PortNumber num = event.port().number();
218 - if (P1.equals(num)) {
219 - assertEquals(PORT_UPDATED, event.type());
220 - assertDevice(DID1, SW1, event.subject());
221 - assertFalse("Port is disabled", event.port().isEnabled());
222 - } else if (P2.equals(num)) {
223 - fail("P2 event not expected.");
224 - } else if (P3.equals(num)) {
225 - assertEquals(PORT_ADDED, event.type());
226 - assertDevice(DID1, SW1, event.subject());
227 - assertTrue("Port is enabled", event.port().isEnabled());
228 - } else {
229 - fail("Unknown port number encountered: " + num);
230 - }
231 - }
232 -
233 - List<PortDescription> pds3 = Arrays.<PortDescription>asList(
234 - new DefaultPortDescription(P1, false),
235 - new DefaultPortDescription(P2, true)
236 - );
237 - events = deviceStore.updatePorts(PID, DID1, pds3);
238 - assertFalse("event should be triggered", events.isEmpty());
239 - for (DeviceEvent event : events) {
240 - PortNumber num = event.port().number();
241 - if (P1.equals(num)) {
242 - fail("P1 event not expected.");
243 - } else if (P2.equals(num)) {
244 - fail("P2 event not expected.");
245 - } else if (P3.equals(num)) {
246 - assertEquals(PORT_REMOVED, event.type());
247 - assertDevice(DID1, SW1, event.subject());
248 - assertTrue("Port was enabled", event.port().isEnabled());
249 - } else {
250 - fail("Unknown port number encountered: " + num);
251 - }
252 - }
253 -
254 - }
255 -
256 - @Test
257 - public final void testUpdatePortStatus() {
258 - putDevice(DID1, SW1);
259 - List<PortDescription> pds = Arrays.<PortDescription>asList(
260 - new DefaultPortDescription(P1, true)
261 - );
262 - deviceStore.updatePorts(PID, DID1, pds);
263 -
264 - DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
265 - new DefaultPortDescription(P1, false));
266 - assertEquals(PORT_UPDATED, event.type());
267 - assertDevice(DID1, SW1, event.subject());
268 - assertEquals(P1, event.port().number());
269 - assertFalse("Port is disabled", event.port().isEnabled());
270 - }
271 -
272 - @Test
273 - public final void testGetPorts() {
274 - putDevice(DID1, SW1);
275 - putDevice(DID2, SW1);
276 - List<PortDescription> pds = Arrays.<PortDescription>asList(
277 - new DefaultPortDescription(P1, true),
278 - new DefaultPortDescription(P2, true)
279 - );
280 - deviceStore.updatePorts(PID, DID1, pds);
281 -
282 - Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
283 - List<Port> ports = deviceStore.getPorts(DID1);
284 - for (Port port : ports) {
285 - assertTrue("Port is enabled", port.isEnabled());
286 - assertTrue("PortNumber is one of expected",
287 - expectedPorts.remove(port.number()));
288 - }
289 - assertTrue("Event for all expectedport appeared", expectedPorts.isEmpty());
290 -
291 -
292 - assertTrue("DID2 has no ports", deviceStore.getPorts(DID2).isEmpty());
293 - }
294 -
295 - @Test
296 - public final void testGetPort() {
297 - putDevice(DID1, SW1);
298 - putDevice(DID2, SW1);
299 - List<PortDescription> pds = Arrays.<PortDescription>asList(
300 - new DefaultPortDescription(P1, true),
301 - new DefaultPortDescription(P2, false)
302 - );
303 - deviceStore.updatePorts(PID, DID1, pds);
304 -
305 - Port port1 = deviceStore.getPort(DID1, P1);
306 - assertEquals(P1, port1.number());
307 - assertTrue("Port is enabled", port1.isEnabled());
308 -
309 - Port port2 = deviceStore.getPort(DID1, P2);
310 - assertEquals(P2, port2.number());
311 - assertFalse("Port is disabled", port2.isEnabled());
312 -
313 - Port port3 = deviceStore.getPort(DID1, P3);
314 - assertNull("P3 not expected", port3);
315 - }
316 -
317 - @Test
318 - public final void testRemoveDevice() {
319 - putDevice(DID1, SW1);
320 - putDevice(DID2, SW1);
321 -
322 - assertEquals(2, deviceStore.getDeviceCount());
323 -
324 - DeviceEvent event = deviceStore.removeDevice(DID1);
325 - assertEquals(DEVICE_REMOVED, event.type());
326 - assertDevice(DID1, SW1, event.subject());
327 -
328 - assertEquals(1, deviceStore.getDeviceCount());
329 - }
330 -
331 - // TODO add test for Port events when we have them
332 - @Ignore("Ignore until Delegate spec. is clear.")
333 - @Test
334 - public final void testEvents() throws InterruptedException {
335 - final CountDownLatch addLatch = new CountDownLatch(1);
336 - DeviceStoreDelegate checkAdd = new DeviceStoreDelegate() {
337 - @Override
338 - public void notify(DeviceEvent event) {
339 - assertEquals(DEVICE_ADDED, event.type());
340 - assertDevice(DID1, SW1, event.subject());
341 - addLatch.countDown();
342 - }
343 - };
344 - final CountDownLatch updateLatch = new CountDownLatch(1);
345 - DeviceStoreDelegate checkUpdate = new DeviceStoreDelegate() {
346 - @Override
347 - public void notify(DeviceEvent event) {
348 - assertEquals(DEVICE_UPDATED, event.type());
349 - assertDevice(DID1, SW2, event.subject());
350 - updateLatch.countDown();
351 - }
352 - };
353 - final CountDownLatch removeLatch = new CountDownLatch(1);
354 - DeviceStoreDelegate checkRemove = new DeviceStoreDelegate() {
355 - @Override
356 - public void notify(DeviceEvent event) {
357 - assertEquals(DEVICE_REMOVED, event.type());
358 - assertDevice(DID1, SW2, event.subject());
359 - removeLatch.countDown();
360 - }
361 - };
362 -
363 - DeviceDescription description =
364 - new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
365 - HW, SW1, SN);
366 - deviceStore.setDelegate(checkAdd);
367 - deviceStore.createOrUpdateDevice(PID, DID1, description);
368 - assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
369 -
370 -
371 - DeviceDescription description2 =
372 - new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
373 - HW, SW2, SN);
374 - deviceStore.unsetDelegate(checkAdd);
375 - deviceStore.setDelegate(checkUpdate);
376 - deviceStore.createOrUpdateDevice(PID, DID1, description2);
377 - assertTrue("Update event fired", updateLatch.await(1, TimeUnit.SECONDS));
378 -
379 - deviceStore.unsetDelegate(checkUpdate);
380 - deviceStore.setDelegate(checkRemove);
381 - deviceStore.removeDevice(DID1);
382 - assertTrue("Remove event fired", removeLatch.await(1, TimeUnit.SECONDS));
383 - }
384 -
385 - private class TestDistributedDeviceStore extends DistributedDeviceStore {
386 - public TestDistributedDeviceStore(StoreService storeService) {
387 - this.storeService = storeService;
388 - }
389 - }
390 -}
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 +}
1 package org.onlab.onos.store.serializers; 1 package org.onlab.onos.store.serializers;
2 2
3 -import org.onlab.util.KryoPool.FamilySerializer; 3 +import org.onlab.util.KryoNamespace.FamilySerializer;
4 4
5 import com.esotericsoftware.kryo.Kryo; 5 import com.esotericsoftware.kryo.Kryo;
6 import com.esotericsoftware.kryo.io.Input; 6 import com.esotericsoftware.kryo.io.Input;
......
...@@ -4,7 +4,7 @@ import java.util.Collections; ...@@ -4,7 +4,7 @@ import java.util.Collections;
4 import java.util.HashMap; 4 import java.util.HashMap;
5 import java.util.Map; 5 import java.util.Map;
6 6
7 -import org.onlab.util.KryoPool.FamilySerializer; 7 +import org.onlab.util.KryoNamespace.FamilySerializer;
8 8
9 import com.esotericsoftware.kryo.Kryo; 9 import com.esotericsoftware.kryo.Kryo;
10 import com.esotericsoftware.kryo.io.Input; 10 import com.esotericsoftware.kryo.io.Input;
......
...@@ -3,7 +3,7 @@ package org.onlab.onos.store.serializers; ...@@ -3,7 +3,7 @@ package org.onlab.onos.store.serializers;
3 import java.util.ArrayList; 3 import java.util.ArrayList;
4 import java.util.List; 4 import java.util.List;
5 5
6 -import org.onlab.util.KryoPool.FamilySerializer; 6 +import org.onlab.util.KryoNamespace.FamilySerializer;
7 7
8 import com.esotericsoftware.kryo.Kryo; 8 import com.esotericsoftware.kryo.Kryo;
9 import com.esotericsoftware.kryo.io.Input; 9 import com.esotericsoftware.kryo.io.Input;
......
...@@ -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,41 +17,52 @@ import org.onlab.onos.net.DefaultPort; ...@@ -17,41 +17,52 @@ 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;
23 import org.onlab.onos.net.Port; 24 import org.onlab.onos.net.Port;
24 import org.onlab.onos.net.PortNumber; 25 import org.onlab.onos.net.PortNumber;
25 import org.onlab.onos.net.device.DefaultDeviceDescription; 26 import org.onlab.onos.net.device.DefaultDeviceDescription;
26 import org.onlab.onos.net.device.DefaultPortDescription; 27 import org.onlab.onos.net.device.DefaultPortDescription;
28 +import org.onlab.onos.net.flow.DefaultFlowRule;
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;
34 +import org.onlab.packet.ChassisId;
30 import org.onlab.packet.IpAddress; 35 import org.onlab.packet.IpAddress;
31 import org.onlab.packet.IpPrefix; 36 import org.onlab.packet.IpPrefix;
32 -import org.onlab.util.KryoPool; 37 +import org.onlab.packet.MacAddress;
38 +import org.onlab.packet.VlanId;
39 +import org.onlab.util.KryoNamespace;
33 40
34 import com.google.common.collect.ImmutableList; 41 import com.google.common.collect.ImmutableList;
35 import com.google.common.collect.ImmutableMap; 42 import com.google.common.collect.ImmutableMap;
43 +import com.google.common.collect.ImmutableSet;
36 44
37 -public final class KryoPoolUtil { 45 +public final class KryoNamespaces {
38 46
39 /** 47 /**
40 - * KryoPool which can serialize ON.lab misc classes. 48 + * KryoNamespace which can serialize ON.lab misc classes.
41 */ 49 */
42 - public static final KryoPool MISC = KryoPool.newBuilder() 50 + public static final KryoNamespace MISC = KryoNamespace.newBuilder()
43 .register(IpPrefix.class, new IpPrefixSerializer()) 51 .register(IpPrefix.class, new IpPrefixSerializer())
44 .register(IpAddress.class, new IpAddressSerializer()) 52 .register(IpAddress.class, new IpAddressSerializer())
53 + .register(MacAddress.class, new MacAddressSerializer())
54 + .register(VlanId.class)
45 .build(); 55 .build();
46 56
47 // TODO: Populate other classes 57 // TODO: Populate other classes
48 /** 58 /**
49 - * KryoPool which can serialize API bundle classes. 59 + * KryoNamespace which can serialize API bundle classes.
50 */ 60 */
51 - public static final KryoPool API = KryoPool.newBuilder() 61 + public static final KryoNamespace API = KryoNamespace.newBuilder()
52 .register(MISC) 62 .register(MISC)
53 .register(ImmutableMap.class, new ImmutableMapSerializer()) 63 .register(ImmutableMap.class, new ImmutableMapSerializer())
54 .register(ImmutableList.class, new ImmutableListSerializer()) 64 .register(ImmutableList.class, new ImmutableListSerializer())
65 + .register(ImmutableSet.class, new ImmutableSetSerializer())
55 .register( 66 .register(
56 // 67 //
57 ArrayList.class, 68 ArrayList.class,
...@@ -61,18 +72,21 @@ public final class KryoPoolUtil { ...@@ -61,18 +72,21 @@ public final class KryoPoolUtil {
61 // 72 //
62 ControllerNode.State.class, 73 ControllerNode.State.class,
63 Device.Type.class, 74 Device.Type.class,
75 + ChassisId.class,
64 DefaultAnnotations.class, 76 DefaultAnnotations.class,
65 DefaultControllerNode.class, 77 DefaultControllerNode.class,
66 DefaultDevice.class, 78 DefaultDevice.class,
67 DefaultDeviceDescription.class, 79 DefaultDeviceDescription.class,
68 DefaultLinkDescription.class, 80 DefaultLinkDescription.class,
69 - MastershipRole.class,
70 Port.class, 81 Port.class,
71 DefaultPortDescription.class, 82 DefaultPortDescription.class,
72 Element.class, 83 Element.class,
73 Link.Type.class, 84 Link.Type.class,
74 - Timestamp.class 85 + Timestamp.class,
75 - 86 + HostId.class,
87 + HostDescription.class,
88 + DefaultHostDescription.class,
89 + DefaultFlowRule.class
76 ) 90 )
77 .register(URI.class, new URISerializer()) 91 .register(URI.class, new URISerializer())
78 .register(NodeId.class, new NodeIdSerializer()) 92 .register(NodeId.class, new NodeIdSerializer())
...@@ -84,11 +98,11 @@ public final class KryoPoolUtil { ...@@ -84,11 +98,11 @@ public final class KryoPoolUtil {
84 .register(ConnectPoint.class, new ConnectPointSerializer()) 98 .register(ConnectPoint.class, new ConnectPointSerializer())
85 .register(DefaultLink.class, new DefaultLinkSerializer()) 99 .register(DefaultLink.class, new DefaultLinkSerializer())
86 .register(MastershipTerm.class, new MastershipTermSerializer()) 100 .register(MastershipTerm.class, new MastershipTermSerializer())
87 - .register(MastershipRole.class, new MastershipRoleSerializer()) 101 + .register(HostLocation.class, new HostLocationSerializer())
88 102
89 .build(); 103 .build();
90 104
91 105
92 // not to be instantiated 106 // not to be instantiated
93 - private KryoPoolUtil() {} 107 + private KryoNamespaces() {}
94 } 108 }
......
1 package org.onlab.onos.store.serializers; 1 package org.onlab.onos.store.serializers;
2 2
3 -import org.onlab.util.KryoPool; 3 +import org.onlab.util.KryoNamespace;
4 import java.nio.ByteBuffer; 4 import java.nio.ByteBuffer;
5 5
6 /** 6 /**
...@@ -8,7 +8,7 @@ import java.nio.ByteBuffer; ...@@ -8,7 +8,7 @@ import java.nio.ByteBuffer;
8 */ 8 */
9 public class KryoSerializer implements StoreSerializer { 9 public class KryoSerializer implements StoreSerializer {
10 10
11 - protected KryoPool serializerPool; 11 + protected KryoNamespace serializerPool;
12 12
13 public KryoSerializer() { 13 public KryoSerializer() {
14 setupKryoPool(); 14 setupKryoPool();
...@@ -18,8 +18,8 @@ public class KryoSerializer implements StoreSerializer { ...@@ -18,8 +18,8 @@ public class KryoSerializer implements StoreSerializer {
18 * Sets up the common serialzers pool. 18 * Sets up the common serialzers pool.
19 */ 19 */
20 protected void setupKryoPool() { 20 protected void setupKryoPool() {
21 - serializerPool = KryoPool.newBuilder() 21 + serializerPool = KryoNamespace.newBuilder()
22 - .register(KryoPoolUtil.API) 22 + .register(KryoNamespaces.API)
23 .build() 23 .build()
24 .populate(1); 24 .populate(1);
25 } 25 }
......
1 package org.onlab.onos.store.serializers; 1 package org.onlab.onos.store.serializers;
2 2
3 -import org.onlab.onos.net.MastershipRole; 3 +import org.onlab.packet.MacAddress;
4 4
5 import com.esotericsoftware.kryo.Kryo; 5 import com.esotericsoftware.kryo.Kryo;
6 import com.esotericsoftware.kryo.Serializer; 6 import com.esotericsoftware.kryo.Serializer;
...@@ -8,27 +8,25 @@ import com.esotericsoftware.kryo.io.Input; ...@@ -8,27 +8,25 @@ import com.esotericsoftware.kryo.io.Input;
8 import com.esotericsoftware.kryo.io.Output; 8 import com.esotericsoftware.kryo.io.Output;
9 9
10 /** 10 /**
11 - * Kryo Serializer for {@link org.onlab.onos.net.MastershipRole}. 11 + * Kryo Serializer for {@link MacAddress}.
12 */ 12 */
13 -public class MastershipRoleSerializer extends Serializer<MastershipRole> { 13 +public class MacAddressSerializer extends Serializer<MacAddress> {
14 14
15 /** 15 /**
16 - * Creates {@link MastershipRole} serializer instance. 16 + * Creates {@link MacAddress} serializer instance.
17 */ 17 */
18 - public MastershipRoleSerializer() { 18 + public MacAddressSerializer() {
19 - // non-null, immutable
20 super(false, true); 19 super(false, true);
21 } 20 }
22 21
23 @Override 22 @Override
24 - public MastershipRole read(Kryo kryo, Input input, Class<MastershipRole> type) { 23 + public void write(Kryo kryo, Output output, MacAddress object) {
25 - final String role = kryo.readObject(input, String.class); 24 + output.writeBytes(object.getAddress());
26 - return MastershipRole.valueOf(role);
27 } 25 }
28 26
29 @Override 27 @Override
30 - public void write(Kryo kryo, Output output, MastershipRole object) { 28 + public MacAddress read(Kryo kryo, Input input, Class<MacAddress> type) {
31 - kryo.writeObject(output, object.toString()); 29 + return MacAddress.valueOf(input.readBytes(MacAddress.MAC_ADDRESS_LENGTH));
32 } 30 }
33 31
34 } 32 }
......
...@@ -20,16 +20,19 @@ import org.onlab.onos.net.DefaultLink; ...@@ -20,16 +20,19 @@ import org.onlab.onos.net.DefaultLink;
20 import org.onlab.onos.net.DefaultPort; 20 import org.onlab.onos.net.DefaultPort;
21 import org.onlab.onos.net.Device; 21 import org.onlab.onos.net.Device;
22 import org.onlab.onos.net.DeviceId; 22 import org.onlab.onos.net.DeviceId;
23 +import org.onlab.onos.net.HostLocation;
23 import org.onlab.onos.net.Link; 24 import org.onlab.onos.net.Link;
24 import org.onlab.onos.net.LinkKey; 25 import org.onlab.onos.net.LinkKey;
25 -import org.onlab.onos.net.MastershipRole;
26 import org.onlab.onos.net.PortNumber; 26 import org.onlab.onos.net.PortNumber;
27 import org.onlab.onos.net.SparseAnnotations; 27 import org.onlab.onos.net.SparseAnnotations;
28 import org.onlab.onos.net.provider.ProviderId; 28 import org.onlab.onos.net.provider.ProviderId;
29 +import org.onlab.packet.ChassisId;
29 import org.onlab.packet.IpAddress; 30 import org.onlab.packet.IpAddress;
30 import org.onlab.packet.IpPrefix; 31 import org.onlab.packet.IpPrefix;
31 -import org.onlab.util.KryoPool; 32 +import org.onlab.packet.MacAddress;
33 +import org.onlab.util.KryoNamespace;
32 34
35 +import com.google.common.collect.ImmutableList;
33 import com.google.common.collect.ImmutableMap; 36 import com.google.common.collect.ImmutableMap;
34 import com.google.common.collect.ImmutableSet; 37 import com.google.common.collect.ImmutableSet;
35 import com.google.common.testing.EqualsTester; 38 import com.google.common.testing.EqualsTester;
...@@ -49,7 +52,9 @@ public class KryoSerializerTest { ...@@ -49,7 +52,9 @@ public class KryoSerializerTest {
49 private static final String SW1 = "3.8.1"; 52 private static final String SW1 = "3.8.1";
50 private static final String SW2 = "3.9.5"; 53 private static final String SW2 = "3.9.5";
51 private static final String SN = "43311-12345"; 54 private static final String SN = "43311-12345";
52 - private static final Device DEV1 = new DefaultDevice(PID, DID1, Device.Type.SWITCH, MFR, HW, SW1, SN); 55 + private static final ChassisId CID = new ChassisId();
56 + private static final Device DEV1 = new DefaultDevice(PID, DID1, Device.Type.SWITCH, MFR, HW,
57 + SW1, SN, CID);
53 private static final SparseAnnotations A1 = DefaultAnnotations.builder() 58 private static final SparseAnnotations A1 = DefaultAnnotations.builder()
54 .set("A1", "a1") 59 .set("A1", "a1")
55 .set("B1", "b1") 60 .set("B1", "b1")
...@@ -59,81 +64,147 @@ public class KryoSerializerTest { ...@@ -59,81 +64,147 @@ public class KryoSerializerTest {
59 .set("B3", "b3") 64 .set("B3", "b3")
60 .build(); 65 .build();
61 66
62 - private static KryoPool kryos; 67 + private KryoSerializer serializer;
63 68
64 @BeforeClass 69 @BeforeClass
65 public static void setUpBeforeClass() throws Exception { 70 public static void setUpBeforeClass() throws Exception {
66 - kryos = KryoPool.newBuilder()
67 - .register(KryoPoolUtil.API)
68 - .register(ImmutableMap.class, new ImmutableMapSerializer())
69 - .register(ImmutableSet.class, new ImmutableSetSerializer())
70 - .build();
71 } 71 }
72 72
73 @Before 73 @Before
74 public void setUp() throws Exception { 74 public void setUp() throws Exception {
75 + serializer = new KryoSerializer() {
76 +
77 + @Override
78 + protected void setupKryoPool() {
79 + serializerPool = KryoNamespace.newBuilder()
80 + .register(KryoNamespaces.API)
81 + .build()
82 + .populate(1);
83 + }
84 + };
75 } 85 }
76 86
77 @After 87 @After
78 public void tearDown() throws Exception { 88 public void tearDown() throws Exception {
79 - // removing Kryo instance to use fresh Kryo on each tests
80 - kryos.getKryo();
81 } 89 }
82 90
83 - private static <T> void testSerialized(T original) { 91 + private <T> void testSerialized(T original) {
84 ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024); 92 ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
85 - kryos.serialize(original, buffer); 93 + serializer.encode(original, buffer);
86 buffer.flip(); 94 buffer.flip();
87 - T copy = kryos.deserialize(buffer); 95 + T copy = serializer.decode(buffer);
96 +
97 + T copy2 = serializer.decode(serializer.encode(original));
88 98
89 new EqualsTester() 99 new EqualsTester()
90 - .addEqualityGroup(original, copy) 100 + .addEqualityGroup(original, copy, copy2)
91 .testEquals(); 101 .testEquals();
92 } 102 }
93 103
94 104
95 @Test 105 @Test
96 - public final void testSerialization() { 106 + public void testConnectPoint() {
97 testSerialized(new ConnectPoint(DID1, P1)); 107 testSerialized(new ConnectPoint(DID1, P1));
108 + }
109 +
110 + @Test
111 + public void testDefaultLink() {
98 testSerialized(new DefaultLink(PID, CP1, CP2, Link.Type.DIRECT)); 112 testSerialized(new DefaultLink(PID, CP1, CP2, Link.Type.DIRECT));
99 - testSerialized(new DefaultPort(DEV1, P1, true));
100 testSerialized(new DefaultLink(PID, CP1, CP2, Link.Type.DIRECT, A1)); 113 testSerialized(new DefaultLink(PID, CP1, CP2, Link.Type.DIRECT, A1));
114 + }
115 +
116 + @Test
117 + public void testDefaultPort() {
118 + testSerialized(new DefaultPort(DEV1, P1, true));
101 testSerialized(new DefaultPort(DEV1, P1, true, A1_2)); 119 testSerialized(new DefaultPort(DEV1, P1, true, A1_2));
120 + }
121 +
122 + @Test
123 + public void testDeviceId() {
102 testSerialized(DID1); 124 testSerialized(DID1);
125 + }
126 +
127 + @Test
128 + public void testImmutableMap() {
103 testSerialized(ImmutableMap.of(DID1, DEV1, DID2, DEV1)); 129 testSerialized(ImmutableMap.of(DID1, DEV1, DID2, DEV1));
104 testSerialized(ImmutableMap.of(DID1, DEV1)); 130 testSerialized(ImmutableMap.of(DID1, DEV1));
105 testSerialized(ImmutableMap.of()); 131 testSerialized(ImmutableMap.of());
132 + }
133 +
134 + @Test
135 + public void testImmutableSet() {
106 testSerialized(ImmutableSet.of(DID1, DID2)); 136 testSerialized(ImmutableSet.of(DID1, DID2));
107 testSerialized(ImmutableSet.of(DID1)); 137 testSerialized(ImmutableSet.of(DID1));
108 testSerialized(ImmutableSet.of()); 138 testSerialized(ImmutableSet.of());
139 + }
140 +
141 + @Test
142 + public void testImmutableList() {
143 + testSerialized(ImmutableList.of(DID1, DID2));
144 + testSerialized(ImmutableList.of(DID1));
145 + testSerialized(ImmutableList.of());
146 + }
147 +
148 + @Test
149 + public void testIpPrefix() {
109 testSerialized(IpPrefix.valueOf("192.168.0.1/24")); 150 testSerialized(IpPrefix.valueOf("192.168.0.1/24"));
151 + }
152 +
153 + @Test
154 + public void testIpAddress() {
110 testSerialized(IpAddress.valueOf("192.168.0.1")); 155 testSerialized(IpAddress.valueOf("192.168.0.1"));
156 + }
157 +
158 + @Test
159 + public void testMacAddress() {
160 + testSerialized(MacAddress.valueOf("12:34:56:78:90:ab"));
161 + }
162 +
163 + @Test
164 + public void testLinkKey() {
111 testSerialized(LinkKey.linkKey(CP1, CP2)); 165 testSerialized(LinkKey.linkKey(CP1, CP2));
166 + }
167 +
168 + @Test
169 + public void testNodeId() {
112 testSerialized(new NodeId("SomeNodeIdentifier")); 170 testSerialized(new NodeId("SomeNodeIdentifier"));
171 + }
172 +
173 + @Test
174 + public void testPortNumber() {
113 testSerialized(P1); 175 testSerialized(P1);
176 + }
177 +
178 + @Test
179 + public void testProviderId() {
114 testSerialized(PID); 180 testSerialized(PID);
115 testSerialized(PIDA); 181 testSerialized(PIDA);
116 - testSerialized(new NodeId("bar")); 182 + }
183 +
184 + @Test
185 + public void testMastershipTerm() {
117 testSerialized(MastershipTerm.of(new NodeId("foo"), 2)); 186 testSerialized(MastershipTerm.of(new NodeId("foo"), 2));
118 - for (MastershipRole role : MastershipRole.values()) {
119 - testSerialized(role);
120 - }
121 } 187 }
122 188
123 @Test 189 @Test
124 - public final void testAnnotations() { 190 + public void testHostLocation() {
191 + testSerialized(new HostLocation(CP1, 1234L));
192 + }
193 +
194 + @Test
195 + public void testAnnotations() {
125 // Annotations does not have equals defined, manually test equality 196 // Annotations does not have equals defined, manually test equality
126 - final byte[] a1Bytes = kryos.serialize(A1); 197 + final byte[] a1Bytes = serializer.encode(A1);
127 - SparseAnnotations copiedA1 = kryos.deserialize(a1Bytes); 198 + SparseAnnotations copiedA1 = serializer.decode(a1Bytes);
128 assertAnnotationsEquals(copiedA1, A1); 199 assertAnnotationsEquals(copiedA1, A1);
129 200
130 - final byte[] a12Bytes = kryos.serialize(A1_2); 201 + final byte[] a12Bytes = serializer.encode(A1_2);
131 - SparseAnnotations copiedA12 = kryos.deserialize(a12Bytes); 202 + SparseAnnotations copiedA12 = serializer.decode(a12Bytes);
132 assertAnnotationsEquals(copiedA12, A1_2); 203 assertAnnotationsEquals(copiedA12, A1_2);
133 } 204 }
134 205
135 // code clone 206 // code clone
136 - public static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) { 207 + protected static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) {
137 SparseAnnotations expected = DefaultAnnotations.builder().build(); 208 SparseAnnotations expected = DefaultAnnotations.builder().build();
138 for (SparseAnnotations a : annotations) { 209 for (SparseAnnotations a : annotations) {
139 expected = DefaultAnnotations.union(expected, a); 210 expected = DefaultAnnotations.union(expected, a);
......
...@@ -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;
...@@ -30,11 +28,13 @@ import org.onlab.onos.net.device.DeviceStoreDelegate; ...@@ -30,11 +28,13 @@ import org.onlab.onos.net.device.DeviceStoreDelegate;
30 import org.onlab.onos.net.device.PortDescription; 28 import org.onlab.onos.net.device.PortDescription;
31 import org.onlab.onos.net.provider.ProviderId; 29 import org.onlab.onos.net.provider.ProviderId;
32 import org.onlab.onos.store.AbstractStore; 30 import org.onlab.onos.store.AbstractStore;
31 +import org.onlab.packet.ChassisId;
33 import org.onlab.util.NewConcurrentHashMap; 32 import org.onlab.util.NewConcurrentHashMap;
34 import org.slf4j.Logger; 33 import org.slf4j.Logger;
35 34
36 import java.util.ArrayList; 35 import java.util.ArrayList;
37 import java.util.Collections; 36 import java.util.Collections;
37 +import java.util.HashMap;
38 import java.util.HashSet; 38 import java.util.HashSet;
39 import java.util.Iterator; 39 import java.util.Iterator;
40 import java.util.List; 40 import java.util.List;
...@@ -71,8 +71,7 @@ public class SimpleDeviceStore ...@@ -71,8 +71,7 @@ public class SimpleDeviceStore
71 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";
72 72
73 // collection of Description given from various providers 73 // collection of Description given from various providers
74 - private final ConcurrentMap<DeviceId, 74 + private final ConcurrentMap<DeviceId, Map<ProviderId, DeviceDescriptions>>
75 - ConcurrentMap<ProviderId, DeviceDescriptions>>
76 deviceDescs = Maps.newConcurrentMap(); 75 deviceDescs = Maps.newConcurrentMap();
77 76
78 // cache of Device and Ports generated by compositing descriptions from providers 77 // cache of Device and Ports generated by compositing descriptions from providers
...@@ -117,15 +116,16 @@ public class SimpleDeviceStore ...@@ -117,15 +116,16 @@ public class SimpleDeviceStore
117 DeviceId deviceId, 116 DeviceId deviceId,
118 DeviceDescription deviceDescription) { 117 DeviceDescription deviceDescription) {
119 118
120 - ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs 119 + Map<ProviderId, DeviceDescriptions> providerDescs
121 - = getDeviceDescriptions(deviceId); 120 + = getOrCreateDeviceDescriptions(deviceId);
122 121
123 synchronized (providerDescs) { 122 synchronized (providerDescs) {
124 // locking per device 123 // locking per device
125 124
126 DeviceDescriptions descs 125 DeviceDescriptions descs
127 - = createIfAbsentUnchecked(providerDescs, providerId, 126 + = getOrCreateProviderDeviceDescriptions(providerDescs,
128 - new InitDeviceDescs(deviceDescription)); 127 + providerId,
128 + deviceDescription);
129 129
130 Device oldDevice = devices.get(deviceId); 130 Device oldDevice = devices.get(deviceId);
131 // update description 131 // update description
...@@ -192,8 +192,8 @@ public class SimpleDeviceStore ...@@ -192,8 +192,8 @@ public class SimpleDeviceStore
192 192
193 @Override 193 @Override
194 public DeviceEvent markOffline(DeviceId deviceId) { 194 public DeviceEvent markOffline(DeviceId deviceId) {
195 - ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs 195 + Map<ProviderId, DeviceDescriptions> providerDescs
196 - = getDeviceDescriptions(deviceId); 196 + = getOrCreateDeviceDescriptions(deviceId);
197 197
198 // locking device 198 // locking device
199 synchronized (providerDescs) { 199 synchronized (providerDescs) {
...@@ -218,7 +218,7 @@ public class SimpleDeviceStore ...@@ -218,7 +218,7 @@ public class SimpleDeviceStore
218 Device device = devices.get(deviceId); 218 Device device = devices.get(deviceId);
219 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); 219 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
220 220
221 - ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); 221 + Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
222 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); 222 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
223 223
224 List<DeviceEvent> events = new ArrayList<>(); 224 List<DeviceEvent> events = new ArrayList<>();
...@@ -287,12 +287,12 @@ public class SimpleDeviceStore ...@@ -287,12 +287,12 @@ public class SimpleDeviceStore
287 Map<PortNumber, Port> ports, 287 Map<PortNumber, Port> ports,
288 Set<PortNumber> processed) { 288 Set<PortNumber> processed) {
289 List<DeviceEvent> events = new ArrayList<>(); 289 List<DeviceEvent> events = new ArrayList<>();
290 - Iterator<PortNumber> iterator = ports.keySet().iterator(); 290 + Iterator<Entry<PortNumber, Port>> iterator = ports.entrySet().iterator();
291 while (iterator.hasNext()) { 291 while (iterator.hasNext()) {
292 - PortNumber portNumber = iterator.next(); 292 + Entry<PortNumber, Port> e = iterator.next();
293 + PortNumber portNumber = e.getKey();
293 if (!processed.contains(portNumber)) { 294 if (!processed.contains(portNumber)) {
294 - events.add(new DeviceEvent(PORT_REMOVED, device, 295 + events.add(new DeviceEvent(PORT_REMOVED, device, e.getValue()));
295 - ports.get(portNumber)));
296 iterator.remove(); 296 iterator.remove();
297 } 297 }
298 } 298 }
...@@ -306,10 +306,36 @@ public class SimpleDeviceStore ...@@ -306,10 +306,36 @@ public class SimpleDeviceStore
306 NewConcurrentHashMap.<PortNumber, Port>ifNeeded()); 306 NewConcurrentHashMap.<PortNumber, Port>ifNeeded());
307 } 307 }
308 308
309 - private ConcurrentMap<ProviderId, DeviceDescriptions> getDeviceDescriptions( 309 + private Map<ProviderId, DeviceDescriptions> getOrCreateDeviceDescriptions(
310 DeviceId deviceId) { 310 DeviceId deviceId) {
311 - return createIfAbsentUnchecked(deviceDescs, deviceId, 311 + Map<ProviderId, DeviceDescriptions> r;
312 - 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 + }
313 } 339 }
314 340
315 @Override 341 @Override
...@@ -318,12 +344,12 @@ public class SimpleDeviceStore ...@@ -318,12 +344,12 @@ public class SimpleDeviceStore
318 Device device = devices.get(deviceId); 344 Device device = devices.get(deviceId);
319 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId); 345 checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
320 346
321 - ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId); 347 + Map<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
322 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); 348 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
323 349
324 synchronized (descsMap) { 350 synchronized (descsMap) {
325 DeviceDescriptions descs = descsMap.get(providerId); 351 DeviceDescriptions descs = descsMap.get(providerId);
326 - // assuming all providers must to give DeviceDescription 352 + // assuming all providers must give DeviceDescription first
327 checkArgument(descs != null, 353 checkArgument(descs != null,
328 "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",
329 deviceId, providerId); 355 deviceId, providerId);
...@@ -367,7 +393,7 @@ public class SimpleDeviceStore ...@@ -367,7 +393,7 @@ public class SimpleDeviceStore
367 393
368 @Override 394 @Override
369 public DeviceEvent removeDevice(DeviceId deviceId) { 395 public DeviceEvent removeDevice(DeviceId deviceId) {
370 - ConcurrentMap<ProviderId, DeviceDescriptions> descs = getDeviceDescriptions(deviceId); 396 + Map<ProviderId, DeviceDescriptions> descs = getOrCreateDeviceDescriptions(deviceId);
371 synchronized (descs) { 397 synchronized (descs) {
372 Device device = devices.remove(deviceId); 398 Device device = devices.remove(deviceId);
373 // should DEVICE_REMOVED carry removed ports? 399 // should DEVICE_REMOVED carry removed ports?
...@@ -390,7 +416,7 @@ public class SimpleDeviceStore ...@@ -390,7 +416,7 @@ public class SimpleDeviceStore
390 * @return Device instance 416 * @return Device instance
391 */ 417 */
392 private Device composeDevice(DeviceId deviceId, 418 private Device composeDevice(DeviceId deviceId,
393 - ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) { 419 + Map<ProviderId, DeviceDescriptions> providerDescs) {
394 420
395 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied"); 421 checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
396 422
...@@ -404,6 +430,7 @@ public class SimpleDeviceStore ...@@ -404,6 +430,7 @@ public class SimpleDeviceStore
404 String hwVersion = base.hwVersion(); 430 String hwVersion = base.hwVersion();
405 String swVersion = base.swVersion(); 431 String swVersion = base.swVersion();
406 String serialNumber = base.serialNumber(); 432 String serialNumber = base.serialNumber();
433 + ChassisId chassisId = base.chassisId();
407 DefaultAnnotations annotations = DefaultAnnotations.builder().build(); 434 DefaultAnnotations annotations = DefaultAnnotations.builder().build();
408 annotations = merge(annotations, base.annotations()); 435 annotations = merge(annotations, base.annotations());
409 436
...@@ -421,7 +448,8 @@ public class SimpleDeviceStore ...@@ -421,7 +448,8 @@ public class SimpleDeviceStore
421 } 448 }
422 449
423 return new DefaultDevice(primary, deviceId , type, manufacturer, 450 return new DefaultDevice(primary, deviceId , type, manufacturer,
424 - hwVersion, swVersion, serialNumber, annotations); 451 + hwVersion, swVersion, serialNumber,
452 + chassisId, annotations);
425 } 453 }
426 454
427 /** 455 /**
...@@ -429,14 +457,14 @@ public class SimpleDeviceStore ...@@ -429,14 +457,14 @@ public class SimpleDeviceStore
429 * 457 *
430 * @param device device the port is on 458 * @param device device the port is on
431 * @param number port number 459 * @param number port number
432 - * @param providerDescs Collection of Descriptions from multiple providers 460 + * @param descsMap Collection of Descriptions from multiple providers
433 * @return Port instance 461 * @return Port instance
434 */ 462 */
435 private Port composePort(Device device, PortNumber number, 463 private Port composePort(Device device, PortNumber number,
436 - ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) { 464 + Map<ProviderId, DeviceDescriptions> descsMap) {
437 465
438 - ProviderId primary = pickPrimaryPID(providerDescs); 466 + ProviderId primary = pickPrimaryPID(descsMap);
439 - DeviceDescriptions primDescs = providerDescs.get(primary); 467 + DeviceDescriptions primDescs = descsMap.get(primary);
440 // if no primary, assume not enabled 468 // if no primary, assume not enabled
441 // TODO: revisit this default port enabled/disabled behavior 469 // TODO: revisit this default port enabled/disabled behavior
442 boolean isEnabled = false; 470 boolean isEnabled = false;
...@@ -448,7 +476,7 @@ public class SimpleDeviceStore ...@@ -448,7 +476,7 @@ public class SimpleDeviceStore
448 annotations = merge(annotations, portDesc.annotations()); 476 annotations = merge(annotations, portDesc.annotations());
449 } 477 }
450 478
451 - for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) { 479 + for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
452 if (e.getKey().equals(primary)) { 480 if (e.getKey().equals(primary)) {
453 continue; 481 continue;
454 } 482 }
...@@ -470,10 +498,9 @@ public class SimpleDeviceStore ...@@ -470,10 +498,9 @@ public class SimpleDeviceStore
470 /** 498 /**
471 * @return primary ProviderID, or randomly chosen one if none exists 499 * @return primary ProviderID, or randomly chosen one if none exists
472 */ 500 */
473 - private ProviderId pickPrimaryPID( 501 + private ProviderId pickPrimaryPID(Map<ProviderId, DeviceDescriptions> descsMap) {
474 - ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
475 ProviderId fallBackPrimary = null; 502 ProviderId fallBackPrimary = null;
476 - for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) { 503 + for (Entry<ProviderId, DeviceDescriptions> e : descsMap.entrySet()) {
477 if (!e.getKey().isAncillary()) { 504 if (!e.getKey().isAncillary()) {
478 return e.getKey(); 505 return e.getKey();
479 } else if (fallBackPrimary == null) { 506 } else if (fallBackPrimary == null) {
...@@ -484,21 +511,6 @@ public class SimpleDeviceStore ...@@ -484,21 +511,6 @@ public class SimpleDeviceStore
484 return fallBackPrimary; 511 return fallBackPrimary;
485 } 512 }
486 513
487 - public static final class InitDeviceDescs
488 - implements ConcurrentInitializer<DeviceDescriptions> {
489 -
490 - private final DeviceDescription deviceDesc;
491 -
492 - public InitDeviceDescs(DeviceDescription deviceDesc) {
493 - this.deviceDesc = checkNotNull(deviceDesc);
494 - }
495 - @Override
496 - public DeviceDescriptions get() throws ConcurrentException {
497 - return new DeviceDescriptions(deviceDesc);
498 - }
499 - }
500 -
501 -
502 /** 514 /**
503 * 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.
504 */ 516 */
......
...@@ -2,9 +2,14 @@ package org.onlab.onos.store.trivial.impl; ...@@ -2,9 +2,14 @@ package org.onlab.onos.store.trivial.impl;
2 2
3 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED; 3 import static org.onlab.onos.net.flow.FlowRuleEvent.Type.RULE_REMOVED;
4 import static org.slf4j.LoggerFactory.getLogger; 4 import static org.slf4j.LoggerFactory.getLogger;
5 +import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
6 +import static java.util.Collections.unmodifiableCollection;
5 7
6 import java.util.Collection; 8 import java.util.Collection;
7 -import java.util.Collections; 9 +import java.util.HashSet;
10 +import java.util.Set;
11 +import java.util.concurrent.ConcurrentHashMap;
12 +import java.util.concurrent.ConcurrentMap;
8 13
9 import org.apache.felix.scr.annotations.Activate; 14 import org.apache.felix.scr.annotations.Activate;
10 import org.apache.felix.scr.annotations.Component; 15 import org.apache.felix.scr.annotations.Component;
...@@ -15,18 +20,17 @@ import org.onlab.onos.net.DeviceId; ...@@ -15,18 +20,17 @@ import org.onlab.onos.net.DeviceId;
15 import org.onlab.onos.net.flow.DefaultFlowEntry; 20 import org.onlab.onos.net.flow.DefaultFlowEntry;
16 import org.onlab.onos.net.flow.FlowEntry; 21 import org.onlab.onos.net.flow.FlowEntry;
17 import org.onlab.onos.net.flow.FlowEntry.FlowEntryState; 22 import org.onlab.onos.net.flow.FlowEntry.FlowEntryState;
23 +import org.onlab.onos.net.flow.FlowId;
18 import org.onlab.onos.net.flow.FlowRule; 24 import org.onlab.onos.net.flow.FlowRule;
19 import org.onlab.onos.net.flow.FlowRuleEvent; 25 import org.onlab.onos.net.flow.FlowRuleEvent;
20 import org.onlab.onos.net.flow.FlowRuleEvent.Type; 26 import org.onlab.onos.net.flow.FlowRuleEvent.Type;
21 import org.onlab.onos.net.flow.FlowRuleStore; 27 import org.onlab.onos.net.flow.FlowRuleStore;
22 import org.onlab.onos.net.flow.FlowRuleStoreDelegate; 28 import org.onlab.onos.net.flow.FlowRuleStoreDelegate;
29 +import org.onlab.onos.net.flow.StoredFlowEntry;
23 import org.onlab.onos.store.AbstractStore; 30 import org.onlab.onos.store.AbstractStore;
31 +import org.onlab.util.NewConcurrentHashMap;
24 import org.slf4j.Logger; 32 import org.slf4j.Logger;
25 33
26 -import com.google.common.collect.ArrayListMultimap;
27 -import com.google.common.collect.ImmutableSet;
28 -import com.google.common.collect.Multimap;
29 -
30 /** 34 /**
31 * Manages inventory of flow rules using trivial in-memory implementation. 35 * Manages inventory of flow rules using trivial in-memory implementation.
32 */ 36 */
...@@ -38,12 +42,11 @@ public class SimpleFlowRuleStore ...@@ -38,12 +42,11 @@ public class SimpleFlowRuleStore
38 42
39 private final Logger log = getLogger(getClass()); 43 private final Logger log = getLogger(getClass());
40 44
41 - // store entries as a pile of rules, no info about device tables
42 - private final Multimap<DeviceId, FlowEntry> flowEntries =
43 - ArrayListMultimap.<DeviceId, FlowEntry>create();
44 45
45 - private final Multimap<Short, FlowRule> flowEntriesById = 46 + // inner Map is Device flow table
46 - ArrayListMultimap.<Short, FlowRule>create(); 47 + // Assumption: FlowId cannot have synonyms
48 + private final ConcurrentMap<DeviceId, ConcurrentMap<FlowId, StoredFlowEntry>>
49 + flowEntries = new ConcurrentHashMap<>();
47 50
48 @Activate 51 @Activate
49 public void activate() { 52 public void activate() {
...@@ -52,88 +55,130 @@ public class SimpleFlowRuleStore ...@@ -52,88 +55,130 @@ public class SimpleFlowRuleStore
52 55
53 @Deactivate 56 @Deactivate
54 public void deactivate() { 57 public void deactivate() {
58 + flowEntries.clear();
55 log.info("Stopped"); 59 log.info("Stopped");
56 } 60 }
57 61
58 62
59 @Override 63 @Override
60 public int getFlowRuleCount() { 64 public int getFlowRuleCount() {
61 - return flowEntries.size(); 65 + int sum = 0;
66 + for (ConcurrentMap<FlowId, StoredFlowEntry> ft : flowEntries.values()) {
67 + sum += ft.size();
68 + }
69 + return sum;
70 + }
71 +
72 + private static NewConcurrentHashMap<FlowId, StoredFlowEntry> lazyEmptyFlowTable() {
73 + return NewConcurrentHashMap.<FlowId, StoredFlowEntry>ifNeeded();
74 + }
75 +
76 + /**
77 + * Returns the flow table for specified device.
78 + *
79 + * @param deviceId identifier of the device
80 + * @return Map representing Flow Table of given device.
81 + */
82 + private ConcurrentMap<FlowId, StoredFlowEntry> getFlowTable(DeviceId deviceId) {
83 + return createIfAbsentUnchecked(flowEntries,
84 + deviceId, lazyEmptyFlowTable());
85 + }
86 +
87 + private StoredFlowEntry getFlowEntry(DeviceId deviceId, FlowId flowId) {
88 + return getFlowTable(deviceId).get(flowId);
62 } 89 }
63 90
64 @Override 91 @Override
65 - public synchronized FlowEntry getFlowEntry(FlowRule rule) { 92 + public FlowEntry getFlowEntry(FlowRule rule) {
66 - for (FlowEntry f : flowEntries.get(rule.deviceId())) { 93 + return getFlowEntry(rule.deviceId(), rule.id());
67 - if (f.equals(rule)) {
68 - return f;
69 - }
70 - }
71 - return null;
72 } 94 }
73 95
74 @Override 96 @Override
75 - public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) { 97 + public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
76 - Collection<FlowEntry> rules = flowEntries.get(deviceId); 98 + return unmodifiableCollection((Collection<? extends FlowEntry>)
77 - if (rules == null) { 99 + getFlowTable(deviceId).values());
78 - return Collections.emptyList();
79 - }
80 - return ImmutableSet.copyOf(rules);
81 } 100 }
82 101
83 @Override 102 @Override
84 - public synchronized Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId) { 103 + public Iterable<FlowRule> getFlowRulesByAppId(ApplicationId appId) {
85 - Collection<FlowRule> rules = flowEntriesById.get(appId.id()); 104 +
86 - if (rules == null) { 105 + Set<FlowRule> rules = new HashSet<>();
87 - return Collections.emptyList(); 106 + for (DeviceId did : flowEntries.keySet()) {
107 + ConcurrentMap<FlowId, StoredFlowEntry> ft = getFlowTable(did);
108 + for (FlowEntry fe : ft.values()) {
109 + if (fe.appId() == appId.id()) {
110 + rules.add(fe);
111 + }
112 + }
88 } 113 }
89 - return ImmutableSet.copyOf(rules); 114 + return rules;
90 } 115 }
91 116
92 @Override 117 @Override
93 - public synchronized void storeFlowRule(FlowRule rule) { 118 + public void storeFlowRule(FlowRule rule) {
94 - FlowEntry f = new DefaultFlowEntry(rule); 119 + final boolean added = storeFlowRuleInternal(rule);
95 - DeviceId did = f.deviceId(); 120 + }
96 - if (!flowEntries.containsEntry(did, f)) { 121 +
97 - flowEntries.put(did, f); 122 + private boolean storeFlowRuleInternal(FlowRule rule) {
98 - flowEntriesById.put(rule.appId(), f); 123 + StoredFlowEntry f = new DefaultFlowEntry(rule);
124 + final DeviceId did = f.deviceId();
125 + final FlowId fid = f.id();
126 + FlowEntry existing = getFlowTable(did).putIfAbsent(fid, f);
127 + if (existing != null) {
128 + // was already there? ignore
129 + return false;
99 } 130 }
131 + // new flow rule added
132 + // TODO: notify through delegate about remote event?
133 + return true;
100 } 134 }
101 135
102 @Override 136 @Override
103 - public synchronized void deleteFlowRule(FlowRule rule) { 137 + public void deleteFlowRule(FlowRule rule) {
104 - FlowEntry entry = getFlowEntry(rule); 138 +
139 + StoredFlowEntry entry = getFlowEntry(rule.deviceId(), rule.id());
105 if (entry == null) { 140 if (entry == null) {
106 //log.warn("Cannot find rule {}", rule); 141 //log.warn("Cannot find rule {}", rule);
107 return; 142 return;
108 } 143 }
109 - entry.setState(FlowEntryState.PENDING_REMOVE); 144 + synchronized (entry) {
145 + entry.setState(FlowEntryState.PENDING_REMOVE);
146 + }
110 } 147 }
111 148
112 @Override 149 @Override
113 - public synchronized FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) { 150 + public FlowRuleEvent addOrUpdateFlowRule(FlowEntry rule) {
114 - DeviceId did = rule.deviceId();
115 -
116 // check if this new rule is an update to an existing entry 151 // check if this new rule is an update to an existing entry
117 - FlowEntry stored = getFlowEntry(rule); 152 + StoredFlowEntry stored = getFlowEntry(rule.deviceId(), rule.id());
118 if (stored != null) { 153 if (stored != null) {
119 - stored.setBytes(rule.bytes()); 154 + synchronized (stored) {
120 - stored.setLife(rule.life()); 155 + stored.setBytes(rule.bytes());
121 - stored.setPackets(rule.packets()); 156 + stored.setLife(rule.life());
122 - if (stored.state() == FlowEntryState.PENDING_ADD) { 157 + stored.setPackets(rule.packets());
123 - stored.setState(FlowEntryState.ADDED); 158 + if (stored.state() == FlowEntryState.PENDING_ADD) {
124 - return new FlowRuleEvent(Type.RULE_ADDED, rule); 159 + stored.setState(FlowEntryState.ADDED);
160 + // TODO: Do we need to change `rule` state?
161 + return new FlowRuleEvent(Type.RULE_ADDED, rule);
162 + }
163 + return new FlowRuleEvent(Type.RULE_UPDATED, rule);
125 } 164 }
126 - return new FlowRuleEvent(Type.RULE_UPDATED, rule);
127 } 165 }
128 166
167 + // should not reach here
168 + // storeFlowRule was expected to be called
169 + log.error("FlowRule was not found in store {} to update", rule);
170 +
129 //flowEntries.put(did, rule); 171 //flowEntries.put(did, rule);
130 return null; 172 return null;
131 } 173 }
132 174
133 @Override 175 @Override
134 - public synchronized FlowRuleEvent removeFlowRule(FlowEntry rule) { 176 + public FlowRuleEvent removeFlowRule(FlowEntry rule) {
135 // This is where one could mark a rule as removed and still keep it in the store. 177 // This is where one could mark a rule as removed and still keep it in the store.
136 - if (flowEntries.remove(rule.deviceId(), rule)) { 178 + final DeviceId did = rule.deviceId();
179 +
180 + ConcurrentMap<FlowId, StoredFlowEntry> ft = getFlowTable(did);
181 + if (ft.remove(rule.id(), rule)) {
137 return new FlowRuleEvent(RULE_REMOVED, rule); 182 return new FlowRuleEvent(RULE_REMOVED, rule);
138 } else { 183 } else {
139 return null; 184 return null;
......
...@@ -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 }
......
...@@ -2,9 +2,11 @@ package org.onlab.onos.store.trivial.impl; ...@@ -2,9 +2,11 @@ package org.onlab.onos.store.trivial.impl;
2 2
3 import static org.slf4j.LoggerFactory.getLogger; 3 import static org.slf4j.LoggerFactory.getLogger;
4 4
5 +import java.util.ArrayList;
5 import java.util.Collections; 6 import java.util.Collections;
6 import java.util.HashMap; 7 import java.util.HashMap;
7 import java.util.HashSet; 8 import java.util.HashSet;
9 +import java.util.List;
8 import java.util.Map; 10 import java.util.Map;
9 import java.util.Set; 11 import java.util.Set;
10 import java.util.concurrent.atomic.AtomicInteger; 12 import java.util.concurrent.atomic.AtomicInteger;
...@@ -95,6 +97,18 @@ public class SimpleMastershipStore ...@@ -95,6 +97,18 @@ public class SimpleMastershipStore
95 } 97 }
96 98
97 @Override 99 @Override
100 + public List<NodeId> getNodes(DeviceId deviceId) {
101 + List<NodeId> nodes = new ArrayList<>();
102 +
103 + nodes.addAll(backups);
104 + if (!nodes.contains(masterMap.get(deviceId))) {
105 + nodes.add(masterMap.get(deviceId));
106 + }
107 +
108 + return Collections.unmodifiableList(nodes);
109 + }
110 +
111 + @Override
98 public Set<DeviceId> getDevices(NodeId nodeId) { 112 public Set<DeviceId> getDevices(NodeId nodeId) {
99 Set<DeviceId> ids = new HashSet<>(); 113 Set<DeviceId> ids = new HashSet<>();
100 for (Map.Entry<DeviceId, NodeId> d : masterMap.entrySet()) { 114 for (Map.Entry<DeviceId, NodeId> d : masterMap.entrySet()) {
......
...@@ -17,6 +17,7 @@ import org.onlab.onos.net.topology.GraphDescription; ...@@ -17,6 +17,7 @@ import org.onlab.onos.net.topology.GraphDescription;
17 import org.onlab.onos.net.topology.LinkWeight; 17 import org.onlab.onos.net.topology.LinkWeight;
18 import org.onlab.onos.net.topology.TopologyCluster; 18 import org.onlab.onos.net.topology.TopologyCluster;
19 import org.onlab.onos.net.topology.TopologyEdge; 19 import org.onlab.onos.net.topology.TopologyEdge;
20 +import org.onlab.packet.ChassisId;
20 21
21 import java.util.Set; 22 import java.util.Set;
22 23
...@@ -119,7 +120,7 @@ public class DefaultTopologyTest { ...@@ -119,7 +120,7 @@ public class DefaultTopologyTest {
119 // Crates a new device with the specified id 120 // Crates a new device with the specified id
120 public static Device device(String id) { 121 public static Device device(String id) {
121 return new DefaultDevice(PID, did(id), Device.Type.SWITCH, 122 return new DefaultDevice(PID, did(id), Device.Type.SWITCH,
122 - "mfg", "1.0", "1.1", "1234"); 123 + "mfg", "1.0", "1.1", "1234", new ChassisId());
123 } 124 }
124 125
125 // Short-hand for producing a device id from a string 126 // Short-hand for producing a device id from a string
......
...@@ -40,6 +40,7 @@ import org.onlab.onos.net.provider.ProviderId; ...@@ -40,6 +40,7 @@ import org.onlab.onos.net.provider.ProviderId;
40 40
41 import com.google.common.collect.Iterables; 41 import com.google.common.collect.Iterables;
42 import com.google.common.collect.Sets; 42 import com.google.common.collect.Sets;
43 +import org.onlab.packet.ChassisId;
43 44
44 /** 45 /**
45 * Test of the simple DeviceStore implementation. 46 * Test of the simple DeviceStore implementation.
...@@ -55,6 +56,7 @@ public class SimpleDeviceStoreTest { ...@@ -55,6 +56,7 @@ public class SimpleDeviceStoreTest {
55 private static final String SW1 = "3.8.1"; 56 private static final String SW1 = "3.8.1";
56 private static final String SW2 = "3.9.5"; 57 private static final String SW2 = "3.9.5";
57 private static final String SN = "43311-12345"; 58 private static final String SN = "43311-12345";
59 + private static final ChassisId CID = new ChassisId();
58 60
59 private static final PortNumber P1 = PortNumber.portNumber(1); 61 private static final PortNumber P1 = PortNumber.portNumber(1);
60 private static final PortNumber P2 = PortNumber.portNumber(2); 62 private static final PortNumber P2 = PortNumber.portNumber(2);
...@@ -107,7 +109,7 @@ public class SimpleDeviceStoreTest { ...@@ -107,7 +109,7 @@ public class SimpleDeviceStoreTest {
107 SparseAnnotations... annotations) { 109 SparseAnnotations... annotations) {
108 DeviceDescription description = 110 DeviceDescription description =
109 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR, 111 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
110 - HW, swVersion, SN, annotations); 112 + HW, swVersion, SN, CID, annotations);
111 deviceStore.createOrUpdateDevice(PID, deviceId, description); 113 deviceStore.createOrUpdateDevice(PID, deviceId, description);
112 } 114 }
113 115
...@@ -115,7 +117,7 @@ public class SimpleDeviceStoreTest { ...@@ -115,7 +117,7 @@ public class SimpleDeviceStoreTest {
115 SparseAnnotations... annotations) { 117 SparseAnnotations... annotations) {
116 DeviceDescription description = 118 DeviceDescription description =
117 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR, 119 new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
118 - HW, swVersion, SN, annotations); 120 + HW, swVersion, SN, CID, annotations);
119 deviceStore.createOrUpdateDevice(PIDA, deviceId, description); 121 deviceStore.createOrUpdateDevice(PIDA, deviceId, description);
120 } 122 }
121 123
...@@ -193,14 +195,14 @@ public class SimpleDeviceStoreTest { ...@@ -193,14 +195,14 @@ public class SimpleDeviceStoreTest {
193 public final void testCreateOrUpdateDevice() { 195 public final void testCreateOrUpdateDevice() {
194 DeviceDescription description = 196 DeviceDescription description =
195 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, 197 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
196 - HW, SW1, SN); 198 + HW, SW1, SN, CID);
197 DeviceEvent event = deviceStore.createOrUpdateDevice(PID, DID1, description); 199 DeviceEvent event = deviceStore.createOrUpdateDevice(PID, DID1, description);
198 assertEquals(DEVICE_ADDED, event.type()); 200 assertEquals(DEVICE_ADDED, event.type());
199 assertDevice(DID1, SW1, event.subject()); 201 assertDevice(DID1, SW1, event.subject());
200 202
201 DeviceDescription description2 = 203 DeviceDescription description2 =
202 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, 204 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
203 - HW, SW2, SN); 205 + HW, SW2, SN, CID);
204 DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2); 206 DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
205 assertEquals(DEVICE_UPDATED, event2.type()); 207 assertEquals(DEVICE_UPDATED, event2.type());
206 assertDevice(DID1, SW2, event2.subject()); 208 assertDevice(DID1, SW2, event2.subject());
...@@ -212,7 +214,7 @@ public class SimpleDeviceStoreTest { ...@@ -212,7 +214,7 @@ public class SimpleDeviceStoreTest {
212 public final void testCreateOrUpdateDeviceAncillary() { 214 public final void testCreateOrUpdateDeviceAncillary() {
213 DeviceDescription description = 215 DeviceDescription description =
214 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, 216 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
215 - HW, SW1, SN, A2); 217 + HW, SW1, SN, CID, A2);
216 DeviceEvent event = deviceStore.createOrUpdateDevice(PIDA, DID1, description); 218 DeviceEvent event = deviceStore.createOrUpdateDevice(PIDA, DID1, description);
217 assertEquals(DEVICE_ADDED, event.type()); 219 assertEquals(DEVICE_ADDED, event.type());
218 assertDevice(DID1, SW1, event.subject()); 220 assertDevice(DID1, SW1, event.subject());
...@@ -222,7 +224,7 @@ public class SimpleDeviceStoreTest { ...@@ -222,7 +224,7 @@ public class SimpleDeviceStoreTest {
222 224
223 DeviceDescription description2 = 225 DeviceDescription description2 =
224 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, 226 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
225 - HW, SW2, SN, A1); 227 + HW, SW2, SN, CID, A1);
226 DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2); 228 DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
227 assertEquals(DEVICE_UPDATED, event2.type()); 229 assertEquals(DEVICE_UPDATED, event2.type());
228 assertDevice(DID1, SW2, event2.subject()); 230 assertDevice(DID1, SW2, event2.subject());
...@@ -238,7 +240,7 @@ public class SimpleDeviceStoreTest { ...@@ -238,7 +240,7 @@ public class SimpleDeviceStoreTest {
238 // But, Ancillary annotations will be in effect 240 // But, Ancillary annotations will be in effect
239 DeviceDescription description3 = 241 DeviceDescription description3 =
240 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, 242 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
241 - HW, SW1, SN, A2_2); 243 + HW, SW1, SN, CID, A2_2);
242 DeviceEvent event3 = deviceStore.createOrUpdateDevice(PIDA, DID1, description3); 244 DeviceEvent event3 = deviceStore.createOrUpdateDevice(PIDA, DID1, description3);
243 assertEquals(DEVICE_UPDATED, event3.type()); 245 assertEquals(DEVICE_UPDATED, event3.type());
244 // basic information will be the one from Primary 246 // basic information will be the one from Primary
...@@ -508,7 +510,7 @@ public class SimpleDeviceStoreTest { ...@@ -508,7 +510,7 @@ public class SimpleDeviceStoreTest {
508 510
509 DeviceDescription description = 511 DeviceDescription description =
510 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, 512 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
511 - HW, SW1, SN); 513 + HW, SW1, SN, CID);
512 deviceStore.setDelegate(checkAdd); 514 deviceStore.setDelegate(checkAdd);
513 deviceStore.createOrUpdateDevice(PID, DID1, description); 515 deviceStore.createOrUpdateDevice(PID, DID1, description);
514 assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS)); 516 assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
...@@ -516,7 +518,7 @@ public class SimpleDeviceStoreTest { ...@@ -516,7 +518,7 @@ public class SimpleDeviceStoreTest {
516 518
517 DeviceDescription description2 = 519 DeviceDescription description2 =
518 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, 520 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
519 - HW, SW2, SN); 521 + HW, SW2, SN, CID);
520 deviceStore.unsetDelegate(checkAdd); 522 deviceStore.unsetDelegate(checkAdd);
521 deviceStore.setDelegate(checkUpdate); 523 deviceStore.setDelegate(checkUpdate);
522 deviceStore.createOrUpdateDevice(PID, DID1, description2); 524 deviceStore.createOrUpdateDevice(PID, DID1, description2);
......
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,15 +98,15 @@ ...@@ -95,15 +98,15 @@
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>
102 <bundle>mvn:org.onlab.onos/onos-of-ctl/1.0.0-SNAPSHOT</bundle> 105 <bundle>mvn:org.onlab.onos/onos-of-ctl/1.0.0-SNAPSHOT</bundle>
103 106
107 + <bundle>mvn:org.onlab.onos/onos-lldp-provider/1.0.0-SNAPSHOT</bundle>
108 + <bundle>mvn:org.onlab.onos/onos-host-provider/1.0.0-SNAPSHOT</bundle>
104 <bundle>mvn:org.onlab.onos/onos-of-provider-device/1.0.0-SNAPSHOT</bundle> 109 <bundle>mvn:org.onlab.onos/onos-of-provider-device/1.0.0-SNAPSHOT</bundle>
105 - <bundle>mvn:org.onlab.onos/onos-of-provider-link/1.0.0-SNAPSHOT</bundle>
106 - <bundle>mvn:org.onlab.onos/onos-of-provider-host/1.0.0-SNAPSHOT</bundle>
107 <bundle>mvn:org.onlab.onos/onos-of-provider-packet/1.0.0-SNAPSHOT</bundle> 110 <bundle>mvn:org.onlab.onos/onos-of-provider-packet/1.0.0-SNAPSHOT</bundle>
108 <bundle>mvn:org.onlab.onos/onos-of-provider-flow/1.0.0-SNAPSHOT</bundle> 111 <bundle>mvn:org.onlab.onos/onos-of-provider-flow/1.0.0-SNAPSHOT</bundle>
109 112
...@@ -152,6 +155,13 @@ ...@@ -152,6 +155,13 @@
152 <feature>onos-api</feature> 155 <feature>onos-api</feature>
153 <bundle>mvn:org.onlab.onos/onos-app-config/1.0.0-SNAPSHOT</bundle> 156 <bundle>mvn:org.onlab.onos/onos-app-config/1.0.0-SNAPSHOT</bundle>
154 </feature> 157 </feature>
158 +
159 + <feature name="onos-app-optical" version="1.0.0"
160 + description="ONOS optical network config">
161 + <feature>onos-api</feature>
162 + <bundle>mvn:org.onlab.onos/onos-app-optical/1.0.0-SNAPSHOT</bundle>
163 + </feature>
164 +
155 165
156 <feature name="onos-app-sdnip" version="1.0.0" 166 <feature name="onos-app-sdnip" version="1.0.0"
157 description="SDN-IP peering application"> 167 description="SDN-IP peering application">
...@@ -159,4 +169,11 @@ ...@@ -159,4 +169,11 @@
159 <bundle>mvn:org.onlab.onos/onos-app-sdnip/1.0.0-SNAPSHOT</bundle> 169 <bundle>mvn:org.onlab.onos/onos-app-sdnip/1.0.0-SNAPSHOT</bundle>
160 </feature> 170 </feature>
161 171
172 + <feature name="onos-app-calendar" version="1.0.0"
173 + description="REST interface for scheduling intents from an external calendar">
174 + <feature>onos-api</feature>
175 + <feature>onos-thirdparty-web</feature>
176 + <bundle>mvn:org.onlab.onos/onos-app-calendar/1.0.0-SNAPSHOT</bundle>
177 + </feature>
178 +
162 </features> 179 </features>
......
...@@ -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>
...@@ -412,7 +431,7 @@ ...@@ -412,7 +431,7 @@
412 <plugin> 431 <plugin>
413 <groupId>org.apache.maven.plugins</groupId> 432 <groupId>org.apache.maven.plugins</groupId>
414 <artifactId>maven-checkstyle-plugin</artifactId> 433 <artifactId>maven-checkstyle-plugin</artifactId>
415 - <version>2.12.1</version> 434 + <version>2.13</version>
416 <dependencies> 435 <dependencies>
417 <dependency> 436 <dependency>
418 <groupId>org.onlab.tools</groupId> 437 <groupId>org.onlab.tools</groupId>
...@@ -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
......
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-of-providers</artifactId>
10 + <version>1.0.0-SNAPSHOT</version>
11 + <relativePath>../pom.xml</relativePath>
12 + </parent>
13 +
14 + <artifactId>onos-of-provider-host</artifactId>
15 + <packaging>bundle</packaging>
16 +
17 + <description>ONOS OpenFlow protocol host provider</description>
18 +
19 +</project>
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-providers</artifactId>
10 + <version>1.0.0-SNAPSHOT</version>
11 + <relativePath>../pom.xml</relativePath>
12 + </parent>
13 +
14 +
15 + <artifactId>onos-host-provider</artifactId>
16 + <packaging>bundle</packaging>
17 +
18 + <description>ONOS host tracking provider</description>
19 + <dependencies>
20 + <dependency>
21 + <groupId>org.onlab.onos</groupId>
22 + <artifactId>onos-api</artifactId>
23 + <classifier>tests</classifier>
24 + <scope>test</scope>
25 + </dependency>
26 + </dependencies>
27 +
28 +
29 +
30 +</project>
1 +package org.onlab.onos.provider.host.impl;
2 +
3 +import static org.slf4j.LoggerFactory.getLogger;
4 +
5 +import org.apache.felix.scr.annotations.Activate;
6 +import org.apache.felix.scr.annotations.Component;
7 +import org.apache.felix.scr.annotations.Deactivate;
8 +import org.apache.felix.scr.annotations.Reference;
9 +import org.apache.felix.scr.annotations.ReferenceCardinality;
10 +import org.onlab.onos.net.ConnectPoint;
11 +import org.onlab.onos.net.Host;
12 +import org.onlab.onos.net.HostId;
13 +import org.onlab.onos.net.HostLocation;
14 +import org.onlab.onos.net.host.DefaultHostDescription;
15 +import org.onlab.onos.net.host.HostDescription;
16 +import org.onlab.onos.net.host.HostProvider;
17 +import org.onlab.onos.net.host.HostProviderRegistry;
18 +import org.onlab.onos.net.host.HostProviderService;
19 +import org.onlab.onos.net.packet.PacketContext;
20 +import org.onlab.onos.net.packet.PacketProcessor;
21 +import org.onlab.onos.net.packet.PacketService;
22 +import org.onlab.onos.net.provider.AbstractProvider;
23 +import org.onlab.onos.net.provider.ProviderId;
24 +import org.onlab.onos.net.topology.Topology;
25 +import org.onlab.onos.net.topology.TopologyService;
26 +import org.onlab.packet.ARP;
27 +import org.onlab.packet.Ethernet;
28 +import org.onlab.packet.IpPrefix;
29 +import org.onlab.packet.VlanId;
30 +import org.slf4j.Logger;
31 +
32 +/**
33 + * Provider which uses an OpenFlow controller to detect network
34 + * end-station hosts.
35 + */
36 +@Component(immediate = true)
37 +public class HostLocationProvider extends AbstractProvider implements HostProvider {
38 +
39 + private final Logger log = getLogger(getClass());
40 +
41 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
42 + protected HostProviderRegistry providerRegistry;
43 +
44 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
45 + protected PacketService pktService;
46 +
47 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
48 + protected TopologyService topologyService;
49 +
50 + private HostProviderService providerService;
51 +
52 + private final InternalHostProvider processor = new InternalHostProvider();
53 +
54 +
55 + /**
56 + * Creates an OpenFlow host provider.
57 + */
58 + public HostLocationProvider() {
59 + super(new ProviderId("of", "org.onlab.onos.provider.host"));
60 + }
61 +
62 + @Activate
63 + public void activate() {
64 + providerService = providerRegistry.register(this);
65 + pktService.addProcessor(processor, 1);
66 + log.info("Started");
67 + }
68 +
69 + @Deactivate
70 + public void deactivate() {
71 + providerRegistry.unregister(this);
72 + pktService.removeProcessor(processor);
73 + providerService = null;
74 + log.info("Stopped");
75 + }
76 +
77 + @Override
78 + public void triggerProbe(Host host) {
79 + log.info("Triggering probe on device {}", host);
80 + }
81 +
82 + private class InternalHostProvider implements PacketProcessor {
83 +
84 + @Override
85 + public void process(PacketContext context) {
86 + if (context == null) {
87 + return;
88 + }
89 + Ethernet eth = context.inPacket().parsed();
90 +
91 + VlanId vlan = VlanId.vlanId(eth.getVlanID());
92 + ConnectPoint heardOn = context.inPacket().receivedFrom();
93 +
94 + // If this is not an edge port, bail out.
95 + Topology topology = topologyService.currentTopology();
96 + if (topologyService.isInfrastructure(topology, heardOn)) {
97 + return;
98 + }
99 +
100 + HostLocation hloc = new HostLocation(heardOn, System.currentTimeMillis());
101 +
102 + HostId hid = HostId.hostId(eth.getSourceMAC(), vlan);
103 +
104 + // Potentially a new or moved host
105 + if (eth.getEtherType() == Ethernet.TYPE_ARP) {
106 + ARP arp = (ARP) eth.getPayload();
107 + IpPrefix ip = IpPrefix.valueOf(arp.getSenderProtocolAddress(),
108 + IpPrefix.MAX_INET_MASK);
109 + HostDescription hdescr =
110 + new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip);
111 + providerService.hostDetected(hid, hdescr);
112 +
113 + } else if (eth.getEtherType() == Ethernet.TYPE_IPV4) {
114 + //Do not learn new ip from ip packet.
115 + HostDescription hdescr =
116 + new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc);
117 + providerService.hostDetected(hid, hdescr);
118 +
119 + }
120 + }
121 + }
122 +}
1 +/**
2 + * Provider that uses packet service as a means of host discovery and tracking.
3 + */
4 +package org.onlab.onos.provider.host.impl;
1 +package org.onlab.onos.provider.host.impl;
2 +
3 +import static org.junit.Assert.assertEquals;
4 +import static org.junit.Assert.assertNotNull;
5 +import static org.junit.Assert.assertNull;
6 +
7 +import java.nio.ByteBuffer;
8 +import java.util.Set;
9 +
10 +import org.junit.After;
11 +import org.junit.Before;
12 +import org.junit.Test;
13 +import org.onlab.onos.net.ConnectPoint;
14 +import org.onlab.onos.net.DeviceId;
15 +import org.onlab.onos.net.HostId;
16 +import org.onlab.onos.net.PortNumber;
17 +import org.onlab.onos.net.flow.TrafficTreatment;
18 +import org.onlab.onos.net.host.HostDescription;
19 +import org.onlab.onos.net.host.HostProvider;
20 +import org.onlab.onos.net.host.HostProviderRegistry;
21 +import org.onlab.onos.net.host.HostProviderService;
22 +import org.onlab.onos.net.packet.DefaultInboundPacket;
23 +import org.onlab.onos.net.packet.InboundPacket;
24 +import org.onlab.onos.net.packet.OutboundPacket;
25 +import org.onlab.onos.net.packet.PacketContext;
26 +import org.onlab.onos.net.packet.PacketProcessor;
27 +import org.onlab.onos.net.packet.PacketService;
28 +import org.onlab.onos.net.provider.AbstractProviderService;
29 +import org.onlab.onos.net.provider.ProviderId;
30 +import org.onlab.onos.net.topology.Topology;
31 +
32 +import org.onlab.onos.net.topology.TopologyServiceAdapter;
33 +import org.onlab.packet.ARP;
34 +import org.onlab.packet.Ethernet;
35 +import org.onlab.packet.MacAddress;
36 +import org.onlab.packet.VlanId;
37 +
38 +public class HostLocationProviderTest {
39 +
40 + private static final Integer INPORT = 10;
41 + private static final String DEV1 = "of:1";
42 + private static final String DEV2 = "of:2";
43 + private static final String DEV3 = "of:3";
44 +
45 + private static final VlanId VLAN = VlanId.vlanId();
46 + private static final MacAddress MAC = MacAddress.valueOf("00:00:11:00:00:01");
47 + private static final MacAddress BCMAC = MacAddress.valueOf("ff:ff:ff:ff:ff:ff");
48 + private static final byte[] IP = new byte[]{10, 0, 0, 1};
49 +
50 + private final HostLocationProvider provider = new HostLocationProvider();
51 + private final TestHostRegistry hostService = new TestHostRegistry();
52 + private final TestTopologyService topoService = new TestTopologyService();
53 + private final TestPacketService packetService = new TestPacketService();
54 +
55 + private PacketProcessor testProcessor;
56 + private TestHostProviderService providerService;
57 +
58 + @Before
59 + public void setUp() {
60 + provider.providerRegistry = hostService;
61 + provider.topologyService = topoService;
62 + provider.pktService = packetService;
63 +
64 + provider.activate();
65 +
66 + }
67 +
68 + @Test
69 + public void basics() {
70 + assertNotNull("registration expected", providerService);
71 + assertEquals("incorrect provider", provider, providerService.provider());
72 + }
73 +
74 + @Test
75 + public void events() {
76 + // new host
77 +
78 +
79 + testProcessor.process(new TestPacketContext(DEV1));
80 + assertNotNull("new host expected", providerService.added);
81 + assertNull("host motion unexpected", providerService.moved);
82 +
83 + // the host moved to new switch
84 + testProcessor.process(new TestPacketContext(DEV2));
85 + assertNotNull("host motion expected", providerService.moved);
86 +
87 + // the host was misheard on a spine
88 + testProcessor.process(new TestPacketContext(DEV3));
89 + assertNull("host misheard on spine switch", providerService.spine);
90 + }
91 +
92 + @After
93 + public void tearDown() {
94 + provider.deactivate();
95 + provider.providerRegistry = null;
96 +
97 + }
98 +
99 + private class TestHostRegistry implements HostProviderRegistry {
100 +
101 + @Override
102 + public HostProviderService register(HostProvider provider) {
103 + providerService = new TestHostProviderService(provider);
104 + return providerService;
105 + }
106 +
107 + @Override
108 + public void unregister(HostProvider provider) {
109 + }
110 +
111 + @Override
112 + public Set<ProviderId> getProviders() {
113 + return null;
114 + }
115 +
116 + }
117 +
118 + private class TestHostProviderService
119 + extends AbstractProviderService<HostProvider>
120 + implements HostProviderService {
121 +
122 + DeviceId added = null;
123 + DeviceId moved = null;
124 + DeviceId spine = null;
125 +
126 + protected TestHostProviderService(HostProvider provider) {
127 + super(provider);
128 + }
129 +
130 + @Override
131 + public void hostDetected(HostId hostId, HostDescription hostDescription) {
132 + DeviceId descr = hostDescription.location().deviceId();
133 + if (added == null) {
134 + added = descr;
135 + } else if ((moved == null) && !descr.equals(added)) {
136 + moved = descr;
137 + } else {
138 + spine = descr;
139 + }
140 + }
141 +
142 + @Override
143 + public void hostVanished(HostId hostId) {
144 + }
145 +
146 + }
147 +
148 + private class TestPacketService implements PacketService {
149 +
150 + @Override
151 + public void addProcessor(PacketProcessor processor, int priority) {
152 + testProcessor = processor;
153 + }
154 +
155 + @Override
156 + public void removeProcessor(PacketProcessor processor) {
157 +
158 + }
159 +
160 + @Override
161 + public void emit(OutboundPacket packet) {
162 +
163 + }
164 + }
165 +
166 +
167 + private class TestTopologyService extends TopologyServiceAdapter {
168 + @Override
169 + public boolean isInfrastructure(Topology topology,
170 + ConnectPoint connectPoint) {
171 + //simulate DPID3 as an infrastructure switch
172 + if ((connectPoint.deviceId()).equals(DeviceId.deviceId(DEV3))) {
173 + return true;
174 + }
175 + return false;
176 + }
177 + }
178 +
179 + private class TestPacketContext implements PacketContext {
180 +
181 + private final String deviceId;
182 +
183 + public TestPacketContext(String deviceId) {
184 + this.deviceId = deviceId;
185 + }
186 +
187 + @Override
188 + public long time() {
189 + return 0;
190 + }
191 +
192 + @Override
193 + public InboundPacket inPacket() {
194 + ARP arp = new ARP();
195 + arp.setSenderProtocolAddress(IP)
196 + .setSenderHardwareAddress(MAC.toBytes())
197 + .setTargetHardwareAddress(BCMAC.toBytes())
198 + .setTargetProtocolAddress(IP);
199 +
200 + Ethernet eth = new Ethernet();
201 + eth.setEtherType(Ethernet.TYPE_ARP)
202 + .setVlanID(VLAN.toShort())
203 + .setSourceMACAddress(MAC.toBytes())
204 + .setDestinationMACAddress(BCMAC.getAddress())
205 + .setPayload(arp);
206 + ConnectPoint receivedFrom = new ConnectPoint(DeviceId.deviceId(deviceId),
207 + PortNumber.portNumber(INPORT));
208 + return new DefaultInboundPacket(receivedFrom, eth,
209 + ByteBuffer.wrap(eth.serialize()));
210 + }
211 +
212 + @Override
213 + public OutboundPacket outPacket() {
214 + return null;
215 + }
216 +
217 + @Override
218 + public TrafficTreatment.Builder treatmentBuilder() {
219 + return null;
220 + }
221 +
222 + @Override
223 + public void send() {
224 +
225 + }
226 +
227 + @Override
228 + public boolean block() {
229 + return false;
230 + }
231 +
232 + @Override
233 + public boolean isHandled() {
234 + return false;
235 + }
236 + }
237 +}
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 +
8 + <parent>
9 + <groupId>org.onlab.onos</groupId>
10 + <artifactId>onos-providers</artifactId>
11 + <version>1.0.0-SNAPSHOT</version>
12 + <relativePath>../pom.xml</relativePath>
13 + </parent>
14 +
15 + <artifactId>onos-lldp-provider</artifactId>
16 + <packaging>bundle</packaging>
17 +
18 + <description>ONOS LLDP Link Discovery</description>
19 +
20 +</project>
1 +package org.onlab.onos.provider.lldp.impl;
2 +
3 +import org.apache.felix.scr.annotations.Activate;
4 +import org.apache.felix.scr.annotations.Component;
5 +import org.apache.felix.scr.annotations.Deactivate;
6 +import org.apache.felix.scr.annotations.Reference;
7 +import org.apache.felix.scr.annotations.ReferenceCardinality;
8 +import org.onlab.onos.mastership.MastershipService;
9 +import org.onlab.onos.net.ConnectPoint;
10 +import org.onlab.onos.net.Device;
11 +import org.onlab.onos.net.DeviceId;
12 +import org.onlab.onos.net.Port;
13 +import org.onlab.onos.net.device.DeviceEvent;
14 +import org.onlab.onos.net.device.DeviceListener;
15 +import org.onlab.onos.net.device.DeviceService;
16 +import org.onlab.onos.net.link.LinkProvider;
17 +import org.onlab.onos.net.link.LinkProviderRegistry;
18 +import org.onlab.onos.net.link.LinkProviderService;
19 +import org.onlab.onos.net.packet.PacketContext;
20 +import org.onlab.onos.net.packet.PacketProcessor;
21 +import org.onlab.onos.net.packet.PacketService;
22 +import org.onlab.onos.net.provider.AbstractProvider;
23 +import org.onlab.onos.net.provider.ProviderId;
24 +import org.slf4j.Logger;
25 +
26 +import java.util.Map;
27 +import java.util.concurrent.ConcurrentHashMap;
28 +
29 +import static org.slf4j.LoggerFactory.getLogger;
30 +
31 +
32 +/**
33 + * Provider which uses an OpenFlow controller to detect network
34 + * infrastructure links.
35 + */
36 +@Component(immediate = true)
37 +public class LLDPLinkProvider extends AbstractProvider implements LinkProvider {
38 +
39 + private final Logger log = getLogger(getClass());
40 +
41 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
42 + protected LinkProviderRegistry providerRegistry;
43 +
44 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
45 + protected DeviceService deviceService;
46 +
47 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
48 + protected PacketService packetSevice;
49 +
50 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
51 + protected MastershipService masterService;
52 +
53 + private LinkProviderService providerService;
54 +
55 + private final boolean useBDDP = true;
56 +
57 +
58 + private final InternalLinkProvider listener = new InternalLinkProvider();
59 +
60 + protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
61 +
62 + /**
63 + * Creates an OpenFlow link provider.
64 + */
65 + public LLDPLinkProvider() {
66 + super(new ProviderId("lldp", "org.onlab.onos.provider.lldp"));
67 + }
68 +
69 + @Activate
70 + public void activate() {
71 + providerService = providerRegistry.register(this);
72 + deviceService.addListener(listener);
73 + packetSevice.addProcessor(listener, 0);
74 +
75 + log.info("Started");
76 + }
77 +
78 + @Deactivate
79 + public void deactivate() {
80 + for (LinkDiscovery ld : discoverers.values()) {
81 + ld.stop();
82 + }
83 + providerRegistry.unregister(this);
84 + deviceService.removeListener(listener);
85 + packetSevice.removeProcessor(listener);
86 + providerService = null;
87 +
88 + log.info("Stopped");
89 + }
90 +
91 +
92 + private class InternalLinkProvider implements PacketProcessor, DeviceListener {
93 +
94 + @Override
95 + public void event(DeviceEvent event) {
96 + LinkDiscovery ld = null;
97 + Device device = event.subject();
98 + Port port = event.port();
99 + switch (event.type()) {
100 + case DEVICE_ADDED:
101 + discoverers.put(device.id(),
102 + new LinkDiscovery(device, packetSevice, masterService,
103 + providerService, useBDDP));
104 + break;
105 + case PORT_ADDED:
106 + case PORT_UPDATED:
107 + if (event.port().isEnabled()) {
108 + ld = discoverers.get(device.id());
109 + if (ld == null) {
110 + return;
111 + }
112 + ld.addPort(port);
113 + } else {
114 + ConnectPoint point = new ConnectPoint(device.id(),
115 + port.number());
116 + providerService.linksVanished(point);
117 + }
118 + break;
119 + case PORT_REMOVED:
120 + ConnectPoint point = new ConnectPoint(device.id(),
121 + port.number());
122 + providerService.linksVanished(point);
123 + break;
124 + case DEVICE_REMOVED:
125 + case DEVICE_SUSPENDED:
126 + ld = discoverers.get(device.id());
127 + if (ld == null) {
128 + return;
129 + }
130 + ld.stop();
131 + providerService.linksVanished(device.id());
132 + break;
133 + case DEVICE_AVAILABILITY_CHANGED:
134 + ld = discoverers.get(device.id());
135 + if (ld == null) {
136 + return;
137 + }
138 + if (deviceService.isAvailable(device.id())) {
139 + ld.start();
140 + } else {
141 + providerService.linksVanished(device.id());
142 + ld.stop();
143 + }
144 + break;
145 + case DEVICE_UPDATED:
146 + case DEVICE_MASTERSHIP_CHANGED:
147 + break;
148 + default:
149 + log.debug("Unknown event {}", event);
150 + }
151 + }
152 +
153 + @Override
154 + public void process(PacketContext context) {
155 + if (context == null) {
156 + return;
157 + }
158 + LinkDiscovery ld = discoverers.get(
159 + context.inPacket().receivedFrom().deviceId());
160 + if (ld == null) {
161 + return;
162 + }
163 +
164 + if (ld.handleLLDP(context)) {
165 + context.block();
166 + }
167 + }
168 + }
169 +
170 +}
1 +/*******************************************************************************
2 + * Copyright 2014 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + ******************************************************************************/
16 +package org.onlab.onos.provider.lldp.impl;
17 +
18 +
19 +import static org.slf4j.LoggerFactory.getLogger;
20 +
21 +import java.nio.ByteBuffer;
22 +import java.util.Collections;
23 +import java.util.HashMap;
24 +import java.util.HashSet;
25 +import java.util.Iterator;
26 +import java.util.Map;
27 +import java.util.Set;
28 +import java.util.concurrent.TimeUnit;
29 +import java.util.concurrent.atomic.AtomicInteger;
30 +
31 +import org.jboss.netty.util.Timeout;
32 +import org.jboss.netty.util.TimerTask;
33 +import org.onlab.onos.mastership.MastershipService;
34 +import org.onlab.onos.net.ConnectPoint;
35 +import org.onlab.onos.net.Device;
36 +import org.onlab.onos.net.DeviceId;
37 +import org.onlab.onos.net.Link.Type;
38 +import org.onlab.onos.net.MastershipRole;
39 +import org.onlab.onos.net.Port;
40 +import org.onlab.onos.net.PortNumber;
41 +import org.onlab.onos.net.flow.DefaultTrafficTreatment;
42 +import org.onlab.onos.net.link.DefaultLinkDescription;
43 +import org.onlab.onos.net.link.LinkDescription;
44 +import org.onlab.onos.net.link.LinkProviderService;
45 +import org.onlab.onos.net.packet.DefaultOutboundPacket;
46 +import org.onlab.onos.net.packet.OutboundPacket;
47 +import org.onlab.onos.net.packet.PacketContext;
48 +import org.onlab.onos.net.packet.PacketService;
49 +import org.onlab.packet.Ethernet;
50 +import org.onlab.packet.ONOSLLDP;
51 +import org.onlab.util.Timer;
52 +import org.slf4j.Logger;
53 +
54 +
55 +
56 +/**
57 + * Run discovery process from a physical switch. Ports are initially labeled as
58 + * slow ports. When an LLDP is successfully received, label the remote port as
59 + * fast. Every probeRate milliseconds, loop over all fast ports and send an
60 + * LLDP, send an LLDP for a single slow port. Based on FlowVisor topology
61 + * discovery implementation.
62 + *
63 + * TODO: add 'fast discovery' mode: drop LLDPs in destination switch but listen
64 + * for flow_removed messages
65 + */
66 +public class LinkDiscovery implements TimerTask {
67 +
68 + private final Device device;
69 + // send 1 probe every probeRate milliseconds
70 + private final long probeRate;
71 + private final Set<Long> slowPorts;
72 + private final Set<Long> fastPorts;
73 + // number of unacknowledged probes per port
74 + private final Map<Long, AtomicInteger> portProbeCount;
75 + // number of probes to send before link is removed
76 + private static final short MAX_PROBE_COUNT = 3;
77 + private final Logger log = getLogger(getClass());
78 + private final ONOSLLDP lldpPacket;
79 + private final Ethernet ethPacket;
80 + private Ethernet bddpEth;
81 + private final boolean useBDDP;
82 + private final LinkProviderService linkProvider;
83 + private final PacketService pktService;
84 + private final MastershipService mastershipService;
85 + private Timeout timeout;
86 + private boolean isStopped;
87 +
88 + /**
89 + * Instantiates discovery manager for the given physical switch. Creates a
90 + * generic LLDP packet that will be customized for the port it is sent out on.
91 + * Starts the the timer for the discovery process.
92 + * @param device the physical switch
93 + * @param masterService
94 + * @param useBDDP flag to also use BDDP for discovery
95 + */
96 + public LinkDiscovery(Device device, PacketService pktService,
97 + MastershipService masterService, LinkProviderService providerService, Boolean... useBDDP) {
98 + this.device = device;
99 + this.probeRate = 3000;
100 + this.linkProvider = providerService;
101 + this.pktService = pktService;
102 + this.mastershipService = masterService;
103 + this.slowPorts = Collections.synchronizedSet(new HashSet<Long>());
104 + this.fastPorts = Collections.synchronizedSet(new HashSet<Long>());
105 + this.portProbeCount = new HashMap<>();
106 + this.lldpPacket = new ONOSLLDP();
107 + this.lldpPacket.setChassisId(device.chassisId());
108 + this.lldpPacket.setDevice(device.id().toString());
109 +
110 +
111 + this.ethPacket = new Ethernet();
112 + this.ethPacket.setEtherType(Ethernet.TYPE_LLDP);
113 + this.ethPacket.setDestinationMACAddress(ONOSLLDP.LLDP_NICIRA);
114 + this.ethPacket.setPayload(this.lldpPacket);
115 + this.ethPacket.setPad(true);
116 + this.useBDDP = useBDDP.length > 0 ? useBDDP[0] : false;
117 + if (this.useBDDP) {
118 + this.bddpEth = new Ethernet();
119 + this.bddpEth.setPayload(this.lldpPacket);
120 + this.bddpEth.setEtherType(Ethernet.TYPE_BSN);
121 + this.bddpEth.setDestinationMACAddress(ONOSLLDP.BDDP_MULTICAST);
122 + this.bddpEth.setPad(true);
123 + log.info("Using BDDP to discover network");
124 + }
125 +
126 + start();
127 + this.log.debug("Started discovery manager for switch {}",
128 + device.id());
129 +
130 + }
131 +
132 + /**
133 + * Add physical port port to discovery process.
134 + * Send out initial LLDP and label it as slow port.
135 + *
136 + * @param port the port
137 + */
138 + public void addPort(final Port port) {
139 + this.log.debug("sending init probe to port {}",
140 + port.number().toLong());
141 +
142 + sendProbes(port.number().toLong());
143 +
144 + synchronized (this) {
145 + this.slowPorts.add(port.number().toLong());
146 + }
147 +
148 +
149 + }
150 +
151 + /**
152 + * Removes physical port from discovery process.
153 + *
154 + * @param port the port
155 + */
156 + public void removePort(final Port port) {
157 + // Ignore ports that are not on this switch
158 +
159 + long portnum = port.number().toLong();
160 + synchronized (this) {
161 + if (this.slowPorts.contains(portnum)) {
162 + this.slowPorts.remove(portnum);
163 +
164 + } else if (this.fastPorts.contains(portnum)) {
165 + this.fastPorts.remove(portnum);
166 + this.portProbeCount.remove(portnum);
167 + // no iterator to update
168 + } else {
169 + this.log.warn(
170 + "tried to dynamically remove non-existing port {}",
171 + portnum);
172 + }
173 + }
174 + }
175 +
176 + /**
177 + * Method called by remote port to acknowledge receipt of LLDP sent by
178 + * this port. If slow port, updates label to fast. If fast port, decrements
179 + * number of unacknowledged probes.
180 + *
181 + * @param portNumber the port
182 + */
183 + public void ackProbe(final Long portNumber) {
184 +
185 + synchronized (this) {
186 + if (this.slowPorts.contains(portNumber)) {
187 + this.log.debug("Setting slow port to fast: {}:{}",
188 + this.device.id(), portNumber);
189 + this.slowPorts.remove(portNumber);
190 + this.fastPorts.add(portNumber);
191 + this.portProbeCount.put(portNumber, new AtomicInteger(0));
192 + } else if (this.fastPorts.contains(portNumber)) {
193 + this.portProbeCount.get(portNumber).set(0);
194 + } else {
195 + this.log.debug(
196 + "Got ackProbe for non-existing port: {}",
197 + portNumber);
198 +
199 + }
200 + }
201 + }
202 +
203 +
204 + /**
205 + * Handles an incoming LLDP packet. Creates link in topology and sends ACK
206 + * to port where LLDP originated.
207 + */
208 + public boolean handleLLDP(PacketContext context) {
209 + Ethernet eth = context.inPacket().parsed();
210 + ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
211 + if (onoslldp != null) {
212 + final PortNumber dstPort =
213 + context.inPacket().receivedFrom().port();
214 + final PortNumber srcPort = PortNumber.portNumber(onoslldp.getPort());
215 + final DeviceId srcDeviceId = DeviceId.deviceId(onoslldp.getDeviceString());
216 + final DeviceId dstDeviceId = context.inPacket().receivedFrom().deviceId();
217 + this.ackProbe(srcPort.toLong());
218 + ConnectPoint src = new ConnectPoint(srcDeviceId, srcPort);
219 + ConnectPoint dst = new ConnectPoint(dstDeviceId, dstPort);
220 +
221 + LinkDescription ld;
222 + if (eth.getEtherType() == Ethernet.TYPE_BSN) {
223 + ld = new DefaultLinkDescription(src, dst, Type.INDIRECT);
224 + } else {
225 + ld = new DefaultLinkDescription(src, dst, Type.DIRECT);
226 + }
227 + linkProvider.linkDetected(ld);
228 + return true;
229 + }
230 + return false;
231 + }
232 +
233 +
234 +
235 + /**
236 + * Execute this method every t milliseconds. Loops over all ports
237 + * labeled as fast and sends out an LLDP. Send out an LLDP on a single slow
238 + * port.
239 + *
240 + * @param t timeout
241 + * @throws Exception
242 + */
243 + @Override
244 + public void run(final Timeout t) {
245 + this.log.debug("sending probes");
246 + synchronized (this) {
247 + final Iterator<Long> fastIterator = this.fastPorts.iterator();
248 + Long portNumber;
249 + Integer probeCount;
250 + while (fastIterator.hasNext()) {
251 + portNumber = fastIterator.next();
252 + probeCount = this.portProbeCount.get(portNumber)
253 + .getAndIncrement();
254 +
255 + if (probeCount < LinkDiscovery.MAX_PROBE_COUNT) {
256 + this.log.debug("sending fast probe to port");
257 + sendProbes(portNumber);
258 + } else {
259 + // Update fast and slow ports
260 + //fastIterator.remove();
261 + //this.slowPorts.add(portNumber);
262 + //this.portProbeCount.remove(portNumber);
263 + this.portProbeCount.get(portNumber).set(0);
264 +
265 + ConnectPoint cp = new ConnectPoint(
266 + device.id(),
267 + PortNumber.portNumber(portNumber));
268 + log.debug("Link down -> {}", cp);
269 + linkProvider.linksVanished(cp);
270 + }
271 + }
272 +
273 + // send a probe for the next slow port
274 + if (!this.slowPorts.isEmpty()) {
275 + Iterator<Long> slowIterator = this.slowPorts.iterator();
276 + while (slowIterator.hasNext()) {
277 + portNumber = slowIterator.next();
278 + this.log.debug("sending slow probe to port {}", portNumber);
279 +
280 + sendProbes(portNumber);
281 +
282 + }
283 + }
284 + }
285 +
286 + // reschedule timer
287 + timeout = Timer.getTimer().newTimeout(this, this.probeRate,
288 + TimeUnit.MILLISECONDS);
289 + }
290 +
291 + public void stop() {
292 + timeout.cancel();
293 + isStopped = true;
294 + }
295 +
296 + public void start() {
297 + timeout = Timer.getTimer().newTimeout(this, 0,
298 + TimeUnit.MILLISECONDS);
299 + isStopped = false;
300 + }
301 +
302 + /**
303 + * Creates packet_out LLDP for specified output port.
304 + *
305 + * @param port the port
306 + * @return Packet_out message with LLDP data
307 + */
308 + private OutboundPacket createOutBoundLLDP(final Long port) {
309 + if (port == null) {
310 + return null;
311 + }
312 + this.lldpPacket.setPortId(port.intValue());
313 + this.ethPacket.setSourceMACAddress("DE:AD:BE:EF:BA:11");
314 +
315 + final byte[] lldp = this.ethPacket.serialize();
316 + OutboundPacket outboundPacket = new DefaultOutboundPacket(
317 + this.device.id(),
318 + DefaultTrafficTreatment.builder().setOutput(
319 + PortNumber.portNumber(port)).build(),
320 + ByteBuffer.wrap(lldp));
321 + return outboundPacket;
322 + }
323 +
324 + /**
325 + * Creates packet_out BDDP for specified output port.
326 + *
327 + * @param port the port
328 + * @return Packet_out message with LLDP data
329 + */
330 + private OutboundPacket createOutBoundBDDP(final Long port) {
331 + if (port == null) {
332 + return null;
333 + }
334 + this.lldpPacket.setPortId(port.intValue());
335 + this.bddpEth.setSourceMACAddress("DE:AD:BE:EF:BA:11");
336 +
337 + final byte[] bddp = this.bddpEth.serialize();
338 + OutboundPacket outboundPacket = new DefaultOutboundPacket(
339 + this.device.id(),
340 + DefaultTrafficTreatment.builder()
341 + .setOutput(PortNumber.portNumber(port)).build(),
342 + ByteBuffer.wrap(bddp));
343 + return outboundPacket;
344 + }
345 +
346 + private void sendProbes(Long portNumber) {
347 + if (mastershipService.getLocalRole(this.device.id()) ==
348 + MastershipRole.MASTER) {
349 + OutboundPacket pkt = this.createOutBoundLLDP(portNumber);
350 + pktService.emit(pkt);
351 + if (useBDDP) {
352 + OutboundPacket bpkt = this.createOutBoundBDDP(portNumber);
353 + pktService.emit(bpkt);
354 + }
355 + }
356 + }
357 +
358 + public boolean containsPort(Long portNumber) {
359 + if (slowPorts.contains(portNumber) || fastPorts.contains(portNumber)) {
360 + return true;
361 + }
362 + return false;
363 + }
364 +
365 + public boolean isStopped() {
366 + return isStopped;
367 + }
368 +
369 +}
1 +/**
2 + * Provider that uses the core as a means of infrastructure link inference.
3 + */
4 +package org.onlab.onos.provider.lldp.impl;
1 +package org.onlab.onos.provider.lldp.impl;
2 +
3 +
4 +import com.google.common.collect.ArrayListMultimap;
5 +import com.google.common.collect.Lists;
6 +import com.google.common.collect.Maps;
7 +
8 +import org.junit.After;
9 +import org.junit.Before;
10 +import org.junit.Test;
11 +import org.onlab.onos.cluster.NodeId;
12 +import org.onlab.onos.mastership.MastershipListener;
13 +import org.onlab.onos.mastership.MastershipService;
14 +import org.onlab.onos.mastership.MastershipTermService;
15 +import org.onlab.onos.net.ConnectPoint;
16 +import org.onlab.onos.net.DefaultDevice;
17 +import org.onlab.onos.net.DefaultPort;
18 +import org.onlab.onos.net.Device;
19 +import org.onlab.onos.net.DeviceId;
20 +import org.onlab.onos.net.MastershipRole;
21 +import org.onlab.onos.net.Port;
22 +import org.onlab.onos.net.PortNumber;
23 +import org.onlab.onos.net.device.DeviceEvent;
24 +import org.onlab.onos.net.device.DeviceListener;
25 +import org.onlab.onos.net.device.DeviceService;
26 +import org.onlab.onos.net.flow.TrafficTreatment;
27 +import org.onlab.onos.net.link.LinkDescription;
28 +import org.onlab.onos.net.link.LinkProvider;
29 +import org.onlab.onos.net.link.LinkProviderRegistry;
30 +import org.onlab.onos.net.link.LinkProviderService;
31 +import org.onlab.onos.net.packet.DefaultInboundPacket;
32 +import org.onlab.onos.net.packet.InboundPacket;
33 +import org.onlab.onos.net.packet.OutboundPacket;
34 +import org.onlab.onos.net.packet.PacketContext;
35 +import org.onlab.onos.net.packet.PacketProcessor;
36 +import org.onlab.onos.net.packet.PacketService;
37 +import org.onlab.onos.net.provider.AbstractProviderService;
38 +import org.onlab.onos.net.provider.ProviderId;
39 +import org.onlab.packet.ChassisId;
40 +import org.onlab.packet.Ethernet;
41 +import org.onlab.packet.ONOSLLDP;
42 +
43 +import java.nio.ByteBuffer;
44 +import java.util.Collections;
45 +import java.util.HashMap;
46 +import java.util.List;
47 +import java.util.Map;
48 +import java.util.Set;
49 +
50 +import static org.junit.Assert.*;
51 +
52 +public class LLDPLinkProviderTest {
53 +
54 + private static final DeviceId DID1 = DeviceId.deviceId("of:0000000000000001");
55 + private static final DeviceId DID2 = DeviceId.deviceId("of:0000000000000002");
56 +
57 + private static Port pd1;
58 + private static Port pd2;
59 + private static Port pd3;
60 + private static Port pd4;
61 +
62 + private final LLDPLinkProvider provider = new LLDPLinkProvider();
63 + private final TestLinkRegistry linkService = new TestLinkRegistry();
64 + private final TestPacketService packetService = new TestPacketService();
65 + private final TestDeviceService deviceService = new TestDeviceService();
66 + private final TestMasterShipService masterService = new TestMasterShipService();
67 +
68 + private TestLinkProviderService providerService;
69 +
70 + private PacketProcessor testProcessor;
71 + private DeviceListener deviceListener;
72 +
73 + @Before
74 + public void setUp() {
75 +
76 + provider.deviceService = deviceService;
77 + provider.packetSevice = packetService;
78 + provider.providerRegistry = linkService;
79 + provider.masterService = masterService;
80 +
81 +
82 + provider.activate();
83 + }
84 +
85 + @Test
86 + public void basics() {
87 + assertNotNull("registration expected", providerService);
88 + assertEquals("incorrect provider", provider, providerService.provider());
89 + }
90 +
91 + @Test
92 + public void switchAdd() {
93 + DeviceEvent de = deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1);
94 + deviceListener.event(de);
95 +
96 + assertFalse("Device not added", provider.discoverers.isEmpty());
97 + }
98 +
99 + @Test
100 + public void switchRemove() {
101 + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1));
102 + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_REMOVED, DID1));
103 +
104 + assertTrue("Discoverer is not gone", provider.discoverers.get(DID1).isStopped());
105 + assertTrue("Device is not gone.", vanishedDpid(DID1));
106 + }
107 +
108 + @Test
109 + public void portUp() {
110 + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1));
111 + deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID1, port(DID1, 3, true)));
112 +
113 + assertTrue("Port not added to discoverer",
114 + provider.discoverers.get(DID1).containsPort((long) 3));
115 + }
116 +
117 + @Test
118 + public void portDown() {
119 +
120 + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1));
121 + deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID1, port(DID1, 1, false)));
122 +
123 +
124 +
125 + assertFalse("Port added to discoverer",
126 + provider.discoverers.get(DID1).containsPort((long) 1));
127 + assertTrue("Port is not gone.", vanishedPort((long) 1));
128 + }
129 +
130 + @Test
131 + public void portUnknown() {
132 + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1));
133 + deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID2, port(DID2, 1, false)));
134 +
135 +
136 + assertNull("DPID exists",
137 + provider.discoverers.get(DID2));
138 + }
139 +
140 + @Test
141 + public void unknownPktCtx() {
142 +
143 + PacketContext pktCtx = new TestPacketContext(deviceService.getDevice(DID2));
144 +
145 + testProcessor.process(pktCtx);
146 + assertFalse("Context should still be free", pktCtx.isHandled());
147 + }
148 +
149 + @Test
150 + public void knownPktCtx() {
151 + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1));
152 + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID2));
153 + PacketContext pktCtx = new TestPacketContext(deviceService.getDevice(DID2));
154 +
155 +
156 + testProcessor.process(pktCtx);
157 +
158 + assertTrue("Link not detected", detectedLink(DID1, DID2));
159 +
160 + }
161 +
162 +
163 + @After
164 + public void tearDown() {
165 + provider.deactivate();
166 + provider.providerRegistry = null;
167 + provider.deviceService = null;
168 + provider.packetSevice = null;
169 + }
170 +
171 + private DeviceEvent deviceEvent(DeviceEvent.Type type, DeviceId did) {
172 + return new DeviceEvent(type, deviceService.getDevice(did));
173 +
174 + }
175 +
176 + private DeviceEvent portEvent(DeviceEvent.Type type, DeviceId did, PortNumber port) {
177 + return new DeviceEvent(type, deviceService.getDevice(did),
178 + deviceService.getPort(did, port));
179 + }
180 +
181 + private DeviceEvent portEvent(DeviceEvent.Type type, DeviceId did, Port port) {
182 + return new DeviceEvent(type, deviceService.getDevice(did), port);
183 + }
184 +
185 + private Port port(DeviceId did, long port, boolean enabled) {
186 + return new DefaultPort(deviceService.getDevice(did),
187 + PortNumber.portNumber(port), enabled);
188 + }
189 +
190 +
191 + private boolean vanishedDpid(DeviceId... dids) {
192 + for (int i = 0; i < dids.length; i++) {
193 + if (!providerService.vanishedDpid.contains(dids[i])) {
194 + return false;
195 + }
196 + }
197 + return true;
198 + }
199 +
200 + private boolean vanishedPort(Long... ports) {
201 + for (int i = 0; i < ports.length; i++) {
202 + if (!providerService.vanishedPort.contains(ports[i])) {
203 + return false;
204 + }
205 + }
206 + return true;
207 + }
208 +
209 + private boolean detectedLink(DeviceId src, DeviceId dst) {
210 + for (DeviceId key : providerService.discoveredLinks.keySet()) {
211 + if (key.equals(src)) {
212 + return providerService.discoveredLinks.get(src).equals(dst);
213 + }
214 + }
215 + return false;
216 + }
217 +
218 +
219 + private class TestLinkRegistry implements LinkProviderRegistry {
220 +
221 + @Override
222 + public LinkProviderService register(LinkProvider provider) {
223 + providerService = new TestLinkProviderService(provider);
224 + return providerService;
225 + }
226 +
227 + @Override
228 + public void unregister(LinkProvider provider) {
229 + }
230 +
231 + @Override
232 + public Set<ProviderId> getProviders() {
233 + return null;
234 + }
235 +
236 + }
237 +
238 + private class TestLinkProviderService
239 + extends AbstractProviderService<LinkProvider>
240 + implements LinkProviderService {
241 +
242 + List<DeviceId> vanishedDpid = Lists.newLinkedList();
243 + List<Long> vanishedPort = Lists.newLinkedList();
244 + Map<DeviceId, DeviceId> discoveredLinks = Maps.newHashMap();
245 +
246 + protected TestLinkProviderService(LinkProvider provider) {
247 + super(provider);
248 + }
249 +
250 + @Override
251 + public void linkDetected(LinkDescription linkDescription) {
252 + DeviceId sDid = linkDescription.src().deviceId();
253 + DeviceId dDid = linkDescription.dst().deviceId();
254 + discoveredLinks.put(sDid, dDid);
255 + }
256 +
257 + @Override
258 + public void linkVanished(LinkDescription linkDescription) {
259 + // TODO Auto-generated method stub
260 +
261 + }
262 +
263 + @Override
264 + public void linksVanished(ConnectPoint connectPoint) {
265 + vanishedPort.add(connectPoint.port().toLong());
266 +
267 + }
268 +
269 + @Override
270 + public void linksVanished(DeviceId deviceId) {
271 + vanishedDpid.add(deviceId);
272 + }
273 +
274 +
275 + }
276 +
277 +
278 +
279 + private class TestPacketContext implements PacketContext {
280 +
281 + protected Device device;
282 + protected boolean blocked = false;
283 +
284 + public TestPacketContext(Device dev) {
285 + device = dev;
286 + }
287 +
288 + @Override
289 + public long time() {
290 + return 0;
291 + }
292 +
293 + @Override
294 + public InboundPacket inPacket() {
295 + ONOSLLDP lldp = new ONOSLLDP();
296 + lldp.setChassisId(device.chassisId());
297 + lldp.setPortId((int) pd1.number().toLong());
298 + lldp.setDevice(deviceService.getDevice(DID1).id().toString());
299 +
300 +
301 + Ethernet ethPacket = new Ethernet();
302 + ethPacket.setEtherType(Ethernet.TYPE_LLDP);
303 + ethPacket.setDestinationMACAddress(ONOSLLDP.LLDP_NICIRA);
304 + ethPacket.setPayload(lldp);
305 + ethPacket.setPad(true);
306 +
307 +
308 +
309 + ethPacket.setSourceMACAddress("DE:AD:BE:EF:BA:11");
310 +
311 + ConnectPoint cp = new ConnectPoint(device.id(), pd3.number());
312 +
313 + return new DefaultInboundPacket(cp, ethPacket,
314 + ByteBuffer.wrap(ethPacket.serialize()));
315 +
316 + }
317 +
318 + @Override
319 + public OutboundPacket outPacket() {
320 + return null;
321 + }
322 +
323 + @Override
324 + public TrafficTreatment.Builder treatmentBuilder() {
325 + return null;
326 + }
327 +
328 + @Override
329 + public void send() {
330 +
331 + }
332 +
333 + @Override
334 + public boolean block() {
335 + blocked = true;
336 + return blocked;
337 + }
338 +
339 + @Override
340 + public boolean isHandled() {
341 + return blocked;
342 + }
343 +
344 + }
345 +
346 + private class TestPacketService implements PacketService {
347 +
348 + @Override
349 + public void addProcessor(PacketProcessor processor, int priority) {
350 + testProcessor = processor;
351 + }
352 +
353 + @Override
354 + public void removeProcessor(PacketProcessor processor) {
355 +
356 + }
357 +
358 + @Override
359 + public void emit(OutboundPacket packet) {
360 +
361 + }
362 + }
363 +
364 + private class TestDeviceService implements DeviceService {
365 +
366 + private Map<DeviceId, Device> devices = new HashMap<>();
367 + private final ArrayListMultimap<DeviceId, Port> ports =
368 + ArrayListMultimap.create();
369 +
370 + public TestDeviceService() {
371 + Device d1 = new DefaultDevice(ProviderId.NONE, DID1, Device.Type.SWITCH,
372 + "TESTMF", "TESTHW", "TESTSW", "TESTSN", new ChassisId());
373 + Device d2 = new DefaultDevice(ProviderId.NONE, DID2, Device.Type.SWITCH,
374 + "TESTMF", "TESTHW", "TESTSW", "TESTSN", new ChassisId());
375 + devices.put(DID1, d1);
376 + devices.put(DID2, d2);
377 +
378 + pd1 = new DefaultPort(d1, PortNumber.portNumber(1), true);
379 + pd2 = new DefaultPort(d1, PortNumber.portNumber(2), true);
380 + pd3 = new DefaultPort(d2, PortNumber.portNumber(1), true);
381 + pd4 = new DefaultPort(d2, PortNumber.portNumber(2), true);
382 +
383 + ports.putAll(DID1, Lists.newArrayList(pd1, pd2));
384 + ports.putAll(DID2, Lists.newArrayList(pd3, pd4));
385 +
386 +
387 + }
388 +
389 + @Override
390 + public int getDeviceCount() {
391 + return devices.values().size();
392 + }
393 +
394 + @Override
395 + public Iterable<Device> getDevices() {
396 + return devices.values();
397 + }
398 +
399 + @Override
400 + public Device getDevice(DeviceId deviceId) {
401 + return devices.get(deviceId);
402 + }
403 +
404 + @Override
405 + public MastershipRole getRole(DeviceId deviceId) {
406 + return MastershipRole.MASTER;
407 + }
408 +
409 + @Override
410 + public List<Port> getPorts(DeviceId deviceId) {
411 + return ports.get(deviceId);
412 + }
413 +
414 + @Override
415 + public Port getPort(DeviceId deviceId, PortNumber portNumber) {
416 + for (Port p : ports.get(deviceId)) {
417 + if (p.number().equals(portNumber)) {
418 + return p;
419 + }
420 + }
421 + return null;
422 + }
423 +
424 + @Override
425 + public boolean isAvailable(DeviceId deviceId) {
426 + return true;
427 + }
428 +
429 + @Override
430 + public void addListener(DeviceListener listener) {
431 + deviceListener = listener;
432 +
433 + }
434 +
435 + @Override
436 + public void removeListener(DeviceListener listener) {
437 +
438 + }
439 + }
440 +
441 + private final class TestMasterShipService implements MastershipService {
442 +
443 + @Override
444 + public MastershipRole getLocalRole(DeviceId deviceId) {
445 + return MastershipRole.MASTER;
446 + }
447 +
448 + @Override
449 + public MastershipRole requestRoleFor(DeviceId deviceId) {
450 + return null;
451 + }
452 +
453 + @Override
454 + public void relinquishMastership(DeviceId deviceId) {
455 +
456 + }
457 +
458 + @Override
459 + public NodeId getMasterFor(DeviceId deviceId) {
460 + return null;
461 + }
462 +
463 + @Override
464 + public Set<DeviceId> getDevicesOf(NodeId nodeId) {
465 + return null;
466 + }
467 +
468 + @Override
469 + public MastershipTermService requestTermService() {
470 + return null;
471 + }
472 +
473 + @Override
474 + public void addListener(MastershipListener listener) {
475 +
476 + }
477 +
478 + @Override
479 + public void removeListener(MastershipListener listener) {
480 +
481 + }
482 +
483 + @Override
484 + public List<NodeId> getNodesFor(DeviceId deviceId) {
485 + return Collections.emptyList();
486 + }
487 + }
488 +
489 +
490 +}
...@@ -23,6 +23,7 @@ import org.onlab.onos.openflow.controller.OpenFlowController; ...@@ -23,6 +23,7 @@ import org.onlab.onos.openflow.controller.OpenFlowController;
23 import org.onlab.onos.openflow.controller.OpenFlowSwitch; 23 import org.onlab.onos.openflow.controller.OpenFlowSwitch;
24 import org.onlab.onos.openflow.controller.OpenFlowSwitchListener; 24 import org.onlab.onos.openflow.controller.OpenFlowSwitchListener;
25 import org.onlab.onos.openflow.controller.RoleState; 25 import org.onlab.onos.openflow.controller.RoleState;
26 +import org.onlab.packet.ChassisId;
26 import org.projectfloodlight.openflow.protocol.OFPortConfig; 27 import org.projectfloodlight.openflow.protocol.OFPortConfig;
27 import org.projectfloodlight.openflow.protocol.OFPortDesc; 28 import org.projectfloodlight.openflow.protocol.OFPortDesc;
28 import org.projectfloodlight.openflow.protocol.OFPortState; 29 import org.projectfloodlight.openflow.protocol.OFPortState;
...@@ -120,12 +121,14 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr ...@@ -120,12 +121,14 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
120 121
121 Device.Type deviceType = sw.isOptical() ? Device.Type.ROADM : 122 Device.Type deviceType = sw.isOptical() ? Device.Type.ROADM :
122 Device.Type.SWITCH; 123 Device.Type.SWITCH;
124 + ChassisId cId = new ChassisId(dpid.value());
123 DeviceDescription description = 125 DeviceDescription description =
124 new DefaultDeviceDescription(did.uri(), deviceType, 126 new DefaultDeviceDescription(did.uri(), deviceType,
125 sw.manfacturerDescription(), 127 sw.manfacturerDescription(),
126 sw.hardwareDescription(), 128 sw.hardwareDescription(),
127 sw.softwareDescription(), 129 sw.softwareDescription(),
128 - sw.serialNumber()); 130 + sw.serialNumber(),
131 + cId);
129 providerService.deviceConnected(did, description); 132 providerService.deviceConnected(did, description);
130 providerService.updatePorts(did, buildPortDescriptions(sw.getPorts())); 133 providerService.updatePorts(did, buildPortDescriptions(sw.getPorts()));
131 } 134 }
...@@ -172,7 +175,7 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr ...@@ -172,7 +175,7 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
172 */ 175 */
173 private List<PortDescription> buildPortDescriptions( 176 private List<PortDescription> buildPortDescriptions(
174 List<OFPortDesc> ports) { 177 List<OFPortDesc> ports) {
175 - final List<PortDescription> portDescs = new ArrayList<>(); 178 + final List<PortDescription> portDescs = new ArrayList<>(ports.size());
176 for (OFPortDesc port : ports) { 179 for (OFPortDesc port : ports) {
177 portDescs.add(buildPortDescription(port)); 180 portDescs.add(buildPortDescription(port));
178 } 181 }
......
...@@ -59,7 +59,7 @@ public class OpenFlowDeviceProviderTest { ...@@ -59,7 +59,7 @@ public class OpenFlowDeviceProviderTest {
59 private static final List<OFPortDesc> PLIST = Lists.newArrayList(PD1, PD2); 59 private static final List<OFPortDesc> PLIST = Lists.newArrayList(PD1, PD2);
60 60
61 private static final Device DEV1 = 61 private static final Device DEV1 =
62 - new DefaultDevice(PID, DID1, SWITCH, "", "", "", ""); 62 + new DefaultDevice(PID, DID1, SWITCH, "", "", "", "", null);
63 63
64 private static final TestOpenFlowSwitch SW1 = new TestOpenFlowSwitch(); 64 private static final TestOpenFlowSwitch SW1 = new TestOpenFlowSwitch();
65 65
......
...@@ -35,6 +35,7 @@ import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyAct ...@@ -35,6 +35,7 @@ import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyAct
35 import org.projectfloodlight.openflow.protocol.match.Match; 35 import org.projectfloodlight.openflow.protocol.match.Match;
36 import org.projectfloodlight.openflow.protocol.match.MatchField; 36 import org.projectfloodlight.openflow.protocol.match.MatchField;
37 import org.projectfloodlight.openflow.types.IPv4Address; 37 import org.projectfloodlight.openflow.types.IPv4Address;
38 +import org.projectfloodlight.openflow.types.Masked;
38 import org.slf4j.Logger; 39 import org.slf4j.Logger;
39 40
40 import com.google.common.collect.Lists; 41 import com.google.common.collect.Lists;
...@@ -218,23 +219,35 @@ public class FlowEntryBuilder { ...@@ -218,23 +219,35 @@ public class FlowEntryBuilder {
218 builder.matchEthType((short) ethType); 219 builder.matchEthType((short) ethType);
219 break; 220 break;
220 case IPV4_DST: 221 case IPV4_DST:
221 - IPv4Address di = match.get(MatchField.IPV4_DST);
222 IpPrefix dip; 222 IpPrefix dip;
223 - if (di.isCidrMask()) { 223 + if (match.isPartiallyMasked(MatchField.IPV4_DST)) {
224 - dip = IpPrefix.valueOf(di.getInt(), di.asCidrMaskLength()); 224 + Masked<IPv4Address> maskedIp = match.getMasked(MatchField.IPV4_DST);
225 +
226 + dip = IpPrefix.valueOf(
227 + maskedIp.getValue().getInt(),
228 + maskedIp.getMask().asCidrMaskLength());
225 } else { 229 } else {
226 - dip = IpPrefix.valueOf(di.getInt()); 230 + dip = IpPrefix.valueOf(
231 + match.get(MatchField.IPV4_DST).getInt(),
232 + IpPrefix.MAX_INET_MASK);
227 } 233 }
234 +
228 builder.matchIPDst(dip); 235 builder.matchIPDst(dip);
229 break; 236 break;
230 case IPV4_SRC: 237 case IPV4_SRC:
231 - IPv4Address si = match.get(MatchField.IPV4_SRC);
232 IpPrefix sip; 238 IpPrefix sip;
233 - if (si.isCidrMask()) { 239 + if (match.isPartiallyMasked(MatchField.IPV4_SRC)) {
234 - sip = IpPrefix.valueOf(si.getInt(), si.asCidrMaskLength()); 240 + Masked<IPv4Address> maskedIp = match.getMasked(MatchField.IPV4_SRC);
241 +
242 + sip = IpPrefix.valueOf(
243 + maskedIp.getValue().getInt(),
244 + maskedIp.getMask().asCidrMaskLength());
235 } else { 245 } else {
236 - sip = IpPrefix.valueOf(si.getInt()); 246 + sip = IpPrefix.valueOf(
247 + match.get(MatchField.IPV4_SRC).getInt(),
248 + IpPrefix.MAX_INET_MASK);
237 } 249 }
250 +
238 builder.matchIPSrc(sip); 251 builder.matchIPSrc(sip);
239 break; 252 break;
240 case IP_PROTO: 253 case IP_PROTO:
...@@ -249,6 +262,12 @@ public class FlowEntryBuilder { ...@@ -249,6 +262,12 @@ public class FlowEntryBuilder {
249 VlanId vlanId = VlanId.vlanId(match.get(MatchField.VLAN_VID).getVlan()); 262 VlanId vlanId = VlanId.vlanId(match.get(MatchField.VLAN_VID).getVlan());
250 builder.matchVlanId(vlanId); 263 builder.matchVlanId(vlanId);
251 break; 264 break;
265 + case TCP_DST:
266 + builder.matchTcpDst((short) match.get(MatchField.TCP_DST).getPort());
267 + break;
268 + case TCP_SRC:
269 + builder.matchTcpSrc((short) match.get(MatchField.TCP_SRC).getPort());
270 + break;
252 case ARP_OP: 271 case ARP_OP:
253 case ARP_SHA: 272 case ARP_SHA:
254 case ARP_SPA: 273 case ARP_SPA:
...@@ -272,8 +291,6 @@ public class FlowEntryBuilder { ...@@ -272,8 +291,6 @@ public class FlowEntryBuilder {
272 case MPLS_TC: 291 case MPLS_TC:
273 case SCTP_DST: 292 case SCTP_DST:
274 case SCTP_SRC: 293 case SCTP_SRC:
275 - case TCP_DST:
276 - case TCP_SRC:
277 case TUNNEL_ID: 294 case TUNNEL_ID:
278 case UDP_DST: 295 case UDP_DST:
279 case UDP_SRC: 296 case UDP_SRC:
......
...@@ -15,6 +15,7 @@ import org.onlab.onos.net.flow.criteria.Criteria.EthTypeCriterion; ...@@ -15,6 +15,7 @@ import org.onlab.onos.net.flow.criteria.Criteria.EthTypeCriterion;
15 import org.onlab.onos.net.flow.criteria.Criteria.IPCriterion; 15 import org.onlab.onos.net.flow.criteria.Criteria.IPCriterion;
16 import org.onlab.onos.net.flow.criteria.Criteria.IPProtocolCriterion; 16 import org.onlab.onos.net.flow.criteria.Criteria.IPProtocolCriterion;
17 import org.onlab.onos.net.flow.criteria.Criteria.PortCriterion; 17 import org.onlab.onos.net.flow.criteria.Criteria.PortCriterion;
18 +import org.onlab.onos.net.flow.criteria.Criteria.TcpPortCriterion;
18 import org.onlab.onos.net.flow.criteria.Criteria.VlanIdCriterion; 19 import org.onlab.onos.net.flow.criteria.Criteria.VlanIdCriterion;
19 import org.onlab.onos.net.flow.criteria.Criteria.VlanPcpCriterion; 20 import org.onlab.onos.net.flow.criteria.Criteria.VlanPcpCriterion;
20 import org.onlab.onos.net.flow.criteria.Criterion; 21 import org.onlab.onos.net.flow.criteria.Criterion;
...@@ -42,6 +43,7 @@ import org.projectfloodlight.openflow.types.Masked; ...@@ -42,6 +43,7 @@ import org.projectfloodlight.openflow.types.Masked;
42 import org.projectfloodlight.openflow.types.OFBufferId; 43 import org.projectfloodlight.openflow.types.OFBufferId;
43 import org.projectfloodlight.openflow.types.OFPort; 44 import org.projectfloodlight.openflow.types.OFPort;
44 import org.projectfloodlight.openflow.types.OFVlanVidMatch; 45 import org.projectfloodlight.openflow.types.OFVlanVidMatch;
46 +import org.projectfloodlight.openflow.types.TransportPort;
45 import org.projectfloodlight.openflow.types.U64; 47 import org.projectfloodlight.openflow.types.U64;
46 import org.projectfloodlight.openflow.types.VlanPcp; 48 import org.projectfloodlight.openflow.types.VlanPcp;
47 import org.projectfloodlight.openflow.types.VlanVid; 49 import org.projectfloodlight.openflow.types.VlanVid;
...@@ -161,10 +163,10 @@ public class FlowModBuilder { ...@@ -161,10 +163,10 @@ public class FlowModBuilder {
161 switch (l3m.subtype()) { 163 switch (l3m.subtype()) {
162 case IP_DST: 164 case IP_DST:
163 ip = (ModIPInstruction) i; 165 ip = (ModIPInstruction) i;
164 - return factory.actions().setNwDst(IPv4Address.of(ip.ip().toRealInt())); 166 + return factory.actions().setNwDst(IPv4Address.of(ip.ip().toInt()));
165 case IP_SRC: 167 case IP_SRC:
166 ip = (ModIPInstruction) i; 168 ip = (ModIPInstruction) i;
167 - return factory.actions().setNwSrc(IPv4Address.of(ip.ip().toRealInt())); 169 + return factory.actions().setNwSrc(IPv4Address.of(ip.ip().toInt()));
168 default: 170 default:
169 log.warn("Unimplemented action type {}.", l3m.subtype()); 171 log.warn("Unimplemented action type {}.", l3m.subtype());
170 break; 172 break;
...@@ -199,6 +201,7 @@ public class FlowModBuilder { ...@@ -199,6 +201,7 @@ public class FlowModBuilder {
199 Match.Builder mBuilder = factory.buildMatch(); 201 Match.Builder mBuilder = factory.buildMatch();
200 EthCriterion eth; 202 EthCriterion eth;
201 IPCriterion ip; 203 IPCriterion ip;
204 + TcpPortCriterion tp;
202 for (Criterion c : selector.criteria()) { 205 for (Criterion c : selector.criteria()) {
203 switch (c.type()) { 206 switch (c.type()) {
204 case IN_PORT: 207 case IN_PORT:
...@@ -220,21 +223,21 @@ public class FlowModBuilder { ...@@ -220,21 +223,21 @@ public class FlowModBuilder {
220 case IPV4_DST: 223 case IPV4_DST:
221 ip = (IPCriterion) c; 224 ip = (IPCriterion) c;
222 if (ip.ip().isMasked()) { 225 if (ip.ip().isMasked()) {
223 - Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toRealInt()), 226 + Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toInt()),
224 - IPv4Address.of(ip.ip().netmask().toRealInt())); 227 + IPv4Address.of(ip.ip().netmask().toInt()));
225 mBuilder.setMasked(MatchField.IPV4_DST, maskedIp); 228 mBuilder.setMasked(MatchField.IPV4_DST, maskedIp);
226 } else { 229 } else {
227 - mBuilder.setExact(MatchField.IPV4_DST, IPv4Address.of(ip.ip().toRealInt())); 230 + mBuilder.setExact(MatchField.IPV4_DST, IPv4Address.of(ip.ip().toInt()));
228 } 231 }
229 break; 232 break;
230 case IPV4_SRC: 233 case IPV4_SRC:
231 ip = (IPCriterion) c; 234 ip = (IPCriterion) c;
232 if (ip.ip().isMasked()) { 235 if (ip.ip().isMasked()) {
233 - Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toRealInt()), 236 + Masked<IPv4Address> maskedIp = Masked.of(IPv4Address.of(ip.ip().toInt()),
234 - IPv4Address.of(ip.ip().netmask().toRealInt())); 237 + IPv4Address.of(ip.ip().netmask().toInt()));
235 mBuilder.setMasked(MatchField.IPV4_SRC, maskedIp); 238 mBuilder.setMasked(MatchField.IPV4_SRC, maskedIp);
236 } else { 239 } else {
237 - mBuilder.setExact(MatchField.IPV4_SRC, IPv4Address.of(ip.ip().toRealInt())); 240 + mBuilder.setExact(MatchField.IPV4_SRC, IPv4Address.of(ip.ip().toInt()));
238 } 241 }
239 break; 242 break;
240 case IP_PROTO: 243 case IP_PROTO:
...@@ -250,6 +253,14 @@ public class FlowModBuilder { ...@@ -250,6 +253,14 @@ public class FlowModBuilder {
250 mBuilder.setExact(MatchField.VLAN_VID, 253 mBuilder.setExact(MatchField.VLAN_VID,
251 OFVlanVidMatch.ofVlanVid(VlanVid.ofVlan(vid.vlanId().toShort()))); 254 OFVlanVidMatch.ofVlanVid(VlanVid.ofVlan(vid.vlanId().toShort())));
252 break; 255 break;
256 + case TCP_DST:
257 + tp = (TcpPortCriterion) c;
258 + mBuilder.setExact(MatchField.TCP_DST, TransportPort.of(tp.tcpPort()));
259 + break;
260 + case TCP_SRC:
261 + tp = (TcpPortCriterion) c;
262 + mBuilder.setExact(MatchField.TCP_SRC, TransportPort.of(tp.tcpPort()));
263 + break;
253 case ARP_OP: 264 case ARP_OP:
254 case ARP_SHA: 265 case ARP_SHA:
255 case ARP_SPA: 266 case ARP_SPA:
...@@ -276,8 +287,6 @@ public class FlowModBuilder { ...@@ -276,8 +287,6 @@ public class FlowModBuilder {
276 case PBB_ISID: 287 case PBB_ISID:
277 case SCTP_DST: 288 case SCTP_DST:
278 case SCTP_SRC: 289 case SCTP_SRC:
279 - case TCP_DST:
280 - case TCP_SRC:
281 case TUNNEL_ID: 290 case TUNNEL_ID:
282 case UDP_DST: 291 case UDP_DST:
283 case UDP_SRC: 292 case UDP_SRC:
......
1 package org.onlab.onos.provider.of.flow.impl; 1 package org.onlab.onos.provider.of.flow.impl;
2 2
3 -import static org.slf4j.LoggerFactory.getLogger; 3 +import com.google.common.collect.ArrayListMultimap;
4 - 4 +import com.google.common.collect.Lists;
5 -import java.util.HashMap; 5 +import com.google.common.collect.Maps;
6 -import java.util.HashSet; 6 +import com.google.common.collect.Multimap;
7 -import java.util.List;
8 -import java.util.Map;
9 -import java.util.Set;
10 -import java.util.concurrent.ConcurrentHashMap;
11 -import java.util.concurrent.CountDownLatch;
12 -import java.util.concurrent.ExecutionException;
13 -import java.util.concurrent.Future;
14 -import java.util.concurrent.TimeUnit;
15 -import java.util.concurrent.TimeoutException;
16 -import java.util.concurrent.atomic.AtomicBoolean;
17 -
18 import org.apache.felix.scr.annotations.Activate; 7 import org.apache.felix.scr.annotations.Activate;
19 import org.apache.felix.scr.annotations.Component; 8 import org.apache.felix.scr.annotations.Component;
20 import org.apache.felix.scr.annotations.Deactivate; 9 import org.apache.felix.scr.annotations.Deactivate;
...@@ -68,10 +57,20 @@ import org.projectfloodlight.openflow.types.OFPort; ...@@ -68,10 +57,20 @@ import org.projectfloodlight.openflow.types.OFPort;
68 import org.projectfloodlight.openflow.types.U32; 57 import org.projectfloodlight.openflow.types.U32;
69 import org.slf4j.Logger; 58 import org.slf4j.Logger;
70 59
71 -import com.google.common.collect.ArrayListMultimap; 60 +import java.util.HashMap;
72 -import com.google.common.collect.Lists; 61 +import java.util.HashSet;
73 -import com.google.common.collect.Maps; 62 +import java.util.List;
74 -import com.google.common.collect.Multimap; 63 +import java.util.Map;
64 +import java.util.Set;
65 +import java.util.concurrent.ConcurrentHashMap;
66 +import java.util.concurrent.CountDownLatch;
67 +import java.util.concurrent.ExecutionException;
68 +import java.util.concurrent.Future;
69 +import java.util.concurrent.TimeUnit;
70 +import java.util.concurrent.TimeoutException;
71 +import java.util.concurrent.atomic.AtomicBoolean;
72 +
73 +import static org.slf4j.LoggerFactory.getLogger;
75 74
76 /** 75 /**
77 * Provider which uses an OpenFlow controller to detect network 76 * Provider which uses an OpenFlow controller to detect network
...@@ -166,6 +165,16 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -166,6 +165,16 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
166 for (FlowRuleBatchEntry fbe : batch.getOperations()) { 165 for (FlowRuleBatchEntry fbe : batch.getOperations()) {
167 FlowRule flowRule = fbe.getTarget(); 166 FlowRule flowRule = fbe.getTarget();
168 OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri())); 167 OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri()));
168 + if (sw == null) {
169 + /*
170 + * if a switch we are supposed to install to is gone then
171 + * cancel (ie. rollback) the work that has been done so far
172 + * and return the associated future.
173 + */
174 + InstallationFuture failed = new InstallationFuture(sws, fmXids);
175 + failed.cancel(true);
176 + return failed;
177 + }
169 sws.add(new Dpid(sw.getId())); 178 sws.add(new Dpid(sw.getId()));
170 FlowModBuilder builder = new FlowModBuilder(flowRule, sw.factory()); 179 FlowModBuilder builder = new FlowModBuilder(flowRule, sw.factory());
171 switch (fbe.getOperator()) { 180 switch (fbe.getOperator()) {
...@@ -322,6 +331,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -322,6 +331,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
322 331
323 public void fail(OFErrorMsg msg, Dpid dpid) { 332 public void fail(OFErrorMsg msg, Dpid dpid) {
324 ok.set(false); 333 ok.set(false);
334 + removeRequirement(dpid);
325 FlowEntry fe = null; 335 FlowEntry fe = null;
326 FlowRuleBatchEntry fbe = fms.get(msg.getXid()); 336 FlowRuleBatchEntry fbe = fms.get(msg.getXid());
327 FlowRule offending = fbe.getTarget(); 337 FlowRule offending = fbe.getTarget();
...@@ -374,11 +384,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -374,11 +384,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
374 384
375 385
376 public void satisfyRequirement(Dpid dpid) { 386 public void satisfyRequirement(Dpid dpid) {
377 - log.warn("Satisfaction from switch {}", dpid); 387 + log.debug("Satisfaction from switch {}", dpid);
378 - sws.remove(dpid); 388 + removeRequirement(dpid);
379 - countDownLatch.countDown();
380 - cleanUp();
381 -
382 } 389 }
383 390
384 391
...@@ -395,6 +402,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -395,6 +402,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
395 402
396 @Override 403 @Override
397 public boolean cancel(boolean mayInterruptIfRunning) { 404 public boolean cancel(boolean mayInterruptIfRunning) {
405 + ok.set(false);
398 this.state = BatchState.CANCELLED; 406 this.state = BatchState.CANCELLED;
399 cleanUp(); 407 cleanUp();
400 for (FlowRuleBatchEntry fbe : fms.values()) { 408 for (FlowRuleBatchEntry fbe : fms.values()) {
...@@ -438,7 +446,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -438,7 +446,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
438 } 446 }
439 447
440 private void cleanUp() { 448 private void cleanUp() {
441 - if (sws.isEmpty()) { 449 + if (isDone() || isCancelled()) {
442 pendingFutures.remove(pendingXid); 450 pendingFutures.remove(pendingXid);
443 for (Long xid : fms.keySet()) { 451 for (Long xid : fms.keySet()) {
444 pendingFMs.remove(xid); 452 pendingFMs.remove(xid);
...@@ -446,6 +454,12 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -446,6 +454,12 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
446 } 454 }
447 } 455 }
448 456
457 + private void removeRequirement(Dpid dpid) {
458 + countDownLatch.countDown();
459 + sws.remove(dpid);
460 + cleanUp();
461 + }
462 +
449 } 463 }
450 464
451 } 465 }
......
1 package org.onlab.onos.provider.of.host.impl; 1 package org.onlab.onos.provider.of.host.impl;
2 2
3 +import static org.onlab.onos.net.DeviceId.deviceId;
4 +import static org.onlab.onos.net.PortNumber.portNumber;
5 +import static org.slf4j.LoggerFactory.getLogger;
6 +
3 import org.apache.felix.scr.annotations.Activate; 7 import org.apache.felix.scr.annotations.Activate;
4 import org.apache.felix.scr.annotations.Component; 8 import org.apache.felix.scr.annotations.Component;
5 import org.apache.felix.scr.annotations.Deactivate; 9 import org.apache.felix.scr.annotations.Deactivate;
...@@ -29,15 +33,12 @@ import org.onlab.packet.IpPrefix; ...@@ -29,15 +33,12 @@ import org.onlab.packet.IpPrefix;
29 import org.onlab.packet.VlanId; 33 import org.onlab.packet.VlanId;
30 import org.slf4j.Logger; 34 import org.slf4j.Logger;
31 35
32 -import static org.onlab.onos.net.DeviceId.deviceId;
33 -import static org.onlab.onos.net.PortNumber.portNumber;
34 -import static org.slf4j.LoggerFactory.getLogger;
35 -
36 /** 36 /**
37 * Provider which uses an OpenFlow controller to detect network 37 * Provider which uses an OpenFlow controller to detect network
38 * end-station hosts. 38 * end-station hosts.
39 */ 39 */
40 @Component(immediate = true) 40 @Component(immediate = true)
41 +@Deprecated
41 public class OpenFlowHostProvider extends AbstractProvider implements HostProvider { 42 public class OpenFlowHostProvider extends AbstractProvider implements HostProvider {
42 43
43 private final Logger log = getLogger(getClass()); 44 private final Logger log = getLogger(getClass());
...@@ -109,14 +110,16 @@ public class OpenFlowHostProvider extends AbstractProvider implements HostProvid ...@@ -109,14 +110,16 @@ public class OpenFlowHostProvider extends AbstractProvider implements HostProvid
109 // Potentially a new or moved host 110 // Potentially a new or moved host
110 if (eth.getEtherType() == Ethernet.TYPE_ARP) { 111 if (eth.getEtherType() == Ethernet.TYPE_ARP) {
111 ARP arp = (ARP) eth.getPayload(); 112 ARP arp = (ARP) eth.getPayload();
112 - IpPrefix ip = IpPrefix.valueOf(arp.getSenderProtocolAddress()); 113 + IpPrefix ip = IpPrefix.valueOf(arp.getSenderProtocolAddress(),
114 + IpPrefix.MAX_INET_MASK);
113 HostDescription hdescr = 115 HostDescription hdescr =
114 new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip); 116 new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip);
115 providerService.hostDetected(hid, hdescr); 117 providerService.hostDetected(hid, hdescr);
116 118
117 } else if (ipLearn && eth.getEtherType() == Ethernet.TYPE_IPV4) { 119 } else if (ipLearn && eth.getEtherType() == Ethernet.TYPE_IPV4) {
118 IPv4 pip = (IPv4) eth.getPayload(); 120 IPv4 pip = (IPv4) eth.getPayload();
119 - IpPrefix ip = IpPrefix.valueOf(pip.getSourceAddress()); 121 + IpPrefix ip = IpPrefix.valueOf(pip.getSourceAddress(),
122 + IpPrefix.MAX_INET_MASK);
120 HostDescription hdescr = 123 HostDescription hdescr =
121 new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip); 124 new DefaultHostDescription(eth.getSourceMAC(), vlan, hloc, ip);
122 providerService.hostDetected(hid, hdescr); 125 providerService.hostDetected(hid, hdescr);
......
...@@ -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());
......
...@@ -28,7 +28,6 @@ import org.onlab.onos.openflow.controller.OpenFlowController; ...@@ -28,7 +28,6 @@ import org.onlab.onos.openflow.controller.OpenFlowController;
28 import org.onlab.onos.openflow.controller.OpenFlowPacketContext; 28 import org.onlab.onos.openflow.controller.OpenFlowPacketContext;
29 import org.onlab.onos.openflow.controller.OpenFlowSwitch; 29 import org.onlab.onos.openflow.controller.OpenFlowSwitch;
30 import org.onlab.onos.openflow.controller.PacketListener; 30 import org.onlab.onos.openflow.controller.PacketListener;
31 -import org.onlab.packet.Ethernet;
32 import org.projectfloodlight.openflow.protocol.OFPacketOut; 31 import org.projectfloodlight.openflow.protocol.OFPacketOut;
33 import org.projectfloodlight.openflow.protocol.OFPortDesc; 32 import org.projectfloodlight.openflow.protocol.OFPortDesc;
34 import org.projectfloodlight.openflow.protocol.action.OFAction; 33 import org.projectfloodlight.openflow.protocol.action.OFAction;
...@@ -96,13 +95,13 @@ public class OpenFlowPacketProvider extends AbstractProvider implements PacketPr ...@@ -96,13 +95,13 @@ public class OpenFlowPacketProvider extends AbstractProvider implements PacketPr
96 return; 95 return;
97 } 96 }
98 97
99 - Ethernet eth = new Ethernet(); 98 + //Ethernet eth = new Ethernet();
100 - eth.deserialize(packet.data().array(), 0, packet.data().array().length); 99 + //eth.deserialize(packet.data().array(), 0, packet.data().array().length);
101 OFPortDesc p = null; 100 OFPortDesc p = null;
102 for (Instruction inst : packet.treatment().instructions()) { 101 for (Instruction inst : packet.treatment().instructions()) {
103 if (inst.type().equals(Instruction.Type.OUTPUT)) { 102 if (inst.type().equals(Instruction.Type.OUTPUT)) {
104 p = portDesc(((OutputInstruction) inst).port()); 103 p = portDesc(((OutputInstruction) inst).port());
105 - OFPacketOut po = packetOut(sw, eth, p.getPortNo()); 104 + OFPacketOut po = packetOut(sw, packet.data().array(), p.getPortNo());
106 sw.sendMsg(po); 105 sw.sendMsg(po);
107 } 106 }
108 } 107 }
...@@ -116,7 +115,7 @@ public class OpenFlowPacketProvider extends AbstractProvider implements PacketPr ...@@ -116,7 +115,7 @@ public class OpenFlowPacketProvider extends AbstractProvider implements PacketPr
116 return builder.build(); 115 return builder.build();
117 } 116 }
118 117
119 - private OFPacketOut packetOut(OpenFlowSwitch sw, Ethernet eth, OFPort out) { 118 + private OFPacketOut packetOut(OpenFlowSwitch sw, byte[] eth, OFPort out) {
120 OFPacketOut.Builder builder = sw.factory().buildPacketOut(); 119 OFPacketOut.Builder builder = sw.factory().buildPacketOut();
121 OFAction act = sw.factory().actions() 120 OFAction act = sw.factory().actions()
122 .buildOutput() 121 .buildOutput()
...@@ -126,7 +125,7 @@ public class OpenFlowPacketProvider extends AbstractProvider implements PacketPr ...@@ -126,7 +125,7 @@ public class OpenFlowPacketProvider extends AbstractProvider implements PacketPr
126 .setBufferId(OFBufferId.NO_BUFFER) 125 .setBufferId(OFBufferId.NO_BUFFER)
127 .setInPort(OFPort.NO_MASK) 126 .setInPort(OFPort.NO_MASK)
128 .setActions(Collections.singletonList(act)) 127 .setActions(Collections.singletonList(act))
129 - .setData(eth.serialize()) 128 + .setData(eth)
130 .build(); 129 .build();
131 } 130 }
132 131
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
18 18
19 <modules> 19 <modules>
20 <module>openflow</module> 20 <module>openflow</module>
21 + <module>lldp</module>
22 + <module>host</module>
21 </modules> 23 </modules>
22 24
23 <dependencies> 25 <dependencies>
......
...@@ -5,8 +5,9 @@ export ONOS_ROOT=${ONOS_ROOT:-~/onos-next} ...@@ -5,8 +5,9 @@ export ONOS_ROOT=${ONOS_ROOT:-~/onos-next}
5 5
6 # M2 repository and Karaf gold bits 6 # M2 repository and Karaf gold bits
7 export M2_REPO=${M2_REPO:-~/.m2/repository} 7 export M2_REPO=${M2_REPO:-~/.m2/repository}
8 -export KARAF_ZIP=${KARAF_ZIP:-~/Downloads/apache-karaf-3.0.1.zip} 8 +export KARAF_VERSION=${KARAF_VERSION:-3.0.1}
9 -export KARAF_TAR=${KARAF_TAR:-~/Downloads/apache-karaf-3.0.1.tar.gz} 9 +export KARAF_ZIP=${KARAF_ZIP:-~/Downloads/apache-karaf-$KARAF_VERSION.zip}
10 +export KARAF_TAR=${KARAF_TAR:-~/Downloads/apache-karaf-$KARAF_VERSION.tar.gz}
10 export KARAF_DIST=$(basename $KARAF_ZIP .zip) 11 export KARAF_DIST=$(basename $KARAF_ZIP .zip)
11 12
12 # Add ONOS-specific directories to the exectable PATH 13 # Add ONOS-specific directories to the exectable PATH
...@@ -25,6 +26,9 @@ export ONOS_STAGE=$ONOS_STAGE_ROOT/$ONOS_BITS ...@@ -25,6 +26,9 @@ export ONOS_STAGE=$ONOS_STAGE_ROOT/$ONOS_BITS
25 export ONOS_TAR=$ONOS_STAGE.tar.gz 26 export ONOS_TAR=$ONOS_STAGE.tar.gz
26 27
27 # Defaults for ONOS testing using remote machines. 28 # Defaults for ONOS testing using remote machines.
29 +if [ -n "${ONOS_CELL}" -a -f $ONOS_ROOT/tools/test/cells/${ONOS_CELL} ]; then
30 + . $ONOS_ROOT/tools/test/cells/${ONOS_CELL}
31 +fi
28 export ONOS_INSTALL_DIR="/opt/onos" # Installation directory on remote 32 export ONOS_INSTALL_DIR="/opt/onos" # Installation directory on remote
29 export OCI="${OCI:-192.168.56.101}" # ONOS Controller Instance 33 export OCI="${OCI:-192.168.56.101}" # ONOS Controller Instance
30 export ONOS_USER="sdn" # ONOS user on remote system 34 export ONOS_USER="sdn" # ONOS user on remote system
......
...@@ -13,13 +13,23 @@ rm -fr $ONOS_STAGE # Remove this when package script is completed ...@@ -13,13 +13,23 @@ rm -fr $ONOS_STAGE # Remove this when package script is completed
13 13
14 # Make sure we have the original apache karaf bits first 14 # Make sure we have the original apache karaf bits first
15 [ ! -d $M2_REPO ] && echo "M2 repository $M2_REPO not found" && exit 1 15 [ ! -d $M2_REPO ] && echo "M2 repository $M2_REPO not found" && exit 1
16 -[ ! -f $KARAF_ZIP -a ! -f $KARAF_TAR ] && echo "Apache Karaf bits $KARAF_ZIP or $KARAF_TAR not found" && exit 1
17 [ -d $ONOS_STAGE ] && echo "ONOS stage $ONOS_STAGE already exists" && exit 1 16 [ -d $ONOS_STAGE ] && echo "ONOS stage $ONOS_STAGE already exists" && exit 1
18 17
19 # Create the stage directory and warp into it 18 # Create the stage directory and warp into it
20 mkdir -p $ONOS_STAGE 19 mkdir -p $ONOS_STAGE
21 cd $ONOS_STAGE 20 cd $ONOS_STAGE
22 21
22 +# Check if Apache Karaf bits are available and if not, fetch them.
23 +if [ ! -f $KARAF_ZIP -a ! -f $KARAF_TAR ]; then
24 + echo "Downloading $KARAF_TAR..."
25 + karafURL=$(curl -s http://www.apache.org/dyn/closer.cgi/karaf/$KARAF_VERSION/apache-karaf-$KARAF_VERSION.tar.gz |
26 + grep "<a href=\".*apache-karaf-$KARAF_VERSION.tar.gz\"" |
27 + head -n 1 | sed 's/.*<a href="//g;s/".*//g')
28 + curl -s $karafURL > $KARAF_TAR
29 +fi
30 +[ ! -f $KARAF_ZIP -a ! -f $KARAF_TAR ] && \
31 + echo "Apache Karaf bits $KARAF_ZIP or $KARAF_TAR not found" && exit 1
32 +
23 # Unroll the Apache Karaf bits, prune them and make ONOS top-level directories. 33 # Unroll the Apache Karaf bits, prune them and make ONOS top-level directories.
24 [ -f $KARAF_ZIP ] && unzip -q $KARAF_ZIP && rm -rf $KARAF_DIST/demos 34 [ -f $KARAF_ZIP ] && unzip -q $KARAF_ZIP && rm -rf $KARAF_DIST/demos
25 [ -f $KARAF_TAR ] && tar zxf $KARAF_TAR && rm -rf $KARAF_DIST/demos 35 [ -f $KARAF_TAR ] && tar zxf $KARAF_TAR && rm -rf $KARAF_DIST/demos
...@@ -28,7 +38,13 @@ mkdir bin ...@@ -28,7 +38,13 @@ mkdir bin
28 # Stage the ONOS admin scripts and patch in Karaf service wrapper extras 38 # Stage the ONOS admin scripts and patch in Karaf service wrapper extras
29 cp -r $ONOS_ROOT/tools/package/bin . 39 cp -r $ONOS_ROOT/tools/package/bin .
30 cp -r $ONOS_ROOT/tools/package/debian $ONOS_STAGE/debian 40 cp -r $ONOS_ROOT/tools/package/debian $ONOS_STAGE/debian
31 -cp -r $ONOS_ROOT/tools/package/etc/* $KARAF_DIST/etc 41 +cp -r $ONOS_ROOT/tools/package/etc/* $ONOS_STAGE/$KARAF_DIST/etc
42 +
43 +# Patch-in proper Karaf version into the startup script.
44 +sed "s/\$KARAF_VERSION/$KARAF_VERSION/g" \
45 + $ONOS_ROOT/tools/package/bin/onos-service > bin/onos-service
46 +sed "s/\$KARAF_VERSION/$KARAF_VERSION/g" \
47 + $ONOS_ROOT/tools/package/bin/onos > bin/onos
32 48
33 # Stage the ONOS bundles 49 # Stage the ONOS bundles
34 mkdir -p $KARAF_DIST/system/org/onlab 50 mkdir -p $KARAF_DIST/system/org/onlab
...@@ -36,16 +52,6 @@ cp -r $M2_REPO/org/onlab $KARAF_DIST/system/org/ ...@@ -36,16 +52,6 @@ cp -r $M2_REPO/org/onlab $KARAF_DIST/system/org/
36 52
37 export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-openflow,onos-app-fwd,onos-app-foo}" 53 export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-openflow,onos-app-fwd,onos-app-foo}"
38 54
39 -# Cellar Patching --------------------------------------------------------------
40 -
41 -# Patch the Apache Karaf distribution file to add Cellar features repository
42 -#perl -pi.old -e "s|^(featuresRepositories=.*)|\1,mvn:org.apache.karaf.cellar/apache-karaf-cellar/3.0.0/xml/features|" \
43 -# $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg
44 -
45 -# Patch the Apache Karaf distribution file to load ONOS features
46 -#perl -pi.old -e 's|^(featuresBoot=.*)|\1,cellar|' \
47 -# $ONOS_STAGE/$KARAF_DIST/etc/org.apache.karaf.features.cfg
48 -
49 # ONOS Patching ---------------------------------------------------------------- 55 # ONOS Patching ----------------------------------------------------------------
50 56
51 # Patch the Apache Karaf distribution file to add ONOS features repository 57 # Patch the Apache Karaf distribution file to add ONOS features repository
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
9 nodes=$(env | sort | egrep "OC[0-9]+" | cut -d= -f2) 9 nodes=$(env | sort | egrep "OC[0-9]+" | cut -d= -f2)
10 10
11 onos-package 11 onos-package
12 +onos-verify-cell || exit 1
13 +
12 for node in $nodes; do onos-install -f $node 1>/dev/null & done 14 for node in $nodes; do onos-install -f $node 1>/dev/null & done
13 15
14 # Wait for shutdown before waiting for restart 16 # Wait for shutdown before waiting for restart
......
...@@ -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'
...@@ -40,6 +45,7 @@ alias pub='onos-push-update-bundle' ...@@ -40,6 +45,7 @@ alias pub='onos-push-update-bundle'
40 # Short-hand for tailing the ONOS (karaf) log 45 # Short-hand for tailing the ONOS (karaf) log
41 alias tl='$ONOS_ROOT/tools/dev/bin/onos-local-log' 46 alias tl='$ONOS_ROOT/tools/dev/bin/onos-local-log'
42 alias tlo='tl | grep --colour=always org.onlab' 47 alias tlo='tl | grep --colour=always org.onlab'
48 +alias ll='less $KARAF_LOG'
43 49
44 # Pretty-print JSON output 50 # Pretty-print JSON output
45 alias pp='python -m json.tool' 51 alias pp='python -m json.tool'
...@@ -62,13 +68,14 @@ function cell { ...@@ -62,13 +68,14 @@ function cell {
62 [ ! -f $ONOS_ROOT/tools/test/cells/$1 ] && \ 68 [ ! -f $ONOS_ROOT/tools/test/cells/$1 ] && \
63 echo "No such cell: $1" >&2 && return 1 69 echo "No such cell: $1" >&2 && return 1
64 unset ONOS_CELL ONOS_NIC ONOS_FEATURES 70 unset ONOS_CELL ONOS_NIC ONOS_FEATURES
65 - unset OC1 OC2 OC3 OC4 OC5 OC6 OC7 OC8 OC9 OCN OCI 71 + unset OC0 OC1 OC2 OC3 OC4 OC5 OC6 OC7 OC8 OC9 OCN OCI
72 + export ONOS_CELL=$1
66 . $ONOS_ROOT/tools/test/cells/$1 73 . $ONOS_ROOT/tools/test/cells/$1
67 cell 74 cell
68 else 75 else
69 env | egrep "ONOS_CELL" 76 env | egrep "ONOS_CELL"
70 env | egrep "OCI" 77 env | egrep "OCI"
71 - env | egrep "OC[1-9]+" | sort 78 + env | egrep "OC[0-9]+" | sort
72 env | egrep "OCN" 79 env | egrep "OCN"
73 env | egrep "ONOS_" | egrep -v 'ONOS_ROOT|ONOS_CELL' 80 env | egrep "ONOS_" | egrep -v 'ONOS_ROOT|ONOS_CELL'
74 fi 81 fi
......
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 )
...@@ -5,5 +5,5 @@ ...@@ -5,5 +5,5 @@
5 5
6 export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/} 6 export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/}
7 7
8 -cd $(dirname $0)/../apache-karaf-*/bin 8 +cd $(dirname $0)/../apache-karaf-$KARAF_VERSION/bin
9 ./client -h localhost "$@" 9 ./client -h localhost "$@"
......
...@@ -7,4 +7,4 @@ export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/} ...@@ -7,4 +7,4 @@ export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-7-openjdk-amd64/}
7 export JAVA_OPTS="-Xms256M -Xmx2048M" 7 export JAVA_OPTS="-Xms256M -Xmx2048M"
8 8
9 cd /opt/onos 9 cd /opt/onos
10 -/opt/onos/apache-karaf-3.0.1/bin/karaf "$@" 10 +/opt/onos/apache-karaf-$KARAF_VERSION/bin/karaf "$@"
......
...@@ -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
......
...@@ -42,7 +42,7 @@ fi ...@@ -42,7 +42,7 @@ fi
42 42
43 echo "ONOS_CELL=${ONOS_CELL}" 43 echo "ONOS_CELL=${ONOS_CELL}"
44 echo "ONOS_NIC=${ONOS_NIC}" 44 echo "ONOS_NIC=${ONOS_NIC}"
45 -for n in {1..9}; do 45 +for n in {0..9}; do
46 ocn="OC${n}" 46 ocn="OC${n}"
47 if [ -n "${!ocn}" ]; then 47 if [ -n "${!ocn}" ]; then
48 echo "$ocn=${!ocn}" 48 echo "$ocn=${!ocn}"
......
...@@ -8,4 +8,4 @@ export OC2="192.168.56.102" ...@@ -8,4 +8,4 @@ export OC2="192.168.56.102"
8 export OCN="192.168.56.103" 8 export OCN="192.168.56.103"
9 export OCI="${OC1}" 9 export OCI="${OC1}"
10 10
11 -export ONOS_FEATURES="" 11 +export ONOS_FEATURES="${ONOS_FEATURES:-webconsole,onos-api,onos-core,onos-cli,onos-openflow,onos-app-fwd,onos-app-proxyarp,onos-app-tvue}"
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
2 2
3 export ONOS_CELL="office" 3 export ONOS_CELL="office"
4 4
5 -export ONOS_NIC="10.128.4.*" 5 +export ONOS_NIC="10.1.10.*"
6 -export OC1="10.128.4.60" 6 +export OC1="10.1.10.223"
7 export OCI="${OC1}" 7 export OCI="${OC1}"
8 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" 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"
......
...@@ -7,4 +7,4 @@ export OC1="192.168.56.101" ...@@ -7,4 +7,4 @@ export OC1="192.168.56.101"
7 export OCN="192.168.56.103" 7 export OCN="192.168.56.103"
8 export OCI="${OC1}" 8 export OCI="${OC1}"
9 9
10 -export ONOS_FEATURES="" 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 single ONOS instance & ONOS mininet box
2 +
3 +export ONOS_NIC=192.168.56.*
4 +export OC1="192.168.56.101"
5 +export OCN="192.168.56.103"
6 +
7 +export ONOS_FEATURES=webconsole,onos-api,onos-core-trivial,onos-cli,onos-openflow,onos-app-fwd,onos-app-mobility,onos-app-tvue,onos-app-optical
1 +package org.onlab.packet;
2 +
3 +/**
4 + * The class representing a network device chassisId.
5 + * This class is immutable.
6 + */
7 +// TODO: Move this to a reasonable place.
8 +public final class ChassisId {
9 +
10 + private static final long UNKNOWN = 0;
11 + private final long value;
12 +
13 + /**
14 + * Default constructor.
15 + */
16 + public ChassisId() {
17 + this.value = ChassisId.UNKNOWN;
18 + }
19 +
20 + /**
21 + * Constructor from a long value.
22 + *
23 + * @param value the value to use.
24 + */
25 + public ChassisId(long value) {
26 + this.value = value;
27 + }
28 +
29 + /**
30 + * Constructor from a string.
31 + *
32 + * @param value the value to use.
33 + */
34 + public ChassisId(String value) {
35 + this.value = Long.valueOf(value);
36 + }
37 +
38 + /**
39 + * Get the value of the chassis id.
40 + *
41 + * @return the value of the chassis id.
42 + */
43 + public long value() {
44 + return value;
45 + }
46 +
47 + /**
48 + * Convert the Chassis Id value to a ':' separated hexadecimal string.
49 + *
50 + * @return the Chassis Id value as a ':' separated hexadecimal string.
51 + */
52 + @Override
53 + public String toString() {
54 + return Long.toHexString(this.value);
55 + }
56 +
57 + @Override
58 + public boolean equals(Object other) {
59 + if (!(other instanceof ChassisId)) {
60 + return false;
61 + }
62 +
63 + ChassisId otherChassisId = (ChassisId) other;
64 +
65 + return value == otherChassisId.value;
66 + }
67 +
68 + @Override
69 + public int hashCode() {
70 + int hash = 17;
71 + hash += 31 * hash + (int) (value ^ value >>> 32);
72 + return hash;
73 + }
74 +}
...@@ -58,6 +58,7 @@ public class Ethernet extends BasePacket { ...@@ -58,6 +58,7 @@ public class Ethernet extends BasePacket {
58 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_RARP, ARP.class); 58 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_RARP, ARP.class);
59 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_IPV4, IPv4.class); 59 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_IPV4, IPv4.class);
60 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_LLDP, LLDP.class); 60 Ethernet.etherTypeClassMap.put(Ethernet.TYPE_LLDP, LLDP.class);
61 + Ethernet.etherTypeClassMap.put(Ethernet.TYPE_BSN, LLDP.class);
61 } 62 }
62 63
63 protected MacAddress destinationMACAddress; 64 protected MacAddress destinationMACAddress;
......
...@@ -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;
......
...@@ -150,7 +150,7 @@ public class LLDP extends BasePacket { ...@@ -150,7 +150,7 @@ public class LLDP extends BasePacket {
150 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length); 150 final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
151 LLDPTLV tlv; 151 LLDPTLV tlv;
152 do { 152 do {
153 - tlv = new LLDPTLV().deserialize(bb); 153 + tlv = new LLDPOrganizationalTLV().deserialize(bb);
154 154
155 // if there was a failure to deserialize stop processing TLVs 155 // if there was a failure to deserialize stop processing TLVs
156 if (tlv == null) { 156 if (tlv == null) {
...@@ -169,6 +169,7 @@ public class LLDP extends BasePacket { ...@@ -169,6 +169,7 @@ public class LLDP extends BasePacket {
169 case 0x3: 169 case 0x3:
170 this.ttl = tlv; 170 this.ttl = tlv;
171 break; 171 break;
172 +
172 default: 173 default:
173 this.optionalTLVList.add(tlv); 174 this.optionalTLVList.add(tlv);
174 break; 175 break;
......
...@@ -140,6 +140,9 @@ public class LLDPOrganizationalTLV extends LLDPTLV { ...@@ -140,6 +140,9 @@ public class LLDPOrganizationalTLV extends LLDPTLV {
140 140
141 @Override 141 @Override
142 public byte[] serialize() { 142 public byte[] serialize() {
143 + if (this.type != LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
144 + return super.serialize();
145 + }
143 final int valueLength = LLDPOrganizationalTLV.OUI_LENGTH 146 final int valueLength = LLDPOrganizationalTLV.OUI_LENGTH
144 + LLDPOrganizationalTLV.SUBTYPE_LENGTH + this.infoString.length; 147 + LLDPOrganizationalTLV.SUBTYPE_LENGTH + this.infoString.length;
145 this.value = new byte[valueLength]; 148 this.value = new byte[valueLength];
...@@ -152,7 +155,11 @@ public class LLDPOrganizationalTLV extends LLDPTLV { ...@@ -152,7 +155,11 @@ public class LLDPOrganizationalTLV extends LLDPTLV {
152 155
153 @Override 156 @Override
154 public LLDPTLV deserialize(final ByteBuffer bb) { 157 public LLDPTLV deserialize(final ByteBuffer bb) {
155 - super.deserialize(bb); 158 + LLDPTLV tlv = super.deserialize(bb);
159 + if (tlv.getType() != LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
160 + return tlv;
161 + }
162 +
156 final ByteBuffer optionalField = ByteBuffer.wrap(this.value); 163 final ByteBuffer optionalField = ByteBuffer.wrap(this.value);
157 164
158 final byte[] oui = new byte[LLDPOrganizationalTLV.OUI_LENGTH]; 165 final byte[] oui = new byte[LLDPOrganizationalTLV.OUI_LENGTH];
......
...@@ -111,6 +111,7 @@ public class LLDPTLV { ...@@ -111,6 +111,7 @@ public class LLDPTLV {
111 sscratch = bb.getShort(); 111 sscratch = bb.getShort();
112 this.type = (byte) (sscratch >> 9 & 0x7f); 112 this.type = (byte) (sscratch >> 9 & 0x7f);
113 this.length = (short) (sscratch & 0x1ff); 113 this.length = (short) (sscratch & 0x1ff);
114 +
114 if (this.length > 0) { 115 if (this.length > 0) {
115 this.value = new byte[this.length]; 116 this.value = new byte[this.length];
116 117
...@@ -120,6 +121,7 @@ public class LLDPTLV { ...@@ -120,6 +121,7 @@ public class LLDPTLV {
120 } 121 }
121 bb.get(this.value); 122 bb.get(this.value);
122 } 123 }
124 +
123 return this; 125 return this;
124 } 126 }
125 127
......
...@@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory; ...@@ -30,6 +30,7 @@ import org.slf4j.LoggerFactory;
30 * Refer to IEEE Std 802.1ABTM-2009 for more information. 30 * Refer to IEEE Std 802.1ABTM-2009 for more information.
31 * 31 *
32 */ 32 */
33 +@Deprecated
33 public class ONLabLddp extends LLDP { 34 public class ONLabLddp extends LLDP {
34 35
35 private static final Logger LOG = LoggerFactory.getLogger(ONLabLddp.class); 36 private static final Logger LOG = LoggerFactory.getLogger(ONLabLddp.class);
......
1 +package org.onlab.packet;
2 +
3 +import com.google.common.collect.Lists;
4 +import org.apache.commons.lang.ArrayUtils;
5 +
6 +import java.nio.ByteBuffer;
7 +
8 +/**
9 + * ONOS LLDP containing organizational TLV for ONOS device dicovery.
10 + */
11 +public class ONOSLLDP extends LLDP {
12 +
13 + public static final byte[] ONLAB_OUI = {(byte) 0xa4, 0x23, 0x05};
14 + public static final String DEFAULT_DEVICE = "INVALID";
15 + public static final String DEFAULT_NAME = "ONOS Discovery";
16 +
17 + public static final byte[] LLDP_NICIRA = {0x01, 0x23, 0x20, 0x00, 0x00,
18 + 0x01};
19 + public static final byte[] LLDP_MULTICAST = {0x01, (byte) 0x80,
20 + (byte) 0xc2, 0x00, 0x00, 0x0e};
21 + public static final byte[] BDDP_MULTICAST = {(byte) 0xff, (byte) 0xff,
22 + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
23 +
24 + private static final byte NAME_SUBTYPE = 1;
25 + private static final byte DEVICE_SUBTYPE = 2;
26 + private static final short NAME_LENGTH = 4; //1 for subtype + 3 for OUI
27 + private static final short DEVICE_LENGTH = 4; //1 for subtype + 3 for OUI
28 + private final LLDPOrganizationalTLV nameTLV = new LLDPOrganizationalTLV();
29 + private final LLDPOrganizationalTLV deviceTLV = new LLDPOrganizationalTLV();
30 +
31 + // TLV constants: type, size and subtype
32 + // Organizationally specific TLV also have packet offset and contents of TLV
33 + // header
34 + private static final byte CHASSIS_TLV_TYPE = 1;
35 + private static final byte CHASSIS_TLV_SIZE = 7;
36 + private static final byte CHASSIS_TLV_SUBTYPE = 4;
37 +
38 + private static final byte PORT_TLV_TYPE = 2;
39 + private static final byte PORT_TLV_SIZE = 5;
40 + private static final byte PORT_TLV_SUBTYPE = 2;
41 +
42 + private static final byte TTL_TLV_TYPE = 3;
43 +
44 +
45 + private final byte[] ttlValue = new byte[] {0, 0x78};
46 +
47 + public ONOSLLDP() {
48 + super();
49 + setName(DEFAULT_NAME);
50 + setDevice(DEFAULT_DEVICE);
51 + setOptionalTLVList(Lists.<LLDPTLV>newArrayList(nameTLV, deviceTLV));
52 + setTtl(new LLDPTLV().setType((byte) TTL_TLV_TYPE)
53 + .setLength((short) ttlValue.length)
54 + .setValue(ttlValue));
55 +
56 + }
57 +
58 + private ONOSLLDP(LLDP lldp) {
59 + this.portId = lldp.getPortId();
60 + this.chassisId = lldp.getChassisId();
61 + this.ttl = lldp.getTtl();
62 + this.optionalTLVList = lldp.getOptionalTLVList();
63 + }
64 +
65 + public void setName(String name) {
66 + nameTLV.setLength((short) (name.length() + NAME_LENGTH));
67 + nameTLV.setInfoString(name);
68 + nameTLV.setSubType(NAME_SUBTYPE);
69 + nameTLV.setOUI(ONLAB_OUI);
70 + }
71 +
72 + public void setDevice(String device) {
73 + deviceTLV.setInfoString(device);
74 + deviceTLV.setLength((short) (device.length() + DEVICE_LENGTH));
75 + deviceTLV.setSubType(DEVICE_SUBTYPE);
76 + deviceTLV.setOUI(ONLAB_OUI);
77 + }
78 +
79 + public void setChassisId(final ChassisId chassisId) {
80 + MacAddress chassisMac = MacAddress.valueOf(chassisId.value());
81 + byte[] chassis = ArrayUtils.addAll(new byte[] {CHASSIS_TLV_SUBTYPE},
82 + chassisMac.getAddress());
83 +
84 + LLDPTLV chassisTLV = new LLDPTLV();
85 + chassisTLV.setLength(CHASSIS_TLV_SIZE);
86 + chassisTLV.setType(CHASSIS_TLV_TYPE);
87 + chassisTLV.setValue(chassis);
88 + this.setChassisId(chassisTLV);
89 + }
90 +
91 + public void setPortId(final int portNumber) {
92 + byte[] port = ArrayUtils.addAll(new byte[] {PORT_TLV_SUBTYPE},
93 + ByteBuffer.allocate(4).putInt(portNumber).array());
94 +
95 + LLDPTLV portTLV = new LLDPTLV();
96 + portTLV.setLength(PORT_TLV_SIZE);
97 + portTLV.setType(PORT_TLV_TYPE);
98 + portTLV.setValue(port);
99 + this.setPortId(portTLV);
100 + }
101 +
102 + public LLDPOrganizationalTLV getNameTLV() {
103 + for (LLDPTLV tlv : this.getOptionalTLVList()) {
104 + if (tlv.getType() == LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
105 + LLDPOrganizationalTLV orgTLV = (LLDPOrganizationalTLV) tlv;
106 + if (orgTLV.getSubType() == NAME_SUBTYPE) {
107 + return orgTLV;
108 + }
109 + }
110 + }
111 + return null;
112 + }
113 +
114 + public LLDPOrganizationalTLV getDeviceTLV() {
115 + for (LLDPTLV tlv : this.getOptionalTLVList()) {
116 + if (tlv.getType() == LLDPOrganizationalTLV.ORGANIZATIONAL_TLV_TYPE) {
117 + LLDPOrganizationalTLV orgTLV = (LLDPOrganizationalTLV) tlv;
118 + if (orgTLV.getSubType() == DEVICE_SUBTYPE) {
119 + return orgTLV;
120 + }
121 + }
122 + }
123 + return null;
124 + }
125 +
126 + public String getNameString() {
127 + LLDPOrganizationalTLV tlv = getNameTLV();
128 + if (tlv != null) {
129 + return new String(tlv.getInfoString());
130 + }
131 + return null;
132 + }
133 +
134 + public String getDeviceString() {
135 + LLDPOrganizationalTLV tlv = getDeviceTLV();
136 + if (tlv != null) {
137 + return new String(tlv.getInfoString());
138 + }
139 + return null;
140 + }
141 +
142 + public Integer getPort() {
143 + ByteBuffer portBB = ByteBuffer.wrap(this.getPortId().getValue());
144 + portBB.position(1);
145 + return portBB.getInt();
146 + }
147 +
148 + /**
149 + * Given an ethernet packet, determines if this is an LLDP from
150 + * ONOS and returns the device the LLDP came from.
151 + * @param eth an ethernet packet
152 + * @return a the lldp packet or null
153 + */
154 + public static ONOSLLDP parseONOSLLDP(Ethernet eth) {
155 + if (eth.getEtherType() == Ethernet.TYPE_LLDP ||
156 + eth.getEtherType() == Ethernet.TYPE_BSN) {
157 + ONOSLLDP onosLldp = new ONOSLLDP((LLDP) eth.getPayload()); //(ONOSLLDP) eth.getPayload();
158 + if (ONOSLLDP.DEFAULT_NAME.equals(onosLldp.getNameString())) {
159 + return onosLldp;
160 + }
161 + }
162 + return null;
163 + }
164 +
165 +
166 +
167 +
168 +
169 +}
1 +package org.onlab.util;
2 +
3 +public final class HexString {
4 +
5 + private HexString() {
6 +
7 + }
8 +
9 + /**
10 + * Convert a string of bytes to a ':' separated hex string.
11 + *
12 + * @param bytes
13 + * @return "0f:ca:fe:de:ad:be:ef"
14 + */
15 + public static String toHexString(final byte[] bytes) {
16 + int i;
17 + StringBuilder ret = new StringBuilder();
18 + String tmp;
19 + for (i = 0; i < bytes.length; i++) {
20 + if (i > 0) {
21 + ret.append(':');
22 + }
23 + tmp = Integer.toHexString((bytes[i] & 0xff));
24 + if (tmp.length() == 1) {
25 + ret.append('0');
26 + }
27 + ret.append(tmp);
28 + }
29 + return ret.toString();
30 + }
31 +
32 + public static String toHexString(final long val, final int padTo) {
33 + char[] arr = Long.toHexString(val).toCharArray();
34 + String ret = "";
35 + // prepend the right number of leading zeros
36 + int i = 0;
37 + for (; i < (padTo * 2 - arr.length); i++) {
38 + ret += "0";
39 + if ((i % 2) != 0) {
40 + ret += ":";
41 + }
42 + }
43 + for (int j = 0; j < arr.length; j++) {
44 + ret += arr[j];
45 + if ((((i + j) % 2) != 0) && (j < (arr.length - 1))) {
46 + ret += ":";
47 + }
48 + }
49 + return ret;
50 + }
51 +
52 + public static String toHexString(final long val) {
53 + return toHexString(val, 8);
54 + }
55 +
56 + /**
57 + * Convert a string of hex values into a string of bytes.
58 + *
59 + * @param values
60 + * "0f:ca:fe:de:ad:be:ef"
61 + * @return [15, 5 ,2, 5, 17]
62 + * @throws NumberFormatException
63 + * If the string can not be parsed
64 + */
65 + public static byte[] fromHexString(final String values) {
66 + String[] octets = values.split(":");
67 + byte[] ret = new byte[octets.length];
68 +
69 + for (int i = 0; i < octets.length; i++) {
70 + if (octets[i].length() > 2) {
71 + throw new NumberFormatException("Invalid octet length");
72 + }
73 + ret[i] = Integer.valueOf(octets[i], 16).byteValue();
74 + }
75 + return ret;
76 + }
77 +
78 + public static long toLong(String value) {
79 + String[] octets = value.split(":");
80 + if (octets.length > 8) {
81 + throw new NumberFormatException("Input string is too big to fit in long: " + value);
82 + }
83 + long l = 0;
84 + for (String octet: octets) {
85 + if (octet.length() > 2) {
86 + throw new NumberFormatException(
87 + "Each colon-separated byte component must consist of 1 or 2 hex digits: " + value);
88 + }
89 + short s = Short.parseShort(octet, 16);
90 + l = (l << 8) + s;
91 + }
92 + return l;
93 + }
94 +}
...@@ -13,6 +13,7 @@ import com.esotericsoftware.kryo.io.ByteBufferInput; ...@@ -13,6 +13,7 @@ import com.esotericsoftware.kryo.io.ByteBufferInput;
13 import com.esotericsoftware.kryo.io.ByteBufferOutput; 13 import com.esotericsoftware.kryo.io.ByteBufferOutput;
14 import com.esotericsoftware.kryo.io.Input; 14 import com.esotericsoftware.kryo.io.Input;
15 import com.esotericsoftware.kryo.io.Output; 15 import com.esotericsoftware.kryo.io.Output;
16 +import com.esotericsoftware.kryo.pool.KryoFactory;
16 import com.google.common.collect.ImmutableList; 17 import com.google.common.collect.ImmutableList;
17 18
18 // TODO Add tests for this class. 19 // TODO Add tests for this class.
...@@ -20,7 +21,7 @@ import com.google.common.collect.ImmutableList; ...@@ -20,7 +21,7 @@ import com.google.common.collect.ImmutableList;
20 * Pool of Kryo instances, with classes pre-registered. 21 * Pool of Kryo instances, with classes pre-registered.
21 */ 22 */
22 //@ThreadSafe 23 //@ThreadSafe
23 -public final class KryoPool { 24 +public final class KryoNamespace implements KryoFactory {
24 25
25 /** 26 /**
26 * Default buffer size used for serialization. 27 * Default buffer size used for serialization.
...@@ -34,7 +35,7 @@ public final class KryoPool { ...@@ -34,7 +35,7 @@ public final class KryoPool {
34 private final boolean registrationRequired; 35 private final boolean registrationRequired;
35 36
36 /** 37 /**
37 - * KryoPool builder. 38 + * KryoNamespace builder.
38 */ 39 */
39 //@NotThreadSafe 40 //@NotThreadSafe
40 public static final class Builder { 41 public static final class Builder {
...@@ -42,12 +43,12 @@ public final class KryoPool { ...@@ -42,12 +43,12 @@ public final class KryoPool {
42 private final List<Pair<Class<?>, Serializer<?>>> types = new ArrayList<>(); 43 private final List<Pair<Class<?>, Serializer<?>>> types = new ArrayList<>();
43 44
44 /** 45 /**
45 - * Builds a {@link KryoPool} instance. 46 + * Builds a {@link KryoNamespace} instance.
46 * 47 *
47 - * @return KryoPool 48 + * @return KryoNamespace
48 */ 49 */
49 - public KryoPool build() { 50 + public KryoNamespace build() {
50 - return new KryoPool(types); 51 + return new KryoNamespace(types);
51 } 52 }
52 53
53 /** 54 /**
...@@ -76,19 +77,19 @@ public final class KryoPool { ...@@ -76,19 +77,19 @@ public final class KryoPool {
76 } 77 }
77 78
78 /** 79 /**
79 - * Registers all the class registered to given KryoPool. 80 + * Registers all the class registered to given KryoNamespace.
80 * 81 *
81 - * @param pool KryoPool 82 + * @param pool KryoNamespace
82 * @return this 83 * @return this
83 */ 84 */
84 - public Builder register(final KryoPool pool) { 85 + public Builder register(final KryoNamespace pool) {
85 types.addAll(pool.registeredTypes); 86 types.addAll(pool.registeredTypes);
86 return this; 87 return this;
87 } 88 }
88 } 89 }
89 90
90 /** 91 /**
91 - * Creates a new {@link KryoPool} builder. 92 + * Creates a new {@link KryoNamespace} builder.
92 * 93 *
93 * @return builder 94 * @return builder
94 */ 95 */
...@@ -101,7 +102,7 @@ public final class KryoPool { ...@@ -101,7 +102,7 @@ public final class KryoPool {
101 * 102 *
102 * @param registerdTypes types to register 103 * @param registerdTypes types to register
103 */ 104 */
104 - private KryoPool(final List<Pair<Class<?>, Serializer<?>>> registerdTypes) { 105 + private KryoNamespace(final List<Pair<Class<?>, Serializer<?>>> registerdTypes) {
105 this.registeredTypes = ImmutableList.copyOf(registerdTypes); 106 this.registeredTypes = ImmutableList.copyOf(registerdTypes);
106 // always true for now 107 // always true for now
107 this.registrationRequired = true; 108 this.registrationRequired = true;
...@@ -113,10 +114,10 @@ public final class KryoPool { ...@@ -113,10 +114,10 @@ public final class KryoPool {
113 * @param instances to add to the pool 114 * @param instances to add to the pool
114 * @return this 115 * @return this
115 */ 116 */
116 - public KryoPool populate(int instances) { 117 + public KryoNamespace populate(int instances) {
117 List<Kryo> kryos = new ArrayList<>(instances); 118 List<Kryo> kryos = new ArrayList<>(instances);
118 for (int i = 0; i < instances; ++i) { 119 for (int i = 0; i < instances; ++i) {
119 - kryos.add(newKryoInstance()); 120 + kryos.add(create());
120 } 121 }
121 pool.addAll(kryos); 122 pool.addAll(kryos);
122 return this; 123 return this;
...@@ -130,7 +131,7 @@ public final class KryoPool { ...@@ -130,7 +131,7 @@ public final class KryoPool {
130 public Kryo getKryo() { 131 public Kryo getKryo() {
131 Kryo kryo = pool.poll(); 132 Kryo kryo = pool.poll();
132 if (kryo == null) { 133 if (kryo == null) {
133 - return newKryoInstance(); 134 + return create();
134 } 135 }
135 return kryo; 136 return kryo;
136 } 137 }
...@@ -235,7 +236,8 @@ public final class KryoPool { ...@@ -235,7 +236,8 @@ public final class KryoPool {
235 * 236 *
236 * @return Kryo instance 237 * @return Kryo instance
237 */ 238 */
238 - private Kryo newKryoInstance() { 239 + @Override
240 + public Kryo create() {
239 Kryo kryo = new Kryo(); 241 Kryo kryo = new Kryo();
240 kryo.setRegistrationRequired(registrationRequired); 242 kryo.setRegistrationRequired(registrationRequired);
241 for (Pair<Class<?>, Serializer<?>> registry : registeredTypes) { 243 for (Pair<Class<?>, Serializer<?>> registry : registeredTypes) {
......
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 org.junit.Test;
4 +
5 +import com.esotericsoftware.minlog.Log;
6 +
7 +import junit.framework.TestCase;
8 +
9 +/**
10 + * Test of the Hexstring.
11 + *
12 + */
13 +
14 +public class HexStringTest extends TestCase {
15 +
16 + @Test
17 + public void testMarshalling() throws Exception {
18 + String dpidStr = "00:00:00:23:20:2d:16:71";
19 + long dpid = HexString.toLong(dpidStr);
20 + String testStr = HexString.toHexString(dpid);
21 + TestCase.assertEquals(dpidStr, testStr);
22 + }
23 +
24 + @Test
25 + public void testToLong() {
26 + String dpidStr = "3e:1f:01:fc:72:8c:63:31";
27 + long valid = 0x3e1f01fc728c6331L;
28 + long testLong = HexString.toLong(dpidStr);
29 + TestCase.assertEquals(valid, testLong);
30 + }
31 +
32 + @Test
33 + public void testToLongMSB() {
34 + String dpidStr = "ca:7c:5e:d1:64:7a:95:9b";
35 + long valid = -3856102927509056101L;
36 + long testLong = HexString.toLong(dpidStr);
37 + TestCase.assertEquals(valid, testLong);
38 + }
39 +
40 + @Test
41 + public void testToLongError() {
42 + String dpidStr = "09:08:07:06:05:04:03:02:01";
43 + try {
44 + HexString.toLong(dpidStr);
45 + fail("HexString.toLong() should have thrown a NumberFormatException");
46 + } catch (NumberFormatException expected) {
47 + Log.info("HexString.toLong() have thrown a NumberFormatException");
48 + }
49 + }
50 +
51 + @Test
52 + public void testToStringBytes() {
53 + byte[] dpid = {0, 0, 0, 0, 0, 0, 0, -1 };
54 + String valid = "00:00:00:00:00:00:00:ff";
55 + String testString = HexString.toHexString(dpid);
56 + TestCase.assertEquals(valid, testString);
57 + }
58 +
59 + @Test
60 + public void testFromHexStringError() {
61 + String invalidStr = "00:00:00:00:00:00:ffff";
62 + try {
63 + HexString.fromHexString(invalidStr);
64 + fail("HexString.fromHexString() should have thrown a NumberFormatException");
65 + } catch (NumberFormatException expected) {
66 + Log.info("HexString.toLong() have thrown a NumberFormatException");
67 + }
68 + }
69 +}
70 +
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 +}
1 package org.onlab.netty; 1 package org.onlab.netty;
2 2
3 -import org.onlab.util.KryoPool; 3 +import org.onlab.util.KryoNamespace;
4 4
5 import java.nio.ByteBuffer; 5 import java.nio.ByteBuffer;
6 import java.util.ArrayList; 6 import java.util.ArrayList;
...@@ -12,7 +12,7 @@ import java.util.HashMap; ...@@ -12,7 +12,7 @@ import java.util.HashMap;
12 */ 12 */
13 public class KryoSerializer { 13 public class KryoSerializer {
14 14
15 - private KryoPool serializerPool; 15 + private KryoNamespace serializerPool;
16 16
17 public KryoSerializer() { 17 public KryoSerializer() {
18 setupKryoPool(); 18 setupKryoPool();
...@@ -23,7 +23,7 @@ public class KryoSerializer { ...@@ -23,7 +23,7 @@ public class KryoSerializer {
23 */ 23 */
24 protected void setupKryoPool() { 24 protected void setupKryoPool() {
25 // FIXME Slice out types used in common to separate pool/namespace. 25 // FIXME Slice out types used in common to separate pool/namespace.
26 - serializerPool = KryoPool.newBuilder() 26 + serializerPool = KryoNamespace.newBuilder()
27 .register(ArrayList.class, 27 .register(ArrayList.class,
28 HashMap.class, 28 HashMap.class,
29 ArrayList.class, 29 ArrayList.class,
......
...@@ -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>
......