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 15265 additions and 3555 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
...@@ -185,7 +209,6 @@ public class ReactiveForwarding { ...@@ -185,7 +209,6 @@ public class ReactiveForwarding {
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,7 +28,10 @@ public class MastersListCommand extends AbstractShellCommand { ...@@ -26,7 +28,10 @@ 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);
29 - ControllerNode self = service.getLocalNode(); 31 +
32 + if (outputJson()) {
33 + print("%s", json(service, mastershipService, nodes));
34 + } else {
30 for (ControllerNode node : nodes) { 35 for (ControllerNode node : nodes) {
31 List<DeviceId> ids = Lists.newArrayList(mastershipService.getDevicesOf(node.id())); 36 List<DeviceId> ids = Lists.newArrayList(mastershipService.getDevicesOf(node.id()));
32 Collections.sort(ids, Comparators.ELEMENT_ID_COMPARATOR); 37 Collections.sort(ids, Comparators.ELEMENT_ID_COMPARATOR);
...@@ -36,5 +41,37 @@ public class MastersListCommand extends AbstractShellCommand { ...@@ -36,5 +41,37 @@ public class MastersListCommand extends AbstractShellCommand {
36 } 41 }
37 } 42 }
38 } 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();
51 + ControllerNode self = service.getLocalNode();
52 + for (ControllerNode node : nodes) {
53 + List<DeviceId> ids = Lists.newArrayList(mastershipService.getDevicesOf(node.id()));
54 + result.add(mapper.createObjectNode()
55 + .put("id", node.id().toString())
56 + .put("size", ids.size())
57 + .set("devices", json(mapper, ids)));
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());
73 + }
74 + return result;
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,6 +27,9 @@ public class NodesListCommand extends AbstractShellCommand { ...@@ -24,6 +27,9 @@ 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 {
27 ControllerNode self = service.getLocalNode(); 33 ControllerNode self = service.getLocalNode();
28 for (ControllerNode node : nodes) { 34 for (ControllerNode node : nodes) {
29 print(FMT, node.id(), node.ip(), node.tcpPort(), 35 print(FMT, node.id(), node.ip(), node.tcpPort(),
...@@ -31,5 +37,22 @@ public class NodesListCommand extends AbstractShellCommand { ...@@ -31,5 +37,22 @@ public class NodesListCommand extends AbstractShellCommand {
31 node.equals(self) ? "*" : ""); 37 node.equals(self) ? "*" : "");
32 } 38 }
33 } 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();
46 + ControllerNode self = service.getLocalNode();
47 + for (ControllerNode node : nodes) {
48 + result.add(mapper.createObjectNode()
49 + .put("id", node.id().toString())
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)));
54 + }
55 + return result;
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,6 +23,19 @@ public class SummaryCommand extends AbstractShellCommand { ...@@ -22,6 +23,19 @@ 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();
26 + if (outputJson()) {
27 + print("%s", new ObjectMapper().createObjectNode()
28 + .put("node", get(ClusterService.class).getLocalNode().ip().toString())
29 + .put("version", get(CoreService.class).version().toString())
30 + .put("nodes", get(ClusterService.class).getNodes().size())
31 + .put("devices", get(DeviceService.class).getDeviceCount())
32 + .put("links", get(LinkService.class).getLinkCount())
33 + .put("hosts", get(HostService.class).getHostCount())
34 + .put("clusters", topologyService.getClusters(topology).size())
35 + .put("paths", topology.pathCount())
36 + .put("flows", get(FlowRuleService.class).getFlowRuleCount())
37 + .put("intents", get(IntentService.class).getIntentCount()));
38 + } else {
25 print("node=%s, version=%s", 39 print("node=%s, version=%s",
26 get(ClusterService.class).getLocalNode().ip(), 40 get(ClusterService.class).getLocalNode().ip(),
27 get(CoreService.class).version().toString()); 41 get(CoreService.class).version().toString());
...@@ -35,5 +49,6 @@ public class SummaryCommand extends AbstractShellCommand { ...@@ -35,5 +49,6 @@ public class SummaryCommand extends AbstractShellCommand {
35 get(FlowRuleService.class).getFlowRuleCount(), 49 get(FlowRuleService.class).getFlowRuleCount(),
36 get(IntentService.class).getIntentCount()); 50 get(IntentService.class).getIntentCount());
37 } 51 }
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);
38 + if (outputJson()) {
39 + print("%s", json(new ObjectMapper(), ids));
40 + } else {
36 for (DeviceId deviceId : ids) { 41 for (DeviceId deviceId : ids) {
37 print("%s", deviceId); 42 print("%s", deviceId);
38 } 43 }
39 } 44 }
40 } 45 }
41 - 46 + }
42 47
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 {
27 for (TopologyCluster cluster : clusters) { 33 for (TopologyCluster cluster : clusters) {
28 print(FMT, cluster.id().index(), cluster.deviceCount(), cluster.linkCount()); 34 print(FMT, cluster.id().index(), cluster.deviceCount(), cluster.linkCount());
29 } 35 }
30 } 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();
43 + for (TopologyCluster cluster : clusters) {
44 + result.add(mapper.createObjectNode()
45 + .put("id", cluster.id().index())
46 + .put("deviceCount", cluster.deviceCount())
47 + .put("linkCount", cluster.linkCount()));
48 + }
49 + return result;
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,27 +43,79 @@ public class DevicePortsListCommand extends DevicesListCommand { ...@@ -30,27 +43,79 @@ 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) {
46 + if (outputJson()) {
47 + print("%s", jsonPorts(service, getSortedDevices(service)));
48 + } else {
33 for (Device device : getSortedDevices(service)) { 49 for (Device device : getSortedDevices(service)) {
34 printDevice(service, device); 50 printDevice(service, device);
35 } 51 }
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) {
115 + if (isIncluded(port)) {
52 print(FMT, port.number(), port.isEnabled() ? "enabled" : "disabled"); 116 print(FMT, port.number(), port.isEnabled() ? "enabled" : "disabled");
53 } 117 }
54 } 118 }
119 + }
55 120
56 } 121 }
......
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,10 +28,53 @@ public class DevicesListCommand extends AbstractShellCommand { ...@@ -24,10 +28,53 @@ 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);
31 + if (outputJson()) {
32 + print("%s", json(service, getSortedDevices(service)));
33 + } else {
27 for (Device device : getSortedDevices(service)) { 34 for (Device device : getSortedDevices(service)) {
28 printDevice(service, device); 35 printDevice(service, device);
29 } 36 }
30 } 37 }
38 + }
39 +
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 + }
31 78
32 /** 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.
......
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,10 +54,74 @@ public class FlowsListCommand extends AbstractShellCommand { ...@@ -48,10 +54,74 @@ 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);
57 +
58 + if (outputJson()) {
59 + print("%s", json(coreService, getSortedDevices(deviceService), flows));
60 + } else {
51 for (Device d : getSortedDevices(deviceService)) { 61 for (Device d : getSortedDevices(deviceService)) {
52 printFlows(d, flows.get(d), coreService); 62 printFlows(d, flows.get(d), coreService);
53 } 63 }
54 } 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)));
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;
124 + }
55 125
56 /** 126 /**
57 * Returns the list of devices sorted using the device ID URIs. 127 * Returns the list of devices sorted using the device ID URIs.
......
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;
...@@ -24,10 +29,41 @@ public class HostsListCommand extends AbstractShellCommand { ...@@ -24,10 +29,41 @@ 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);
32 + if (outputJson()) {
33 + print("%s", json(getSortedHosts(service)));
34 + } else {
27 for (Host host : getSortedHosts(service)) { 35 for (Host host : getSortedHosts(service)) {
28 printHost(host); 36 printHost(host);
29 } 37 }
30 } 38 }
39 + }
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 + }
31 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.
...@@ -44,7 +80,7 @@ public class HostsListCommand extends AbstractShellCommand { ...@@ -44,7 +80,7 @@ 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) {
...@@ -54,4 +90,4 @@ public class HostsListCommand extends AbstractShellCommand { ...@@ -54,4 +90,4 @@ public class HostsListCommand extends AbstractShellCommand {
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,10 +32,56 @@ public class LinksListCommand extends AbstractShellCommand { ...@@ -27,10 +32,56 @@ 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 {
30 for (Link link : links) { 38 for (Link link : links) {
31 print(linkString(link)); 39 print(linkString(link));
32 } 40 }
33 } 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();
53 + for (Link link : links) {
54 + result.add(json(mapper, link));
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());
84 + }
34 85
35 /** 86 /**
36 * Returns a formatted string representing the given link. 87 * Returns a formatted string representing the given 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 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,10 +35,31 @@ public class PathListCommand extends TopologyCommand { ...@@ -32,10 +35,31 @@ 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 {
35 for (Path path : paths) { 41 for (Path path : paths) {
36 print(pathString(path)); 42 print(pathString(path));
37 } 43 }
38 } 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();
56 + for (Path path : paths) {
57 + result.add(LinksListCommand.json(mapper, path)
58 + .put("cost", path.cost())
59 + .set("links", LinksListCommand.json(path.links())));
60 + }
61 + return result;
62 + }
39 63
40 /** 64 /**
41 * Produces a formatted string representing the specified path. 65 * Produces a formatted string representing the specified path.
......
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();
34 + if (outputJson()) {
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 {
33 print(FMT, topology.time(), topology.deviceCount(), topology.linkCount(), 42 print(FMT, topology.time(), topology.deviceCount(), topology.linkCount(),
34 topology.clusterCount(), topology.pathCount()); 43 topology.clusterCount(), topology.pathCount());
35 } 44 }
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,8 +12,12 @@ public final class ControllerNodeToNodeId ...@@ -12,8 +12,12 @@ public final class ControllerNodeToNodeId
12 12
13 @Override 13 @Override
14 public NodeId apply(ControllerNode input) { 14 public NodeId apply(ControllerNode input) {
15 + if (input == null) {
16 + return null;
17 + } else {
15 return input.id(); 18 return input.id();
16 } 19 }
20 + }
17 21
18 /** 22 /**
19 * Returns a Function to convert ControllerNode to NodeId. 23 * Returns a Function to convert ControllerNode to NodeId.
......
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;
......
...@@ -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
...@@ -42,23 +42,6 @@ ...@@ -42,23 +42,6 @@
42 <scope>test</scope> 42 <scope>test</scope>
43 </dependency> 43 </dependency>
44 44
45 - <!-- TODO Consider removing store dependency.
46 - Currently required for DistributedDeviceManagerTest. -->
47 - <dependency>
48 - <groupId>org.onlab.onos</groupId>
49 - <artifactId>onos-core-hz-net</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>
60 - </dependency>
61 -
62 <dependency> 45 <dependency>
63 <groupId>org.apache.felix</groupId> 46 <groupId>org.apache.felix</groupId>
64 <artifactId>org.apache.felix.scr.annotations</artifactId> 47 <artifactId>org.apache.felix.scr.annotations</artifactId>
......
...@@ -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,13 +261,31 @@ public class DeviceManager ...@@ -256,13 +261,31 @@ 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); 271 +
272 + DeviceEvent event = null;
273 + try {
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 {
266 //relinquish master role and ability to be backup. 289 //relinquish master role and ability to be backup.
267 mastershipService.relinquishMastership(deviceId); 290 mastershipService.relinquishMastership(deviceId);
268 291
...@@ -271,6 +294,7 @@ public class DeviceManager ...@@ -271,6 +294,7 @@ public class DeviceManager
271 post(event); 294 post(event);
272 } 295 }
273 } 296 }
297 + }
274 298
275 @Override 299 @Override
276 public void updatePorts(DeviceId deviceId, 300 public void updatePorts(DeviceId deviceId,
...@@ -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())) {
374 + // something went wrong in consistency, let go
375 + mastershipService.relinquishMastership(did);
376 + applyRole(did, MastershipRole.STANDBY);
377 + return;
378 + }
379 +
380 + log.info("## setting term for CPS as new master for {}", did);
335 // only set the new term if I am the master 381 // only set the new term if I am the master
336 deviceClockProviderService.setMastershipTerm(did, term); 382 deviceClockProviderService.setMastershipTerm(did, term);
337 - }
338 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.
343 - //flag the device as online. Is there a better way to do this? 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.
344 Device device = getDevice(did); 390 Device device = getDevice(did);
391 + if ((device != null) && !isAvailable(did)) {
392 + //flag the device as online. Is there a better way to do this?
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 +
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))));
24 } 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))));
25 } 109 }
26 110
27 - private static class MockTreatment implements TrafficTreatment { 111 + /**
28 - @Override 112 + * Tests the equals() method where two LinkCollectionIntents have different
29 - public List<Instruction> instructions() { 113 + * ids. These should compare not equal.
30 - return new ArrayList<>(); 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))));
31 } 127 }
128 +
129 + /**
130 + * Tests that the hashCode() values for two equivalent LinkCollectionIntent
131 + * objects are the same.
132 + */
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,21 +525,12 @@ public class GossipLinkStore ...@@ -476,21 +525,12 @@ 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) {
528 + if (input == null) {
529 + return null;
530 + } else {
479 return links.get(input); 531 return links.get(input);
480 } 532 }
481 } 533 }
482 -
483 - private static final class IsPrimary implements Predicate<Provided> {
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 - }
494 } 534 }
495 535
496 private void notifyDelegateIfNotNull(LinkEvent event) { 536 private void notifyDelegateIfNotNull(LinkEvent event) {
...@@ -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) {
513 - try {
514 ClusterMessage message = new ClusterMessage( 551 ClusterMessage message = new ClusterMessage(
515 clusterService.getLocalNode().id(), 552 clusterService.getLocalNode().id(),
516 subject, 553 subject,
517 SERIALIZER.encode(event)); 554 SERIALIZER.encode(event));
518 clusterCommunicator.unicast(message, recipient); 555 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) {
568 + try {
533 unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_UPDATE, event); 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) {
577 + try {
537 unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_REMOVED, event); 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) {
656 +
657 + final NodeId sender = ad.sender();
658 + boolean localOutdated = false;
612 659
613 - NodeId peer = advertisement.sender(); 660 + for (Entry<LinkKey, Map<ProviderId, Timestamped<LinkDescription>>>
661 + l : linkDescs.entrySet()) {
614 662
615 - Map<LinkFragmentId, Timestamp> linkTimestamps = advertisement.linkTimestamps(); 663 + final LinkKey key = l.getKey();
616 - Map<LinkKey, Timestamp> linkTombstones = advertisement.linkTombstones(); 664 + final Map<ProviderId, Timestamped<LinkDescription>> link = l.getValue();
617 - for (Map.Entry<LinkFragmentId, Timestamp> entry : linkTimestamps.entrySet()) { 665 + synchronized (link) {
618 - LinkFragmentId linkFragmentId = entry.getKey(); 666 + Timestamp localLatest = removedLinks.get(key);
619 - Timestamp peerTimestamp = entry.getValue();
620 667
621 - LinkKey key = linkFragmentId.linkKey(); 668 + for (Entry<ProviderId, Timestamped<LinkDescription>> p : link.entrySet()) {
622 - ProviderId providerId = linkFragmentId.providerId(); 669 + final ProviderId providerId = p.getKey();
670 + final Timestamped<LinkDescription> pDesc = p.getValue();
623 671
624 - Timestamped<LinkDescription> linkDescription = getLinkDescription(key, providerId); 672 + final LinkFragmentId fragId = new LinkFragmentId(key, providerId);
625 - if (linkDescription.isNewer(peerTimestamp)) { 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)) {
626 // I have more recent link description. update peer. 680 // I have more recent link description. update peer.
627 - notifyPeer(peer, new InternalLinkEvent(providerId, linkDescription)); 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 + }
628 } 689 }
629 - // else TODO: Peer has more recent link description. request it.
630 690
631 - Timestamp linkRemovedTimestamp = removedLinks.get(key); 691 + // search local latest along the way
632 - if (linkRemovedTimestamp != null && linkRemovedTimestamp.compareTo(peerTimestamp) > 0) { 692 + if (localLatest == null ||
633 - // peer has a zombie link. update peer. 693 + pDesc.isNewer(localLatest)) {
634 - notifyPeer(peer, new InternalLinkRemovedEvent(key, linkRemovedTimestamp)); 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();
641 - 712 + final Timestamp remoteRemove = remoteRm.getValue();
642 - ProviderId primaryProviderId = pickPrimaryProviderId(getLinkDescriptions(key)); 713 + // relying on removeLinkInternal to ignore stale info
643 - if (primaryProviderId != null) { 714 + notifyDelegateIfNotNull(removeLinkInternal(key, remoteRemove));
644 - if (!getLinkDescription(key, primaryProviderId).isNewer(peerTimestamp)) {
645 - notifyDelegateIfNotNull(removeLinkInternal(key, peerTimestamp));
646 } 715 }
716 +
717 + if (localOutdated) {
718 + // send back advertisement to speed up convergence
719 + try {
720 + unicastMessage(sender, LINK_ANTI_ENTROPY_ADVERTISEMENT,
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)
67 74
68 - masters.addEntryListener(new RemoteMasterShipEventHandler(), true); 75 + .register(RoleValue.class, new RoleValueSerializer())
76 + .build()
77 + .populate(1);
78 + }
79 + };
80 +
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);
108 - lock.lock();
109 - try {
110 MastershipRole role = getRole(nodeId, deviceId); 118 MastershipRole role = getRole(nodeId, deviceId);
119 + roleMap.lock(deviceId);
120 + try {
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);
339 + }
340 + return null;
303 } 341 }
304 - if (standbys.containsEntry(deviceId, nodeId)) { 342 +
305 - standbys.remove(deviceId, nodeId); 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) {
355 + terms.lock(deviceId);
356 + try {
311 Integer term = terms.get(deviceId); 357 Integer term = terms.get(deviceId);
312 if (term == null) { 358 if (term == null) {
313 terms.put(deviceId, INIT); 359 terms.put(deviceId, INIT);
314 } else { 360 } else {
315 terms.put(deviceId, ++term); 361 terms.put(deviceId, ++term);
316 } 362 }
363 + } finally {
364 + terms.unlock(deviceId);
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()));
268 - }
269 } 271 }
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 } 272 }
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>
...@@ -23,20 +25,13 @@ ...@@ -23,20 +25,13 @@
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> 28 <groupId>org.onlab.onos</groupId>
31 - <artifactId>onlab-misc</artifactId> 29 + <artifactId>onos-api</artifactId>
32 - </dependency>
33 - <dependency>
34 - <groupId>org.onlab.onos</groupId>
35 - <artifactId>onlab-junit</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 } 187 }
188 +
189 + @Test
190 + public void testHostLocation() {
191 + testSerialized(new HostLocation(CP1, 1234L));
121 } 192 }
122 193
123 @Test 194 @Test
124 - public final void testAnnotations() { 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;
62 } 70 }
63 71
64 - @Override 72 + private static NewConcurrentHashMap<FlowId, StoredFlowEntry> lazyEmptyFlowTable() {
65 - public synchronized FlowEntry getFlowEntry(FlowRule rule) { 73 + return NewConcurrentHashMap.<FlowId, StoredFlowEntry>ifNeeded();
66 - for (FlowEntry f : flowEntries.get(rule.deviceId())) {
67 - if (f.equals(rule)) {
68 - return f;
69 } 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());
70 } 85 }
71 - return null; 86 +
87 + private StoredFlowEntry getFlowEntry(DeviceId deviceId, FlowId flowId) {
88 + return getFlowTable(deviceId).get(flowId);
72 } 89 }
73 90
74 @Override 91 @Override
75 - public synchronized Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) { 92 + public FlowEntry getFlowEntry(FlowRule rule) {
76 - Collection<FlowEntry> rules = flowEntries.get(deviceId); 93 + return getFlowEntry(rule.deviceId(), rule.id());
77 - if (rules == null) {
78 - return Collections.emptyList();
79 } 94 }
80 - return ImmutableSet.copyOf(rules); 95 +
96 + @Override
97 + public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
98 + return unmodifiableCollection((Collection<? extends FlowEntry>)
99 + getFlowTable(deviceId).values());
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);
88 } 111 }
89 - return ImmutableSet.copyOf(rules); 112 + }
113 + }
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();
96 - if (!flowEntries.containsEntry(did, f)) {
97 - flowEntries.put(did, f);
98 - flowEntriesById.put(rule.appId(), f);
99 } 120 }
121 +
122 + private boolean storeFlowRuleInternal(FlowRule rule) {
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;
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 }
144 + synchronized (entry) {
109 entry.setState(FlowEntryState.PENDING_REMOVE); 145 entry.setState(FlowEntryState.PENDING_REMOVE);
110 } 146 }
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) {
154 + synchronized (stored) {
119 stored.setBytes(rule.bytes()); 155 stored.setBytes(rule.bytes());
120 stored.setLife(rule.life()); 156 stored.setLife(rule.life());
121 stored.setPackets(rule.packets()); 157 stored.setPackets(rule.packets());
122 if (stored.state() == FlowEntryState.PENDING_ADD) { 158 if (stored.state() == FlowEntryState.PENDING_ADD) {
123 stored.setState(FlowEntryState.ADDED); 159 stored.setState(FlowEntryState.ADDED);
160 + // TODO: Do we need to change `rule` state?
124 return new FlowRuleEvent(Type.RULE_ADDED, rule); 161 return new FlowRuleEvent(Type.RULE_ADDED, rule);
125 } 162 }
126 return new FlowRuleEvent(Type.RULE_UPDATED, rule); 163 return new FlowRuleEvent(Type.RULE_UPDATED, rule);
127 } 164 }
165 + }
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);
128 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) {
315 + if (input == null) {
316 + return null;
317 + } else {
305 return links.get(input); 318 return links.get(input);
306 } 319 }
307 } 320 }
308 -
309 - private static final Predicate<Provided> IS_PRIMARY = new IsPrimary();
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 } 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">
...@@ -30,13 +31,15 @@ ...@@ -30,13 +31,15 @@
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>
...@@ -101,9 +104,9 @@ ...@@ -101,9 +104,9 @@
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
...@@ -153,10 +156,24 @@ ...@@ -153,10 +156,24 @@
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>
155 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 +
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">
158 <feature>onos-api</feature> 168 <feature>onos-api</feature>
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,6 +107,12 @@ ...@@ -107,6 +107,12 @@
107 </dependency> 107 </dependency>
108 108
109 <dependency> 109 <dependency>
110 + <groupId>com.googlecode.concurrent-trees</groupId>
111 + <artifactId>concurrent-trees</artifactId>
112 + <version>2.4.0</version>
113 + </dependency>
114 +
115 + <dependency>
110 <groupId>commons-lang</groupId> 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>
...@@ -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>
......