Madan Jampani

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

Showing 117 changed files with 2137 additions and 486 deletions
...@@ -8,5 +8,6 @@ ...@@ -8,5 +8,6 @@
8 .checkstyle 8 .checkstyle
9 target 9 target
10 *.iml 10 *.iml
11 +*.pyc
11 dependency-reduced-pom.xml 12 dependency-reduced-pom.xml
12 .idea 13 .idea
......
...@@ -16,14 +16,24 @@ ...@@ -16,14 +16,24 @@
16 package org.onlab.onos.calendar; 16 package org.onlab.onos.calendar;
17 17
18 import java.net.URI; 18 import java.net.URI;
19 +import java.util.concurrent.CountDownLatch;
20 +import java.util.concurrent.TimeUnit;
21 +
19 import org.onlab.onos.net.ConnectPoint; 22 import org.onlab.onos.net.ConnectPoint;
20 import org.onlab.onos.net.DeviceId; 23 import org.onlab.onos.net.DeviceId;
24 +import org.onlab.onos.net.intent.Intent;
25 +import org.onlab.onos.net.intent.IntentEvent;
26 +import org.onlab.onos.net.intent.IntentId;
27 +import org.onlab.onos.net.intent.IntentListener;
21 import org.onlab.onos.net.intent.IntentService; 28 import org.onlab.onos.net.intent.IntentService;
29 +import org.onlab.onos.net.intent.IntentState;
22 import org.onlab.rest.BaseResource; 30 import org.onlab.rest.BaseResource;
31 +
23 import javax.ws.rs.POST; 32 import javax.ws.rs.POST;
24 import javax.ws.rs.DELETE; 33 import javax.ws.rs.DELETE;
25 import javax.ws.rs.PathParam; 34 import javax.ws.rs.PathParam;
26 import javax.ws.rs.core.Response; 35 import javax.ws.rs.core.Response;
36 +
27 import org.onlab.onos.core.ApplicationId; 37 import org.onlab.onos.core.ApplicationId;
28 import org.onlab.onos.core.CoreService; 38 import org.onlab.onos.core.CoreService;
29 import org.onlab.onos.net.flow.DefaultTrafficSelector; 39 import org.onlab.onos.net.flow.DefaultTrafficSelector;
...@@ -31,10 +41,15 @@ import org.onlab.onos.net.flow.TrafficSelector; ...@@ -31,10 +41,15 @@ import org.onlab.onos.net.flow.TrafficSelector;
31 import org.onlab.onos.net.flow.TrafficTreatment; 41 import org.onlab.onos.net.flow.TrafficTreatment;
32 import org.onlab.onos.net.intent.PointToPointIntent; 42 import org.onlab.onos.net.intent.PointToPointIntent;
33 import org.onlab.packet.Ethernet; 43 import org.onlab.packet.Ethernet;
44 +
34 import static org.onlab.onos.net.PortNumber.portNumber; 45 import static org.onlab.onos.net.PortNumber.portNumber;
35 import static org.onlab.onos.net.flow.DefaultTrafficTreatment.builder; 46 import static org.onlab.onos.net.flow.DefaultTrafficTreatment.builder;
36 47
48 +import static org.onlab.onos.net.intent.IntentState.FAILED;
49 +import static org.onlab.onos.net.intent.IntentState.INSTALLED;
50 +import static org.onlab.onos.net.intent.IntentState.WITHDRAWN;
37 import static org.slf4j.LoggerFactory.getLogger; 51 import static org.slf4j.LoggerFactory.getLogger;
52 +
38 import org.slf4j.Logger; 53 import org.slf4j.Logger;
39 54
40 /** 55 /**
...@@ -44,6 +59,7 @@ import org.slf4j.Logger; ...@@ -44,6 +59,7 @@ import org.slf4j.Logger;
44 public class BandwidthCalendarResource extends BaseResource { 59 public class BandwidthCalendarResource extends BaseResource {
45 60
46 private static final Logger log = getLogger(BandwidthCalendarResource.class); 61 private static final Logger log = getLogger(BandwidthCalendarResource.class);
62 + private static final long TIMEOUT = 5; // seconds
47 63
48 @javax.ws.rs.Path("/{src}/{dst}/{srcPort}/{dstPort}/{bandwidth}") 64 @javax.ws.rs.Path("/{src}/{dst}/{srcPort}/{dstPort}/{bandwidth}")
49 @POST 65 @POST
...@@ -68,24 +84,38 @@ public class BandwidthCalendarResource extends BaseResource { ...@@ -68,24 +84,38 @@ public class BandwidthCalendarResource extends BaseResource {
68 PointToPointIntent intentP2P = 84 PointToPointIntent intentP2P =
69 new PointToPointIntent(appId(), selector, treatment, 85 new PointToPointIntent(appId(), selector, treatment,
70 srcPoint, dstPoint); 86 srcPoint, dstPoint);
71 - service.submit(intentP2P);
72 - log.info("Submitted Calendar App intent: src = " + src + "dest = " + dst
73 - + "srcPort = " + srcPort + "destPort" + dstPort + "intentID = " + intentP2P.id().toString());
74 - String reply = intentP2P.id().toString() + "\n";
75 87
88 + CountDownLatch latch = new CountDownLatch(1);
89 + InternalIntentListener listener = new InternalIntentListener(intentP2P, service, latch);
90 + service.addListener(listener);
91 + service.submit(intentP2P);
92 + try {
93 + if (latch.await(TIMEOUT, TimeUnit.SECONDS)) {
94 + log.info("Submitted Calendar App intent: src = {}; dst = {}; " +
95 + "srcPort = {}; dstPort = {}; intentID = {}",
96 + src, dst, srcPort, dstPort, intentP2P.id());
97 + String reply = intentP2P.id() + " " + listener.getState() + "\n";
76 return Response.ok(reply).build(); 98 return Response.ok(reply).build();
77 } 99 }
100 + } catch (InterruptedException e) {
101 + log.warn("Interrupted while waiting for intent {} status", intentP2P.id());
102 + }
103 + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
104 + }
78 105
79 @javax.ws.rs.Path("/cancellation/{intentId}") 106 @javax.ws.rs.Path("/cancellation/{intentId}")
80 @DELETE 107 @DELETE
81 public Response withdrawIntent(@PathParam("intentId") String intentId) { 108 public Response withdrawIntent(@PathParam("intentId") String intentId) {
82 - 109 + log.info("Receiving Teardown request for {}", intentId);
83 - log.info("Receiving Teardown request..."); 110 + IntentService service = get(IntentService.class);
84 - log.info("Withdraw intentId = {} ", intentId); 111 + Intent intent = service.getIntent(IntentId.valueOf(Long.parseLong(intentId)));
85 - 112 + if (intent != null) {
113 + service.withdraw(intent);
86 String reply = "ok\n"; 114 String reply = "ok\n";
87 return Response.ok(reply).build(); 115 return Response.ok(reply).build();
88 } 116 }
117 + return Response.status(Response.Status.NOT_FOUND).build();
118 + }
89 119
90 @javax.ws.rs.Path("/modification/{intentId}/{bandwidth}") 120 @javax.ws.rs.Path("/modification/{intentId}/{bandwidth}")
91 @POST 121 @POST
...@@ -115,4 +145,34 @@ public class BandwidthCalendarResource extends BaseResource { ...@@ -115,4 +145,34 @@ public class BandwidthCalendarResource extends BaseResource {
115 protected ApplicationId appId() { 145 protected ApplicationId appId() {
116 return get(CoreService.class).registerApplication("org.onlab.onos.calendar"); 146 return get(CoreService.class).registerApplication("org.onlab.onos.calendar");
117 } 147 }
148 +
149 + // Auxiliary listener to wait until the given intent reaches the installed or failed states.
150 + private final class InternalIntentListener implements IntentListener {
151 + private final Intent intent;
152 + private final IntentService service;
153 + private final CountDownLatch latch;
154 + private IntentState state;
155 +
156 + private InternalIntentListener(Intent intent, IntentService service,
157 + CountDownLatch latch) {
158 + this.intent = intent;
159 + this.service = service;
160 + this.latch = latch;
161 + }
162 +
163 + @Override
164 + public void event(IntentEvent event) {
165 + if (event.subject().equals(intent)) {
166 + state = service.getIntentState(intent.id());
167 + if (state == INSTALLED || state == FAILED || state == WITHDRAWN) {
168 + latch.countDown();
169 + }
170 + service.removeListener(this);
171 + }
172 + }
173 +
174 + public IntentState getState() {
175 + return state;
176 + }
177 + }
118 } 178 }
......
...@@ -50,7 +50,10 @@ public class NetworkConfigReader { ...@@ -50,7 +50,10 @@ public class NetworkConfigReader {
50 50
51 private final Logger log = getLogger(getClass()); 51 private final Logger log = getLogger(getClass());
52 52
53 - private static final String DEFAULT_CONFIG_FILE = "config/addresses.json"; 53 + // Current working dir seems to be /opt/onos/apache-karaf-3.0.2
54 + // TODO: Set the path to /opt/onos/config
55 + private static final String CONFIG_DIR = "../config";
56 + private static final String DEFAULT_CONFIG_FILE = "addresses.json";
54 private String configFileName = DEFAULT_CONFIG_FILE; 57 private String configFileName = DEFAULT_CONFIG_FILE;
55 58
56 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 59 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
...@@ -60,19 +63,53 @@ public class NetworkConfigReader { ...@@ -60,19 +63,53 @@ public class NetworkConfigReader {
60 protected void activate() { 63 protected void activate() {
61 log.info("Started network config reader"); 64 log.info("Started network config reader");
62 65
63 - log.info("Config file set to {}", configFileName);
64 -
65 AddressConfiguration config = readNetworkConfig(); 66 AddressConfiguration config = readNetworkConfig();
66 -
67 if (config != null) { 67 if (config != null) {
68 - for (AddressEntry entry : config.getAddresses()) { 68 + applyNetworkConfig(config);
69 + }
70 + }
71 +
72 + @Deactivate
73 + protected void deactivate() {
74 + log.info("Stopped");
75 + }
76 +
77 + /**
78 + * Reads the network configuration.
79 + *
80 + * @return the network configuration on success, otherwise null
81 + */
82 + private AddressConfiguration readNetworkConfig() {
83 + File configFile = new File(CONFIG_DIR, configFileName);
84 + ObjectMapper mapper = new ObjectMapper();
85 +
86 + try {
87 + log.info("Loading config: {}", configFile.getAbsolutePath());
88 + AddressConfiguration config =
89 + mapper.readValue(configFile, AddressConfiguration.class);
90 +
91 + return config;
92 + } catch (FileNotFoundException e) {
93 + log.warn("Configuration file not found: {}", configFileName);
94 + } catch (IOException e) {
95 + log.error("Error loading configuration", e);
96 + }
97 +
98 + return null;
99 + }
69 100
101 + /**
102 + * Applies the network configuration.
103 + *
104 + * @param config the network configuration to apply
105 + */
106 + private void applyNetworkConfig(AddressConfiguration config) {
107 + for (AddressEntry entry : config.getAddresses()) {
70 ConnectPoint cp = new ConnectPoint( 108 ConnectPoint cp = new ConnectPoint(
71 DeviceId.deviceId(dpidToUri(entry.getDpid())), 109 DeviceId.deviceId(dpidToUri(entry.getDpid())),
72 PortNumber.portNumber(entry.getPortNumber())); 110 PortNumber.portNumber(entry.getPortNumber()));
73 111
74 Set<InterfaceIpAddress> interfaceIpAddresses = new HashSet<>(); 112 Set<InterfaceIpAddress> interfaceIpAddresses = new HashSet<>();
75 -
76 for (String strIp : entry.getIpAddresses()) { 113 for (String strIp : entry.getIpAddresses()) {
77 // Get the IP address and the subnet mask length 114 // Get the IP address and the subnet mask length
78 try { 115 try {
...@@ -103,35 +140,9 @@ public class NetworkConfigReader { ...@@ -103,35 +140,9 @@ public class NetworkConfigReader {
103 140
104 PortAddresses addresses = new PortAddresses(cp, 141 PortAddresses addresses = new PortAddresses(cp,
105 interfaceIpAddresses, macAddress); 142 interfaceIpAddresses, macAddress);
106 -
107 hostAdminService.bindAddressesToPort(addresses); 143 hostAdminService.bindAddressesToPort(addresses);
108 } 144 }
109 } 145 }
110 - }
111 -
112 - @Deactivate
113 - protected void deactivate() {
114 - log.info("Stopped");
115 - }
116 -
117 - private AddressConfiguration readNetworkConfig() {
118 - File configFile = new File(configFileName);
119 -
120 - ObjectMapper mapper = new ObjectMapper();
121 -
122 - try {
123 - AddressConfiguration config =
124 - mapper.readValue(configFile, AddressConfiguration.class);
125 -
126 - return config;
127 - } catch (FileNotFoundException e) {
128 - log.warn("Configuration file not found: {}", configFileName);
129 - } catch (IOException e) {
130 - log.error("Unable to read config from file:", e);
131 - }
132 -
133 - return null;
134 - }
135 146
136 private static String dpidToUri(String dpid) { 147 private static String dpidToUri(String dpid) {
137 return "of:" + dpid.replace(":", ""); 148 return "of:" + dpid.replace(":", "");
......
1 -{
2 - "interfaces" : [
3 - {
4 - "dpid" : "00:00:00:00:00:00:01",
5 - "port" : "1",
6 - "ips" : ["192.168.10.101/24"],
7 - "mac" : "00:00:00:11:22:33"
8 - },
9 - {
10 - "dpid" : "00:00:00:00:00:00:02",
11 - "port" : "1",
12 - "ips" : ["192.168.20.101/24", "192.168.30.101/24"]
13 - },
14 - {
15 - "dpid" : "00:00:00:00:00:00:03",
16 - "port" : "1",
17 - "ips" : ["10.1.0.1/16"],
18 - "mac" : "00:00:00:00:00:01"
19 - }
20 - ]
21 -}
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2014 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +<project xmlns="http://maven.apache.org/POM/4.0.0"
18 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
19 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
20 + <modelVersion>4.0.0</modelVersion>
21 +
22 + <parent>
23 + <groupId>org.onlab.onos</groupId>
24 + <artifactId>onos-apps</artifactId>
25 + <version>1.0.0-SNAPSHOT</version>
26 + <relativePath>../pom.xml</relativePath>
27 + </parent>
28 +
29 + <artifactId>onos-app-demo</artifactId>
30 + <packaging>bundle</packaging>
31 +
32 + <description>ONOS demo app bundle</description>
33 +
34 + <properties>
35 + <web.context>/onos/demo</web.context>
36 + </properties>
37 +
38 + <dependencies>
39 + <dependency>
40 + <groupId>org.osgi</groupId>
41 + <artifactId>org.osgi.compendium</artifactId>
42 + </dependency>
43 + <dependency>
44 + <groupId>org.onlab.onos</groupId>
45 + <artifactId>onlab-rest</artifactId>
46 + <version>${project.version}</version>
47 + </dependency>
48 +
49 + <dependency>
50 + <groupId>org.onlab.onos</groupId>
51 + <artifactId>onos-rest</artifactId>
52 + <version>${project.version}</version>
53 + </dependency>
54 +
55 + <dependency>
56 + <groupId>com.sun.jersey</groupId>
57 + <artifactId>jersey-servlet</artifactId>
58 + </dependency>
59 + <dependency>
60 + <groupId>com.sun.jersey.jersey-test-framework</groupId>
61 + <artifactId>jersey-test-framework-core</artifactId>
62 + <version>1.18.1</version>
63 + <scope>test</scope>
64 + </dependency>
65 + <dependency>
66 + <groupId>com.sun.jersey.jersey-test-framework</groupId>
67 + <artifactId>jersey-test-framework-grizzly2</artifactId>
68 + <version>1.18.1</version>
69 + <scope>test</scope>
70 + </dependency>
71 +
72 + <dependency>
73 + <groupId>com.fasterxml.jackson.core</groupId>
74 + <artifactId>jackson-databind</artifactId>
75 + </dependency>
76 +
77 + <dependency>
78 + <groupId>com.fasterxml.jackson.core</groupId>
79 + <artifactId>jackson-annotations</artifactId>
80 + </dependency>
81 +
82 + <dependency>
83 + <groupId>org.osgi</groupId>
84 + <artifactId>org.osgi.core</artifactId>
85 + </dependency>
86 + </dependencies>
87 +
88 + <build>
89 + <plugins>
90 + <plugin>
91 + <groupId>org.apache.felix</groupId>
92 + <artifactId>maven-bundle-plugin</artifactId>
93 + <extensions>true</extensions>
94 + <configuration>
95 + <instructions>
96 + <_wab>src/main/webapp/</_wab>
97 + <Bundle-SymbolicName>
98 + ${project.groupId}.${project.artifactId}
99 + </Bundle-SymbolicName>
100 + <Import-Package>
101 + org.slf4j,
102 + org.osgi.framework,
103 + javax.ws.rs,javax.ws.rs.core,
104 + com.sun.jersey.api.core,
105 + com.sun.jersey.spi.container.servlet,
106 + com.sun.jersey.server.impl.container.servlet,
107 + com.fasterxml.jackson.databind,
108 + com.fasterxml.jackson.databind.node,
109 + com.google.common.*,
110 + org.onlab.packet.*,
111 + org.onlab.rest.*,
112 + org.onlab.onos.*
113 + </Import-Package>
114 + <Web-ContextPath>${web.context}</Web-ContextPath>
115 + </instructions>
116 + </configuration>
117 + </plugin>
118 + </plugins>
119 + </build>
120 +
121 +</project>
1 +package org.onlab.onos.demo;
2 +
3 +/**
4 + * Simple demo api interface.
5 + */
6 +public interface DemoAPI {
7 +
8 + enum InstallType { MESH, RANDOM };
9 +
10 + /**
11 + * Installs intents based on the installation type.
12 + * @param type the installation type.
13 + */
14 + void setup(InstallType type);
15 +
16 + /**
17 + * Uninstalls all existing intents.
18 + */
19 + void tearDown();
20 +
21 +}
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.demo;
17 +
18 +import com.google.common.collect.Lists;
19 +import com.google.common.util.concurrent.ThreadFactoryBuilder;
20 +import org.apache.felix.scr.annotations.Activate;
21 +import org.apache.felix.scr.annotations.Component;
22 +import org.apache.felix.scr.annotations.Deactivate;
23 +import org.apache.felix.scr.annotations.Reference;
24 +import org.apache.felix.scr.annotations.ReferenceCardinality;
25 +import org.apache.felix.scr.annotations.Service;
26 +import org.onlab.onos.core.ApplicationId;
27 +import org.onlab.onos.core.CoreService;
28 +import org.onlab.onos.net.Host;
29 +import org.onlab.onos.net.flow.DefaultTrafficSelector;
30 +import org.onlab.onos.net.flow.DefaultTrafficTreatment;
31 +import org.onlab.onos.net.flow.TrafficSelector;
32 +import org.onlab.onos.net.flow.TrafficTreatment;
33 +import org.onlab.onos.net.host.HostService;
34 +import org.onlab.onos.net.intent.HostToHostIntent;
35 +import org.onlab.onos.net.intent.Intent;
36 +import org.onlab.onos.net.intent.IntentService;
37 +import org.slf4j.Logger;
38 +
39 +
40 +import java.util.HashSet;
41 +import java.util.List;
42 +import java.util.Set;
43 +import java.util.concurrent.ExecutorService;
44 +import java.util.concurrent.Executors;
45 +
46 +import static org.slf4j.LoggerFactory.getLogger;
47 +
48 +/**
49 + * Application to set up demos.
50 + */
51 +@Component(immediate = true)
52 +@Service
53 +public class DemoInstaller implements DemoAPI {
54 +
55 + private final Logger log = getLogger(getClass());
56 +
57 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
58 + protected CoreService coreService;
59 +
60 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
61 + protected IntentService intentService;
62 +
63 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
64 + protected HostService hostService;
65 +
66 + private ExecutorService worker;
67 +
68 + private ApplicationId appId;
69 +
70 + private final Set<Intent> existingIntents = new HashSet<>();
71 +
72 +
73 +
74 + @Activate
75 + public void activate() {
76 + appId = coreService.registerApplication("org.onlab.onos.demo.installer");
77 + worker = Executors.newFixedThreadPool(1,
78 + new ThreadFactoryBuilder()
79 + .setNameFormat("demo-app-worker")
80 + .build());
81 + log.info("Started with Application ID {}", appId.id());
82 + }
83 +
84 + @Deactivate
85 + public void deactivate() {
86 + worker.shutdownNow();
87 + log.info("Stopped");
88 + }
89 +
90 + @Override
91 + public void setup(InstallType type) {
92 + switch (type) {
93 + case MESH:
94 + log.debug("Installing mesh intents");
95 + worker.execute(new MeshInstaller());
96 + break;
97 + case RANDOM:
98 + throw new IllegalArgumentException("Not yet implemented.");
99 + default:
100 + throw new IllegalArgumentException("What is it you want exactly?");
101 + }
102 + }
103 +
104 + @Override
105 + public void tearDown() {
106 + worker.submit(new UnInstaller());
107 + }
108 +
109 +
110 + private class MeshInstaller implements Runnable {
111 +
112 + @Override
113 + public void run() {
114 + TrafficSelector selector = DefaultTrafficSelector.builder().build();
115 + TrafficTreatment treatment = DefaultTrafficTreatment.builder().build();
116 +
117 + List<Host> hosts = Lists.newArrayList(hostService.getHosts());
118 + while (!hosts.isEmpty()) {
119 + Host src = hosts.remove(0);
120 + for (Host dst : hosts) {
121 + HostToHostIntent intent = new HostToHostIntent(appId, src.id(), dst.id(),
122 + selector, treatment,
123 + null);
124 + existingIntents.add(intent);
125 + intentService.submit(intent);
126 + }
127 + }
128 + }
129 + }
130 +
131 +
132 + private class UnInstaller implements Runnable {
133 + @Override
134 + public void run() {
135 + for (Intent i : existingIntents) {
136 + intentService.withdraw(i);
137 + }
138 + }
139 + }
140 +}
141 +
142 +
1 +package org.onlab.onos.demo;
2 +
3 +import com.fasterxml.jackson.databind.JsonNode;
4 +import com.fasterxml.jackson.databind.ObjectMapper;
5 +import org.onlab.rest.BaseResource;
6 +
7 +import javax.ws.rs.Consumes;
8 +import javax.ws.rs.GET;
9 +import javax.ws.rs.POST;
10 +import javax.ws.rs.Path;
11 +import javax.ws.rs.Produces;
12 +import javax.ws.rs.core.MediaType;
13 +import javax.ws.rs.core.Response;
14 +import java.io.IOException;
15 +import java.io.InputStream;
16 +
17 +/**
18 + * Rest API for demos.
19 + */
20 +@Path("intents")
21 +public class DemoResource extends BaseResource {
22 +
23 +
24 + @POST
25 + @Path("setup")
26 + @Consumes(MediaType.APPLICATION_JSON)
27 + @Produces(MediaType.APPLICATION_JSON)
28 + public Response setup(InputStream input) throws IOException {
29 + ObjectMapper mapper = new ObjectMapper();
30 + JsonNode cfg = mapper.readTree(input);
31 + if (!cfg.has("type")) {
32 + return Response.status(Response.Status.BAD_REQUEST)
33 + .entity("Expected type field containing either mesh or random.").build();
34 + }
35 +
36 + DemoAPI.InstallType type = DemoAPI.InstallType.valueOf(
37 + cfg.get("type").asText().toUpperCase());
38 + DemoAPI demo = get(DemoAPI.class);
39 + demo.setup(type);
40 +
41 + return Response.ok(mapper.createObjectNode().toString()).build();
42 + }
43 +
44 + @GET
45 + @Path("teardown")
46 + @Produces(MediaType.APPLICATION_JSON)
47 + public Response tearDown() throws IOException {
48 + ObjectMapper mapper = new ObjectMapper();
49 + DemoAPI demo = get(DemoAPI.class);
50 + demo.tearDown();
51 + return Response.ok(mapper.createObjectNode().toString()).build();
52 + }
53 +
54 +}
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 +
17 +/**
18 + * Demo applications live here.
19 + */
20 +package org.onlab.onos.demo;
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<!--
3 + ~ Copyright 2014 Open Networking Laboratory
4 + ~
5 + ~ Licensed under the Apache License, Version 2.0 (the "License");
6 + ~ you may not use this file except in compliance with the License.
7 + ~ You may obtain a copy of the License at
8 + ~
9 + ~ http://www.apache.org/licenses/LICENSE-2.0
10 + ~
11 + ~ Unless required by applicable law or agreed to in writing, software
12 + ~ distributed under the License is distributed on an "AS IS" BASIS,
13 + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 + ~ See the License for the specific language governing permissions and
15 + ~ limitations under the License.
16 + -->
17 +<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
18 + xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
19 + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
20 + id="ONOS" version="2.5">
21 + <display-name>ONOS DEMO APP API v1.0</display-name>
22 +
23 + <servlet>
24 + <servlet-name>JAX-RS Service</servlet-name>
25 + <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
26 + <init-param>
27 + <param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
28 + <param-value>com.sun.jersey.api.core.ClassNamesResourceConfig</param-value>
29 + </init-param>
30 + <init-param>
31 + <param-name>com.sun.jersey.config.property.classnames</param-name>
32 + <param-value>
33 + org.onlab.onos.demo.DemoResource
34 + </param-value>
35 + </init-param>
36 + <load-on-startup>1</load-on-startup>
37 + </servlet>
38 +
39 + <servlet-mapping>
40 + <servlet-name>JAX-RS Service</servlet-name>
41 + <url-pattern>/*</url-pattern>
42 + </servlet-mapping>
43 +
44 +</web-app>
...\ No newline at end of file ...\ No newline at end of file
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
44 <module>optical</module> 44 <module>optical</module>
45 <module>metrics</module> 45 <module>metrics</module>
46 <module>oecfg</module> 46 <module>oecfg</module>
47 + <module>demo</module>
47 </modules> 48 </modules>
48 49
49 <properties> 50 <properties>
......
...@@ -37,24 +37,31 @@ import com.fasterxml.jackson.databind.ObjectMapper; ...@@ -37,24 +37,31 @@ import com.fasterxml.jackson.databind.ObjectMapper;
37 */ 37 */
38 public class SdnIpConfigReader implements SdnIpConfigService { 38 public class SdnIpConfigReader implements SdnIpConfigService {
39 39
40 - private static final Logger log = LoggerFactory.getLogger(SdnIpConfigReader.class); 40 + private final Logger log = LoggerFactory.getLogger(getClass());
41 41
42 - private static final String DEFAULT_CONFIG_FILE = "config/sdnip.json"; 42 + // Current working dir seems to be /opt/onos/apache-karaf-3.0.2
43 + // TODO: Set the path to /opt/onos/config
44 + private static final String CONFIG_DIR = "../config";
45 + private static final String DEFAULT_CONFIG_FILE = "sdnip.json";
43 private String configFileName = DEFAULT_CONFIG_FILE; 46 private String configFileName = DEFAULT_CONFIG_FILE;
47 +
44 private Map<String, BgpSpeaker> bgpSpeakers = new ConcurrentHashMap<>(); 48 private Map<String, BgpSpeaker> bgpSpeakers = new ConcurrentHashMap<>();
45 private Map<IpAddress, BgpPeer> bgpPeers = new ConcurrentHashMap<>(); 49 private Map<IpAddress, BgpPeer> bgpPeers = new ConcurrentHashMap<>();
46 50
47 /** 51 /**
48 - * Reads the info contained in the configuration file. 52 + * Reads SDN-IP related information contained in the configuration file.
49 * 53 *
50 - * @param configFilename The name of configuration file for SDN-IP application. 54 + * @param configFilename the name of the configuration file for the SDN-IP
55 + * application
51 */ 56 */
52 private void readConfiguration(String configFilename) { 57 private void readConfiguration(String configFilename) {
53 - File gatewaysFile = new File(configFilename); 58 + File configFile = new File(CONFIG_DIR, configFilename);
54 ObjectMapper mapper = new ObjectMapper(); 59 ObjectMapper mapper = new ObjectMapper();
55 60
56 try { 61 try {
57 - Configuration config = mapper.readValue(gatewaysFile, Configuration.class); 62 + log.info("Loading config: {}", configFile.getAbsolutePath());
63 + Configuration config = mapper.readValue(configFile,
64 + Configuration.class);
58 for (BgpSpeaker speaker : config.getBgpSpeakers()) { 65 for (BgpSpeaker speaker : config.getBgpSpeakers()) {
59 bgpSpeakers.put(speaker.name(), speaker); 66 bgpSpeakers.put(speaker.name(), speaker);
60 } 67 }
...@@ -64,13 +71,11 @@ public class SdnIpConfigReader implements SdnIpConfigService { ...@@ -64,13 +71,11 @@ public class SdnIpConfigReader implements SdnIpConfigService {
64 } catch (FileNotFoundException e) { 71 } catch (FileNotFoundException e) {
65 log.warn("Configuration file not found: {}", configFileName); 72 log.warn("Configuration file not found: {}", configFileName);
66 } catch (IOException e) { 73 } catch (IOException e) {
67 - log.error("Error reading JSON file", e); 74 + log.error("Error loading configuration", e);
68 } 75 }
69 } 76 }
70 77
71 public void init() { 78 public void init() {
72 - log.debug("Config file set to {}", configFileName);
73 -
74 readConfiguration(configFileName); 79 readConfiguration(configFileName);
75 } 80 }
76 81
......
1 -ONOS looks for these config files by default in $KARAF_LOG/config/
...\ No newline at end of file ...\ No newline at end of file
1 +The SDN-IP configuration files should be copied to directory
2 + $ONOS_HOME/tools/package/config
3 +
4 +After deployment and starting up the ONOS cluster, ONOS looks for these
5 +configuration files in /opt/onos/config on each cluster member.
......
...@@ -27,6 +27,7 @@ import org.onlab.onos.net.intent.constraint.BandwidthConstraint; ...@@ -27,6 +27,7 @@ import org.onlab.onos.net.intent.constraint.BandwidthConstraint;
27 import org.onlab.onos.net.intent.constraint.LambdaConstraint; 27 import org.onlab.onos.net.intent.constraint.LambdaConstraint;
28 import org.onlab.onos.net.resource.Bandwidth; 28 import org.onlab.onos.net.resource.Bandwidth;
29 import org.onlab.packet.Ethernet; 29 import org.onlab.packet.Ethernet;
30 +import org.onlab.packet.IpPrefix;
30 import org.onlab.packet.MacAddress; 31 import org.onlab.packet.MacAddress;
31 32
32 import static com.google.common.base.Strings.isNullOrEmpty; 33 import static com.google.common.base.Strings.isNullOrEmpty;
...@@ -48,6 +49,26 @@ public abstract class ConnectivityIntentCommand extends AbstractShellCommand { ...@@ -48,6 +49,26 @@ public abstract class ConnectivityIntentCommand extends AbstractShellCommand {
48 required = false, multiValued = false) 49 required = false, multiValued = false)
49 private String ethTypeString = ""; 50 private String ethTypeString = "";
50 51
52 + @Option(name = "--ipProto", description = "IP Protocol",
53 + required = false, multiValued = false)
54 + private String ipProtoString = null;
55 +
56 + @Option(name = "--ipSrc", description = "Source IP Address",
57 + required = false, multiValued = false)
58 + private String srcIpString = null;
59 +
60 + @Option(name = "--ipDst", description = "Destination IP Address",
61 + required = false, multiValued = false)
62 + private String dstIpString = null;
63 +
64 + @Option(name = "--tcpSrc", description = "Source TCP Port",
65 + required = false, multiValued = false)
66 + private String srcTcpString = null;
67 +
68 + @Option(name = "--tcpDst", description = "Destination TCP Port",
69 + required = false, multiValued = false)
70 + private String dstTcpString = null;
71 +
51 @Option(name = "-b", aliases = "--bandwidth", description = "Bandwidth", 72 @Option(name = "-b", aliases = "--bandwidth", description = "Bandwidth",
52 required = false, multiValued = false) 73 required = false, multiValued = false)
53 private String bandwidthString = ""; 74 private String bandwidthString = "";
...@@ -79,6 +100,26 @@ public abstract class ConnectivityIntentCommand extends AbstractShellCommand { ...@@ -79,6 +100,26 @@ public abstract class ConnectivityIntentCommand extends AbstractShellCommand {
79 selectorBuilder.matchEthDst(MacAddress.valueOf(dstMacString)); 100 selectorBuilder.matchEthDst(MacAddress.valueOf(dstMacString));
80 } 101 }
81 102
103 + if (!isNullOrEmpty(ipProtoString)) {
104 + selectorBuilder.matchIPProtocol((byte) Short.parseShort(ipProtoString));
105 + }
106 +
107 + if (!isNullOrEmpty(srcIpString)) {
108 + selectorBuilder.matchIPSrc(IpPrefix.valueOf(srcIpString));
109 + }
110 +
111 + if (!isNullOrEmpty(dstIpString)) {
112 + selectorBuilder.matchIPDst(IpPrefix.valueOf(dstIpString));
113 + }
114 +
115 + if (!isNullOrEmpty(srcTcpString)) {
116 + selectorBuilder.matchTcpSrc((short) Integer.parseInt(srcTcpString));
117 + }
118 +
119 + if (!isNullOrEmpty(dstTcpString)) {
120 + selectorBuilder.matchTcpSrc((short) Integer.parseInt(dstTcpString));
121 + }
122 +
82 return selectorBuilder.build(); 123 return selectorBuilder.build();
83 } 124 }
84 125
......
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.net;
17 +
18 +/**
19 + * Collection of keys for annotation.
20 + * Definitions of annotation keys needs to be here to avoid scattering.
21 + */
22 +public final class AnnotationKeys {
23 +
24 + // Prohibit instantiation
25 + private AnnotationKeys() {}
26 +
27 + /**
28 + * Annotation key for latency.
29 + */
30 + public static final String LATENCY = "latency";
31 +
32 + /**
33 + * Returns the value annotated object for the specified annotation key.
34 + * The annotated value is expected to be String that can be parsed as double.
35 + * If parsing fails, the returned value will be 1.0.
36 + *
37 + * @param annotated annotated object whose annotated value is obtained
38 + * @param key key of annotation
39 + * @return double value of annotated object for the specified key
40 + */
41 + public static double getAnnotatedValue(Annotated annotated, String key) {
42 + double value;
43 + try {
44 + value = Double.parseDouble(annotated.annotations().value(key));
45 + } catch (NumberFormatException e) {
46 + value = 1.0;
47 + }
48 + return value;
49 + }
50 +}
...@@ -80,6 +80,7 @@ public class DefaultFlowEntry extends DefaultFlowRule ...@@ -80,6 +80,7 @@ public class DefaultFlowEntry extends DefaultFlowRule
80 this.state = FlowEntryState.FAILED; 80 this.state = FlowEntryState.FAILED;
81 this.errType = errType; 81 this.errType = errType;
82 this.errCode = errCode; 82 this.errCode = errCode;
83 + this.lastSeen = System.currentTimeMillis();
83 } 84 }
84 85
85 @Override 86 @Override
......
...@@ -58,7 +58,7 @@ public class DefaultFlowRule implements FlowRule { ...@@ -58,7 +58,7 @@ public class DefaultFlowRule implements FlowRule {
58 } 58 }
59 59
60 public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector, 60 public DefaultFlowRule(DeviceId deviceId, TrafficSelector selector,
61 - TrafficTreatment treatement, int priority, ApplicationId appId, 61 + TrafficTreatment treatment, int priority, ApplicationId appId,
62 int timeout, boolean permanent) { 62 int timeout, boolean permanent) {
63 63
64 if (priority < FlowRule.MIN_PRIORITY) { 64 if (priority < FlowRule.MIN_PRIORITY) {
...@@ -68,7 +68,7 @@ public class DefaultFlowRule implements FlowRule { ...@@ -68,7 +68,7 @@ public class DefaultFlowRule implements FlowRule {
68 this.deviceId = deviceId; 68 this.deviceId = deviceId;
69 this.priority = priority; 69 this.priority = priority;
70 this.selector = selector; 70 this.selector = selector;
71 - this.treatment = treatement; 71 + this.treatment = treatment;
72 this.appId = appId.id(); 72 this.appId = appId.id();
73 this.timeout = timeout; 73 this.timeout = timeout;
74 this.permanent = permanent; 74 this.permanent = permanent;
......
...@@ -63,7 +63,7 @@ public final class DefaultTrafficSelector implements TrafficSelector { ...@@ -63,7 +63,7 @@ public final class DefaultTrafficSelector implements TrafficSelector {
63 63
64 @Override 64 @Override
65 public int hashCode() { 65 public int hashCode() {
66 - return Objects.hash(criteria); 66 + return criteria.hashCode();
67 } 67 }
68 68
69 @Override 69 @Override
......
...@@ -18,7 +18,7 @@ package org.onlab.onos.net.flow; ...@@ -18,7 +18,7 @@ package org.onlab.onos.net.flow;
18 import org.onlab.onos.core.ApplicationId; 18 import org.onlab.onos.core.ApplicationId;
19 import org.onlab.onos.net.provider.Provider; 19 import org.onlab.onos.net.provider.Provider;
20 20
21 -import com.google.common.util.concurrent.ListenableFuture; 21 +import java.util.concurrent.Future;
22 22
23 /** 23 /**
24 * Abstraction of a flow rule provider. 24 * Abstraction of a flow rule provider.
...@@ -58,6 +58,6 @@ public interface FlowRuleProvider extends Provider { ...@@ -58,6 +58,6 @@ public interface FlowRuleProvider extends Provider {
58 * @param batch a batch of flow rules 58 * @param batch a batch of flow rules
59 * @return a future indicating the status of this execution 59 * @return a future indicating the status of this execution
60 */ 60 */
61 - ListenableFuture<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch); 61 + Future<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch);
62 62
63 } 63 }
......
...@@ -196,7 +196,7 @@ public final class Criteria { ...@@ -196,7 +196,7 @@ public final class Criteria {
196 196
197 @Override 197 @Override
198 public int hashCode() { 198 public int hashCode() {
199 - return Objects.hash(port, type()); 199 + return Objects.hash(type(), port);
200 } 200 }
201 201
202 @Override 202 @Override
...@@ -242,7 +242,7 @@ public final class Criteria { ...@@ -242,7 +242,7 @@ public final class Criteria {
242 242
243 @Override 243 @Override
244 public int hashCode() { 244 public int hashCode() {
245 - return Objects.hash(mac, type); 245 + return Objects.hash(type, mac);
246 } 246 }
247 247
248 @Override 248 @Override
...@@ -288,7 +288,7 @@ public final class Criteria { ...@@ -288,7 +288,7 @@ public final class Criteria {
288 288
289 @Override 289 @Override
290 public int hashCode() { 290 public int hashCode() {
291 - return Objects.hash(ethType, type()); 291 + return Objects.hash(type(), ethType);
292 } 292 }
293 293
294 @Override 294 @Override
...@@ -336,7 +336,7 @@ public final class Criteria { ...@@ -336,7 +336,7 @@ public final class Criteria {
336 336
337 @Override 337 @Override
338 public int hashCode() { 338 public int hashCode() {
339 - return Objects.hash(ip, type); 339 + return Objects.hash(type, ip);
340 } 340 }
341 341
342 @Override 342 @Override
...@@ -382,7 +382,7 @@ public final class Criteria { ...@@ -382,7 +382,7 @@ public final class Criteria {
382 382
383 @Override 383 @Override
384 public int hashCode() { 384 public int hashCode() {
385 - return Objects.hash(proto, type()); 385 + return Objects.hash(type(), proto);
386 } 386 }
387 387
388 @Override 388 @Override
...@@ -427,7 +427,7 @@ public final class Criteria { ...@@ -427,7 +427,7 @@ public final class Criteria {
427 427
428 @Override 428 @Override
429 public int hashCode() { 429 public int hashCode() {
430 - return Objects.hash(vlanPcp); 430 + return Objects.hash(type(), vlanPcp);
431 } 431 }
432 432
433 @Override 433 @Override
...@@ -474,7 +474,7 @@ public final class Criteria { ...@@ -474,7 +474,7 @@ public final class Criteria {
474 474
475 @Override 475 @Override
476 public int hashCode() { 476 public int hashCode() {
477 - return Objects.hash(vlanId, type()); 477 + return Objects.hash(type(), vlanId);
478 } 478 }
479 479
480 @Override 480 @Override
...@@ -522,7 +522,7 @@ public final class Criteria { ...@@ -522,7 +522,7 @@ public final class Criteria {
522 522
523 @Override 523 @Override
524 public int hashCode() { 524 public int hashCode() {
525 - return Objects.hash(tcpPort, type); 525 + return Objects.hash(type, tcpPort);
526 } 526 }
527 527
528 @Override 528 @Override
...@@ -568,7 +568,7 @@ public final class Criteria { ...@@ -568,7 +568,7 @@ public final class Criteria {
568 568
569 @Override 569 @Override
570 public int hashCode() { 570 public int hashCode() {
571 - return Objects.hash(lambda, type); 571 + return Objects.hash(type, lambda);
572 } 572 }
573 573
574 @Override 574 @Override
...@@ -612,7 +612,7 @@ public final class Criteria { ...@@ -612,7 +612,7 @@ public final class Criteria {
612 612
613 @Override 613 @Override
614 public int hashCode() { 614 public int hashCode() {
615 - return Objects.hash(signalType, type); 615 + return Objects.hash(type, signalType);
616 } 616 }
617 617
618 @Override 618 @Override
......
...@@ -190,7 +190,7 @@ public final class Instructions { ...@@ -190,7 +190,7 @@ public final class Instructions {
190 190
191 @Override 191 @Override
192 public int hashCode() { 192 public int hashCode() {
193 - return Objects.hash(port, type()); 193 + return Objects.hash(type(), port);
194 } 194 }
195 195
196 @Override 196 @Override
......
...@@ -70,7 +70,7 @@ public abstract class L0ModificationInstruction implements Instruction { ...@@ -70,7 +70,7 @@ public abstract class L0ModificationInstruction implements Instruction {
70 70
71 @Override 71 @Override
72 public int hashCode() { 72 public int hashCode() {
73 - return Objects.hash(lambda, type(), subtype); 73 + return Objects.hash(type(), subtype, lambda);
74 } 74 }
75 75
76 @Override 76 @Override
......
...@@ -93,7 +93,7 @@ public abstract class L2ModificationInstruction implements Instruction { ...@@ -93,7 +93,7 @@ public abstract class L2ModificationInstruction implements Instruction {
93 93
94 @Override 94 @Override
95 public int hashCode() { 95 public int hashCode() {
96 - return Objects.hash(mac, type(), subtype); 96 + return Objects.hash(type(), subtype, mac);
97 } 97 }
98 98
99 @Override 99 @Override
...@@ -142,7 +142,7 @@ public abstract class L2ModificationInstruction implements Instruction { ...@@ -142,7 +142,7 @@ public abstract class L2ModificationInstruction implements Instruction {
142 142
143 @Override 143 @Override
144 public int hashCode() { 144 public int hashCode() {
145 - return Objects.hash(vlanId, type(), subtype()); 145 + return Objects.hash(type(), subtype(), vlanId);
146 } 146 }
147 147
148 @Override 148 @Override
...@@ -191,7 +191,7 @@ public abstract class L2ModificationInstruction implements Instruction { ...@@ -191,7 +191,7 @@ public abstract class L2ModificationInstruction implements Instruction {
191 191
192 @Override 192 @Override
193 public int hashCode() { 193 public int hashCode() {
194 - return Objects.hash(vlanPcp, type(), subtype()); 194 + return Objects.hash(type(), subtype(), vlanPcp);
195 } 195 }
196 196
197 @Override 197 @Override
......
...@@ -85,7 +85,7 @@ public abstract class L3ModificationInstruction implements Instruction { ...@@ -85,7 +85,7 @@ public abstract class L3ModificationInstruction implements Instruction {
85 85
86 @Override 86 @Override
87 public int hashCode() { 87 public int hashCode() {
88 - return Objects.hash(ip, type(), subtype()); 88 + return Objects.hash(type(), subtype(), ip);
89 } 89 }
90 90
91 @Override 91 @Override
......
...@@ -23,6 +23,7 @@ import org.onlab.onos.net.flow.TrafficSelector; ...@@ -23,6 +23,7 @@ import org.onlab.onos.net.flow.TrafficSelector;
23 import org.onlab.onos.net.flow.TrafficTreatment; 23 import org.onlab.onos.net.flow.TrafficTreatment;
24 24
25 import java.util.Collection; 25 import java.util.Collection;
26 +import java.util.Collections;
26 import java.util.List; 27 import java.util.List;
27 28
28 import static com.google.common.base.Preconditions.checkNotNull; 29 import static com.google.common.base.Preconditions.checkNotNull;
...@@ -61,7 +62,7 @@ public abstract class ConnectivityIntent extends Intent { ...@@ -61,7 +62,7 @@ public abstract class ConnectivityIntent extends Intent {
61 Collection<NetworkResource> resources, 62 Collection<NetworkResource> resources,
62 TrafficSelector selector, 63 TrafficSelector selector,
63 TrafficTreatment treatment) { 64 TrafficTreatment treatment) {
64 - this(id, appId, resources, selector, treatment, null); 65 + this(id, appId, resources, selector, treatment, Collections.emptyList());
65 } 66 }
66 67
67 /** 68 /**
...@@ -87,7 +88,7 @@ public abstract class ConnectivityIntent extends Intent { ...@@ -87,7 +88,7 @@ public abstract class ConnectivityIntent extends Intent {
87 super(id, appId, resources); 88 super(id, appId, resources);
88 this.selector = checkNotNull(selector); 89 this.selector = checkNotNull(selector);
89 this.treatment = checkNotNull(treatment); 90 this.treatment = checkNotNull(treatment);
90 - this.constraints = constraints; 91 + this.constraints = checkNotNull(constraints);
91 } 92 }
92 93
93 /** 94 /**
...@@ -97,7 +98,7 @@ public abstract class ConnectivityIntent extends Intent { ...@@ -97,7 +98,7 @@ public abstract class ConnectivityIntent extends Intent {
97 super(); 98 super();
98 this.selector = null; 99 this.selector = null;
99 this.treatment = null; 100 this.treatment = null;
100 - this.constraints = null; 101 + this.constraints = Collections.emptyList();
101 } 102 }
102 103
103 /** 104 /**
......
...@@ -21,6 +21,7 @@ import org.onlab.onos.net.HostId; ...@@ -21,6 +21,7 @@ import org.onlab.onos.net.HostId;
21 import org.onlab.onos.net.flow.TrafficSelector; 21 import org.onlab.onos.net.flow.TrafficSelector;
22 import org.onlab.onos.net.flow.TrafficTreatment; 22 import org.onlab.onos.net.flow.TrafficTreatment;
23 23
24 +import java.util.Collections;
24 import java.util.List; 25 import java.util.List;
25 26
26 import static com.google.common.base.Preconditions.checkNotNull; 27 import static com.google.common.base.Preconditions.checkNotNull;
...@@ -46,7 +47,7 @@ public final class HostToHostIntent extends ConnectivityIntent { ...@@ -46,7 +47,7 @@ public final class HostToHostIntent extends ConnectivityIntent {
46 public HostToHostIntent(ApplicationId appId, HostId one, HostId two, 47 public HostToHostIntent(ApplicationId appId, HostId one, HostId two,
47 TrafficSelector selector, 48 TrafficSelector selector,
48 TrafficTreatment treatment) { 49 TrafficTreatment treatment) {
49 - this(appId, one, two, selector, treatment, null); 50 + this(appId, one, two, selector, treatment, Collections.emptyList());
50 } 51 }
51 52
52 /** 53 /**
......
...@@ -22,6 +22,7 @@ import org.onlab.onos.net.Link; ...@@ -22,6 +22,7 @@ import org.onlab.onos.net.Link;
22 import org.onlab.onos.net.flow.TrafficSelector; 22 import org.onlab.onos.net.flow.TrafficSelector;
23 import org.onlab.onos.net.flow.TrafficTreatment; 23 import org.onlab.onos.net.flow.TrafficTreatment;
24 24
25 +import java.util.Collections;
25 import java.util.List; 26 import java.util.List;
26 import java.util.Set; 27 import java.util.Set;
27 28
...@@ -51,7 +52,7 @@ public final class LinkCollectionIntent extends ConnectivityIntent { ...@@ -51,7 +52,7 @@ public final class LinkCollectionIntent extends ConnectivityIntent {
51 TrafficTreatment treatment, 52 TrafficTreatment treatment,
52 Set<Link> links, 53 Set<Link> links,
53 ConnectPoint egressPoint) { 54 ConnectPoint egressPoint) {
54 - this(appId, selector , treatment, links, egressPoint, null); 55 + this(appId, selector , treatment, links, egressPoint, Collections.emptyList());
55 } 56 }
56 57
57 /** 58 /**
......
...@@ -22,6 +22,7 @@ import org.onlab.onos.net.ConnectPoint; ...@@ -22,6 +22,7 @@ import org.onlab.onos.net.ConnectPoint;
22 import org.onlab.onos.net.flow.TrafficSelector; 22 import org.onlab.onos.net.flow.TrafficSelector;
23 import org.onlab.onos.net.flow.TrafficTreatment; 23 import org.onlab.onos.net.flow.TrafficTreatment;
24 24
25 +import java.util.Collections;
25 import java.util.List; 26 import java.util.List;
26 import java.util.Set; 27 import java.util.Set;
27 28
...@@ -55,14 +56,7 @@ public final class MultiPointToSinglePointIntent extends ConnectivityIntent { ...@@ -55,14 +56,7 @@ public final class MultiPointToSinglePointIntent extends ConnectivityIntent {
55 TrafficTreatment treatment, 56 TrafficTreatment treatment,
56 Set<ConnectPoint> ingressPoints, 57 Set<ConnectPoint> ingressPoints,
57 ConnectPoint egressPoint) { 58 ConnectPoint egressPoint) {
58 - super(id(MultiPointToSinglePointIntent.class, selector, treatment, 59 + this(appId, selector, treatment, ingressPoints, egressPoint, Collections.emptyList());
59 - ingressPoints, egressPoint), appId, null, selector, treatment);
60 -
61 - checkNotNull(ingressPoints);
62 - checkArgument(!ingressPoints.isEmpty(), "Ingress point set cannot be empty");
63 -
64 - this.ingressPoints = Sets.newHashSet(ingressPoints);
65 - this.egressPoint = checkNotNull(egressPoint);
66 } 60 }
67 61
68 /** 62 /**
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
15 */ 15 */
16 package org.onlab.onos.net.intent; 16 package org.onlab.onos.net.intent;
17 17
18 +import java.util.Collections;
18 import java.util.List; 19 import java.util.List;
19 20
20 import com.google.common.base.MoreObjects; 21 import com.google.common.base.MoreObjects;
...@@ -42,9 +43,7 @@ public class PathIntent extends ConnectivityIntent { ...@@ -42,9 +43,7 @@ public class PathIntent extends ConnectivityIntent {
42 */ 43 */
43 public PathIntent(ApplicationId appId, TrafficSelector selector, 44 public PathIntent(ApplicationId appId, TrafficSelector selector,
44 TrafficTreatment treatment, Path path) { 45 TrafficTreatment treatment, Path path) {
45 - super(id(PathIntent.class, selector, treatment, path), appId, 46 + this(appId, selector, treatment, path, Collections.emptyList());
46 - resources(path.links()), selector, treatment);
47 - this.path = path;
48 } 47 }
49 48
50 /** 49 /**
......
...@@ -21,6 +21,8 @@ import org.onlab.onos.net.resource.LinkResourceService; ...@@ -21,6 +21,8 @@ import org.onlab.onos.net.resource.LinkResourceService;
21 21
22 import java.util.Objects; 22 import java.util.Objects;
23 23
24 +import static org.onlab.onos.net.AnnotationKeys.getAnnotatedValue;
25 +
24 /** 26 /**
25 * Constraint that evaluates an arbitrary link annotated value is under the specified threshold. 27 * Constraint that evaluates an arbitrary link annotated value is under the specified threshold.
26 */ 28 */
...@@ -41,6 +43,12 @@ public class AnnotationConstraint extends BooleanConstraint { ...@@ -41,6 +43,12 @@ public class AnnotationConstraint extends BooleanConstraint {
41 this.threshold = threshold; 43 this.threshold = threshold;
42 } 44 }
43 45
46 + // Constructor for serialization
47 + private AnnotationConstraint() {
48 + this.key = "";
49 + this.threshold = 0;
50 + }
51 +
44 /** 52 /**
45 * Returns the key of link annotation this constraint designates. 53 * Returns the key of link annotation this constraint designates.
46 * @return key of link annotation 54 * @return key of link annotation
...@@ -65,25 +73,6 @@ public class AnnotationConstraint extends BooleanConstraint { ...@@ -65,25 +73,6 @@ public class AnnotationConstraint extends BooleanConstraint {
65 return value <= threshold; 73 return value <= threshold;
66 } 74 }
67 75
68 - /**
69 - * Returns the annotated value of the specified link. The annotated value
70 - * is expected to be String that can be parsed as double. If parsing fails,
71 - * the returned value will be 1.0.
72 - *
73 - * @param link link whose annotated value is obtained
74 - * @param key key of link annotation
75 - * @return double value of link annotation for the specified key
76 - */
77 - private double getAnnotatedValue(Link link, String key) {
78 - double value;
79 - try {
80 - value = Double.parseDouble(link.annotations().value(key));
81 - } catch (NumberFormatException e) {
82 - value = 1.0;
83 - }
84 - return value;
85 - }
86 -
87 @Override 76 @Override
88 public double cost(Link link, LinkResourceService resourceService) { 77 public double cost(Link link, LinkResourceService resourceService) {
89 if (isValid(link, resourceService)) { 78 if (isValid(link, resourceService)) {
......
...@@ -25,14 +25,14 @@ import java.time.Duration; ...@@ -25,14 +25,14 @@ import java.time.Duration;
25 import java.time.temporal.ChronoUnit; 25 import java.time.temporal.ChronoUnit;
26 import java.util.Objects; 26 import java.util.Objects;
27 27
28 +import static org.onlab.onos.net.AnnotationKeys.LATENCY;
29 +import static org.onlab.onos.net.AnnotationKeys.getAnnotatedValue;
30 +
28 /** 31 /**
29 * Constraint that evaluates the latency through a path. 32 * Constraint that evaluates the latency through a path.
30 */ 33 */
31 public class LatencyConstraint implements Constraint { 34 public class LatencyConstraint implements Constraint {
32 35
33 - // TODO: formalize the key for latency all over the codes.
34 - private static final String LATENCY_KEY = "latency";
35 -
36 private final Duration latency; 36 private final Duration latency;
37 37
38 /** 38 /**
...@@ -43,22 +43,18 @@ public class LatencyConstraint implements Constraint { ...@@ -43,22 +43,18 @@ public class LatencyConstraint implements Constraint {
43 this.latency = latency; 43 this.latency = latency;
44 } 44 }
45 45
46 + // Constructor for serialization
47 + private LatencyConstraint() {
48 + this.latency = Duration.ZERO;
49 + }
50 +
46 public Duration latency() { 51 public Duration latency() {
47 return latency; 52 return latency;
48 } 53 }
49 54
50 @Override 55 @Override
51 public double cost(Link link, LinkResourceService resourceService) { 56 public double cost(Link link, LinkResourceService resourceService) {
52 - String value = link.annotations().value(LATENCY_KEY); 57 + return getAnnotatedValue(link, LATENCY);
53 -
54 - double latencyInMicroSec;
55 - try {
56 - latencyInMicroSec = Double.parseDouble(value);
57 - } catch (NumberFormatException e) {
58 - latencyInMicroSec = 1.0;
59 - }
60 -
61 - return latencyInMicroSec;
62 } 58 }
63 59
64 @Override 60 @Override
......
...@@ -21,6 +21,7 @@ import org.onlab.onos.net.DeviceId; ...@@ -21,6 +21,7 @@ import org.onlab.onos.net.DeviceId;
21 import org.onlab.onos.net.Link; 21 import org.onlab.onos.net.Link;
22 import org.onlab.onos.net.resource.LinkResourceService; 22 import org.onlab.onos.net.resource.LinkResourceService;
23 23
24 +import java.util.Collections;
24 import java.util.Objects; 25 import java.util.Objects;
25 import java.util.Set; 26 import java.util.Set;
26 27
...@@ -39,6 +40,11 @@ public class ObstacleConstraint extends BooleanConstraint { ...@@ -39,6 +40,11 @@ public class ObstacleConstraint extends BooleanConstraint {
39 this.obstacles = ImmutableSet.copyOf(obstacles); 40 this.obstacles = ImmutableSet.copyOf(obstacles);
40 } 41 }
41 42
43 + // Constructor for serialization
44 + private ObstacleConstraint() {
45 + this.obstacles = Collections.emptySet();
46 + }
47 +
42 @Override 48 @Override
43 public boolean isValid(Link link, LinkResourceService resourceService) { 49 public boolean isValid(Link link, LinkResourceService resourceService) {
44 DeviceId src = link.src().deviceId(); 50 DeviceId src = link.src().deviceId();
......
...@@ -17,12 +17,13 @@ package org.onlab.onos.net.intent.constraint; ...@@ -17,12 +17,13 @@ package org.onlab.onos.net.intent.constraint;
17 17
18 import com.google.common.base.MoreObjects; 18 import com.google.common.base.MoreObjects;
19 import com.google.common.collect.ImmutableList; 19 import com.google.common.collect.ImmutableList;
20 -import org.onlab.onos.net.ElementId; 20 +import org.onlab.onos.net.DeviceId;
21 import org.onlab.onos.net.Link; 21 import org.onlab.onos.net.Link;
22 import org.onlab.onos.net.Path; 22 import org.onlab.onos.net.Path;
23 import org.onlab.onos.net.intent.Constraint; 23 import org.onlab.onos.net.intent.Constraint;
24 import org.onlab.onos.net.resource.LinkResourceService; 24 import org.onlab.onos.net.resource.LinkResourceService;
25 25
26 +import java.util.Collections;
26 import java.util.LinkedList; 27 import java.util.LinkedList;
27 import java.util.List; 28 import java.util.List;
28 import java.util.Objects; 29 import java.util.Objects;
...@@ -35,20 +36,25 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -35,20 +36,25 @@ import static com.google.common.base.Preconditions.checkNotNull;
35 */ 36 */
36 public class WaypointConstraint implements Constraint { 37 public class WaypointConstraint implements Constraint {
37 38
38 - private final List<ElementId> waypoints; 39 + private final List<DeviceId> waypoints;
39 40
40 /** 41 /**
41 * Creates a new waypoint constraint. 42 * Creates a new waypoint constraint.
42 * 43 *
43 * @param waypoints waypoints 44 * @param waypoints waypoints
44 */ 45 */
45 - public WaypointConstraint(ElementId... waypoints) { 46 + public WaypointConstraint(DeviceId... waypoints) {
46 checkNotNull(waypoints, "waypoints cannot be null"); 47 checkNotNull(waypoints, "waypoints cannot be null");
47 checkArgument(waypoints.length > 0, "length of waypoints should be more than 0"); 48 checkArgument(waypoints.length > 0, "length of waypoints should be more than 0");
48 this.waypoints = ImmutableList.copyOf(waypoints); 49 this.waypoints = ImmutableList.copyOf(waypoints);
49 } 50 }
50 51
51 - public List<ElementId> waypoints() { 52 + // Constructor for serialization
53 + private WaypointConstraint() {
54 + this.waypoints = Collections.emptyList();
55 + }
56 +
57 + public List<DeviceId> waypoints() {
52 return waypoints; 58 return waypoints;
53 } 59 }
54 60
...@@ -60,8 +66,8 @@ public class WaypointConstraint implements Constraint { ...@@ -60,8 +66,8 @@ public class WaypointConstraint implements Constraint {
60 66
61 @Override 67 @Override
62 public boolean validate(Path path, LinkResourceService resourceService) { 68 public boolean validate(Path path, LinkResourceService resourceService) {
63 - LinkedList<ElementId> waypoints = new LinkedList<>(this.waypoints); 69 + LinkedList<DeviceId> waypoints = new LinkedList<>(this.waypoints);
64 - ElementId current = waypoints.poll(); 70 + DeviceId current = waypoints.poll();
65 // This is safe because Path class ensures the number of links are more than 0 71 // This is safe because Path class ensures the number of links are more than 0
66 Link firstLink = path.links().get(0); 72 Link firstLink = path.links().get(0);
67 if (firstLink.src().elementId().equals(current)) { 73 if (firstLink.src().elementId().equals(current)) {
......
...@@ -37,6 +37,7 @@ import static org.easymock.EasyMock.createMock; ...@@ -37,6 +37,7 @@ import static org.easymock.EasyMock.createMock;
37 import static org.hamcrest.Matchers.closeTo; 37 import static org.hamcrest.Matchers.closeTo;
38 import static org.hamcrest.Matchers.is; 38 import static org.hamcrest.Matchers.is;
39 import static org.junit.Assert.assertThat; 39 import static org.junit.Assert.assertThat;
40 +import static org.onlab.onos.net.AnnotationKeys.LATENCY;
40 import static org.onlab.onos.net.DefaultLinkTest.cp; 41 import static org.onlab.onos.net.DefaultLinkTest.cp;
41 import static org.onlab.onos.net.DeviceId.deviceId; 42 import static org.onlab.onos.net.DeviceId.deviceId;
42 import static org.onlab.onos.net.Link.Type.DIRECT; 43 import static org.onlab.onos.net.Link.Type.DIRECT;
...@@ -51,7 +52,6 @@ public class LatencyConstraintTest { ...@@ -51,7 +52,6 @@ public class LatencyConstraintTest {
51 private static final PortNumber PN3 = PortNumber.portNumber(3); 52 private static final PortNumber PN3 = PortNumber.portNumber(3);
52 private static final PortNumber PN4 = PortNumber.portNumber(4); 53 private static final PortNumber PN4 = PortNumber.portNumber(4);
53 private static final ProviderId PROVIDER_ID = new ProviderId("of", "foo"); 54 private static final ProviderId PROVIDER_ID = new ProviderId("of", "foo");
54 - private static final String LATENCY_KEY = "latency";
55 private static final String LATENCY1 = "3.0"; 55 private static final String LATENCY1 = "3.0";
56 private static final String LATENCY2 = "4.0"; 56 private static final String LATENCY2 = "4.0";
57 57
...@@ -66,8 +66,8 @@ public class LatencyConstraintTest { ...@@ -66,8 +66,8 @@ public class LatencyConstraintTest {
66 public void setUp() { 66 public void setUp() {
67 linkResourceService = createMock(LinkResourceService.class); 67 linkResourceService = createMock(LinkResourceService.class);
68 68
69 - Annotations annotations1 = DefaultAnnotations.builder().set(LATENCY_KEY, LATENCY1).build(); 69 + Annotations annotations1 = DefaultAnnotations.builder().set(LATENCY, LATENCY1).build();
70 - Annotations annotations2 = DefaultAnnotations.builder().set(LATENCY_KEY, LATENCY2).build(); 70 + Annotations annotations2 = DefaultAnnotations.builder().set(LATENCY, LATENCY2).build();
71 71
72 link1 = new DefaultLink(PROVIDER_ID, cp(DID1, PN1), cp(DID2, PN2), DIRECT, annotations1); 72 link1 = new DefaultLink(PROVIDER_ID, cp(DID1, PN1), cp(DID2, PN2), DIRECT, annotations1);
73 link2 = new DefaultLink(PROVIDER_ID, cp(DID2, PN3), cp(DID3, PN4), DIRECT, annotations2); 73 link2 = new DefaultLink(PROVIDER_ID, cp(DID2, PN3), cp(DID3, PN4), DIRECT, annotations2);
......
...@@ -18,9 +18,13 @@ package org.onlab.onos.net.device.impl; ...@@ -18,9 +18,13 @@ package org.onlab.onos.net.device.impl;
18 import static com.google.common.base.Preconditions.checkNotNull; 18 import static com.google.common.base.Preconditions.checkNotNull;
19 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_MASTERSHIP_CHANGED; 19 import static org.onlab.onos.net.device.DeviceEvent.Type.DEVICE_MASTERSHIP_CHANGED;
20 import static org.onlab.onos.net.MastershipRole.*; 20 import static org.onlab.onos.net.MastershipRole.*;
21 +import static org.onlab.util.Tools.namedThreads;
21 import static org.slf4j.LoggerFactory.getLogger; 22 import static org.slf4j.LoggerFactory.getLogger;
22 23
23 import java.util.List; 24 import java.util.List;
25 +import java.util.concurrent.Executors;
26 +import java.util.concurrent.ScheduledExecutorService;
27 +import java.util.concurrent.TimeUnit;
24 28
25 import org.apache.felix.scr.annotations.Activate; 29 import org.apache.felix.scr.annotations.Activate;
26 import org.apache.felix.scr.annotations.Component; 30 import org.apache.felix.scr.annotations.Component;
...@@ -83,6 +87,8 @@ public class DeviceManager ...@@ -83,6 +87,8 @@ public class DeviceManager
83 87
84 private final MastershipListener mastershipListener = new InternalMastershipListener(); 88 private final MastershipListener mastershipListener = new InternalMastershipListener();
85 89
90 + private ScheduledExecutorService backgroundService;
91 +
86 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 92 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
87 protected DeviceStore store; 93 protected DeviceStore store;
88 94
...@@ -102,15 +108,31 @@ public class DeviceManager ...@@ -102,15 +108,31 @@ public class DeviceManager
102 108
103 @Activate 109 @Activate
104 public void activate() { 110 public void activate() {
111 + backgroundService = Executors.newSingleThreadScheduledExecutor(namedThreads("device-manager-background"));
112 +
105 store.setDelegate(delegate); 113 store.setDelegate(delegate);
106 eventDispatcher.addSink(DeviceEvent.class, listenerRegistry); 114 eventDispatcher.addSink(DeviceEvent.class, listenerRegistry);
107 mastershipService.addListener(mastershipListener); 115 mastershipService.addListener(mastershipListener);
108 termService = mastershipService.requestTermService(); 116 termService = mastershipService.requestTermService();
117 +
118 + backgroundService.scheduleWithFixedDelay(new Runnable() {
119 +
120 + @Override
121 + public void run() {
122 + try {
123 + mastershipCheck();
124 + } catch (Exception e) {
125 + log.error("Exception thrown during integrity check", e);
126 + }
127 + }
128 + }, 1, 1, TimeUnit.MINUTES);
109 log.info("Started"); 129 log.info("Started");
110 } 130 }
111 131
112 @Deactivate 132 @Deactivate
113 public void deactivate() { 133 public void deactivate() {
134 + backgroundService.shutdown();
135 +
114 store.unsetDelegate(delegate); 136 store.unsetDelegate(delegate);
115 mastershipService.removeListener(mastershipListener); 137 mastershipService.removeListener(mastershipListener);
116 eventDispatcher.removeSink(DeviceEvent.class); 138 eventDispatcher.removeSink(DeviceEvent.class);
...@@ -172,10 +194,6 @@ public class DeviceManager ...@@ -172,10 +194,6 @@ public class DeviceManager
172 @Override 194 @Override
173 public void removeDevice(DeviceId deviceId) { 195 public void removeDevice(DeviceId deviceId) {
174 checkNotNull(deviceId, DEVICE_ID_NULL); 196 checkNotNull(deviceId, DEVICE_ID_NULL);
175 - // XXX is this intended to apply to the full global topology?
176 - // if so, we probably don't want the fact that we aren't
177 - // MASTER to get in the way, as it would do now.
178 - // FIXME: forward or broadcast and let the Master handler the event.
179 DeviceEvent event = store.removeDevice(deviceId); 197 DeviceEvent event = store.removeDevice(deviceId);
180 if (event != null) { 198 if (event != null) {
181 log.info("Device {} administratively removed", deviceId); 199 log.info("Device {} administratively removed", deviceId);
...@@ -199,6 +217,31 @@ public class DeviceManager ...@@ -199,6 +217,31 @@ public class DeviceManager
199 return new InternalDeviceProviderService(provider); 217 return new InternalDeviceProviderService(provider);
200 } 218 }
201 219
220 + /**
221 + * Checks if all the reachable devices have a valid mastership role.
222 + */
223 + private void mastershipCheck() {
224 + log.debug("Checking mastership");
225 + for (Device device : getDevices()) {
226 + final DeviceId deviceId = device.id();
227 + log.debug("Checking device {}", deviceId);
228 +
229 + if (!isReachable(deviceId)) {
230 + continue;
231 + }
232 +
233 + if (mastershipService.getLocalRole(deviceId) != NONE) {
234 + continue;
235 + }
236 +
237 + log.info("{} is reachable but did not have a valid role, reasserting", deviceId);
238 +
239 + // isReachable but was not MASTER or STANDBY, get a role and apply
240 + // Note: NONE triggers request to MastershipService
241 + reassertRole(deviceId, NONE);
242 + }
243 + }
244 +
202 // Personalized device provider service issued to the supplied provider. 245 // Personalized device provider service issued to the supplied provider.
203 private class InternalDeviceProviderService 246 private class InternalDeviceProviderService
204 extends AbstractProviderService<DeviceProvider> 247 extends AbstractProviderService<DeviceProvider>
...@@ -418,18 +461,15 @@ public class DeviceManager ...@@ -418,18 +461,15 @@ public class DeviceManager
418 } 461 }
419 } 462 }
420 463
421 - // Intercepts mastership events
422 - private class InternalMastershipListener implements MastershipListener {
423 -
424 // Applies the specified role to the device; ignores NONE 464 // Applies the specified role to the device; ignores NONE
425 /** 465 /**
426 - * Apply role in reaction to mastership event. 466 + * Apply role to device and send probe if MASTER.
427 * 467 *
428 * @param deviceId device identifier 468 * @param deviceId device identifier
429 * @param newRole new role to apply to the device 469 * @param newRole new role to apply to the device
430 * @return true if the request was sent to provider 470 * @return true if the request was sent to provider
431 */ 471 */
432 - private boolean applyRole(DeviceId deviceId, MastershipRole newRole) { 472 + private boolean applyRoleAndProbe(DeviceId deviceId, MastershipRole newRole) {
433 if (newRole.equals(MastershipRole.NONE)) { 473 if (newRole.equals(MastershipRole.NONE)) {
434 //no-op 474 //no-op
435 return true; 475 return true;
...@@ -460,6 +500,73 @@ public class DeviceManager ...@@ -460,6 +500,73 @@ public class DeviceManager
460 return true; 500 return true;
461 } 501 }
462 502
503 + /**
504 + * Reaasert role for specified device connected to this node.
505 + *
506 + * @param did device identifier
507 + * @param nextRole role to apply. If NONE is specified,
508 + * it will ask mastership service for a role and apply it.
509 + */
510 + private void reassertRole(final DeviceId did,
511 + final MastershipRole nextRole) {
512 +
513 + final NodeId myNodeId = clusterService.getLocalNode().id();
514 + MastershipRole myNextRole = nextRole;
515 + if (myNextRole == NONE) {
516 + mastershipService.requestRoleFor(did);
517 + MastershipTerm term = termService.getMastershipTerm(did);
518 + if (myNodeId.equals(term.master())) {
519 + myNextRole = MASTER;
520 + } else {
521 + myNextRole = STANDBY;
522 + }
523 + }
524 +
525 + switch (myNextRole) {
526 + case MASTER:
527 + final Device device = getDevice(did);
528 + if ((device != null) && !isAvailable(did)) {
529 + //flag the device as online. Is there a better way to do this?
530 + DefaultDeviceDescription deviceDescription
531 + = new DefaultDeviceDescription(did.uri(),
532 + device.type(),
533 + device.manufacturer(),
534 + device.hwVersion(),
535 + device.swVersion(),
536 + device.serialNumber(),
537 + device.chassisId());
538 + DeviceEvent devEvent =
539 + store.createOrUpdateDevice(device.providerId(), did,
540 + deviceDescription);
541 + post(devEvent);
542 + }
543 + // TODO: should apply role only if there is mismatch
544 + log.info("Applying role {} to {}", myNextRole, did);
545 + if (!applyRoleAndProbe(did, MASTER)) {
546 + // immediately failed to apply role
547 + mastershipService.relinquishMastership(did);
548 + // FIXME disconnect?
549 + }
550 + break;
551 + case STANDBY:
552 + log.info("Applying role {} to {}", myNextRole, did);
553 + if (!applyRoleAndProbe(did, STANDBY)) {
554 + // immediately failed to apply role
555 + mastershipService.relinquishMastership(did);
556 + // FIXME disconnect?
557 + }
558 + break;
559 + case NONE:
560 + default:
561 + // should never reach here
562 + log.error("You didn't see anything. I did not exist.");
563 + break;
564 + }
565 + }
566 +
567 + // Intercepts mastership events
568 + private class InternalMastershipListener implements MastershipListener {
569 +
463 @Override 570 @Override
464 public void event(MastershipEvent event) { 571 public void event(MastershipEvent event) {
465 572
...@@ -499,55 +606,12 @@ public class DeviceManager ...@@ -499,55 +606,12 @@ public class DeviceManager
499 + "Relinquishing role. ", 606 + "Relinquishing role. ",
500 myNextRole, did); 607 myNextRole, did);
501 mastershipService.relinquishMastership(did); 608 mastershipService.relinquishMastership(did);
502 - // FIXME disconnect?
503 } 609 }
504 return; 610 return;
505 } 611 }
506 612
507 // device is connected to this node: 613 // device is connected to this node:
508 - 614 + reassertRole(did, myNextRole);
509 - if (myNextRole == NONE) {
510 - mastershipService.requestRoleFor(did);
511 - MastershipTerm term = termService.getMastershipTerm(did);
512 - if (myNodeId.equals(term.master())) {
513 - myNextRole = MASTER;
514 - } else {
515 - myNextRole = STANDBY;
516 - }
517 - }
518 -
519 - switch (myNextRole) {
520 - case MASTER:
521 - final Device device = getDevice(did);
522 - if ((device != null) && !isAvailable(did)) {
523 - //flag the device as online. Is there a better way to do this?
524 - DefaultDeviceDescription deviceDescription
525 - = new DefaultDeviceDescription(did.uri(),
526 - device.type(),
527 - device.manufacturer(),
528 - device.hwVersion(),
529 - device.swVersion(),
530 - device.serialNumber(),
531 - device.chassisId());
532 - DeviceEvent devEvent =
533 - store.createOrUpdateDevice(device.providerId(), did,
534 - deviceDescription);
535 - post(devEvent);
536 - }
537 - // TODO: should apply role only if there is mismatch
538 - log.info("Applying role {} to {}", myNextRole, did);
539 - applyRole(did, MASTER);
540 - break;
541 - case STANDBY:
542 - log.info("Applying role {} to {}", myNextRole, did);
543 - applyRole(did, STANDBY);
544 - break;
545 - case NONE:
546 - default:
547 - // should never reach here
548 - log.error("You didn't see anything. I did not exist.");
549 - break;
550 - }
551 } 615 }
552 } 616 }
553 617
......
...@@ -15,21 +15,12 @@ ...@@ -15,21 +15,12 @@
15 */ 15 */
16 package org.onlab.onos.net.flow.impl; 16 package org.onlab.onos.net.flow.impl;
17 17
18 -import static com.google.common.base.Preconditions.checkNotNull; 18 +import com.google.common.collect.ArrayListMultimap;
19 -import static org.slf4j.LoggerFactory.getLogger; 19 +import com.google.common.collect.Iterables;
20 -import static org.onlab.util.Tools.namedThreads; 20 +import com.google.common.collect.Lists;
21 - 21 +import com.google.common.collect.Maps;
22 -import java.util.List; 22 +import com.google.common.collect.Multimap;
23 -import java.util.Map; 23 +import com.google.common.collect.Sets;
24 -import java.util.Set;
25 -import java.util.concurrent.CancellationException;
26 -import java.util.concurrent.ExecutionException;
27 -import java.util.concurrent.ExecutorService;
28 -import java.util.concurrent.Executors;
29 -import java.util.concurrent.Future;
30 -import java.util.concurrent.TimeUnit;
31 -import java.util.concurrent.TimeoutException;
32 -import java.util.concurrent.atomic.AtomicReference;
33 24
34 import org.apache.felix.scr.annotations.Activate; 25 import org.apache.felix.scr.annotations.Activate;
35 import org.apache.felix.scr.annotations.Component; 26 import org.apache.felix.scr.annotations.Component;
...@@ -64,14 +55,22 @@ import org.onlab.onos.net.provider.AbstractProviderRegistry; ...@@ -64,14 +55,22 @@ import org.onlab.onos.net.provider.AbstractProviderRegistry;
64 import org.onlab.onos.net.provider.AbstractProviderService; 55 import org.onlab.onos.net.provider.AbstractProviderService;
65 import org.slf4j.Logger; 56 import org.slf4j.Logger;
66 57
67 -import com.google.common.collect.ArrayListMultimap; 58 +import java.util.HashSet;
68 -import com.google.common.collect.Iterables; 59 +import java.util.List;
69 -import com.google.common.collect.Lists; 60 +import java.util.Map;
70 -import com.google.common.collect.Maps; 61 +import java.util.Set;
71 -import com.google.common.collect.Multimap; 62 +import java.util.concurrent.CancellationException;
72 -import com.google.common.collect.Sets; 63 +import java.util.concurrent.ExecutionException;
73 -import com.google.common.util.concurrent.Futures; 64 +import java.util.concurrent.ExecutorService;
74 -import com.google.common.util.concurrent.ListenableFuture; 65 +import java.util.concurrent.Executors;
66 +import java.util.concurrent.Future;
67 +import java.util.concurrent.TimeUnit;
68 +import java.util.concurrent.TimeoutException;
69 +import java.util.concurrent.atomic.AtomicReference;
70 +
71 +import static com.google.common.base.Preconditions.checkNotNull;
72 +import static org.onlab.util.Tools.namedThreads;
73 +import static org.slf4j.LoggerFactory.getLogger;
75 74
76 /** 75 /**
77 * Provides implementation of the flow NB &amp; SB APIs. 76 * Provides implementation of the flow NB &amp; SB APIs.
...@@ -92,8 +91,7 @@ public class FlowRuleManager ...@@ -92,8 +91,7 @@ public class FlowRuleManager
92 91
93 private final FlowRuleStoreDelegate delegate = new InternalStoreDelegate(); 92 private final FlowRuleStoreDelegate delegate = new InternalStoreDelegate();
94 93
95 - private final ExecutorService futureListeners = 94 + private ExecutorService futureService;
96 - Executors.newCachedThreadPool(namedThreads("provider-future-listeners"));
97 95
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected FlowRuleStore store; 97 protected FlowRuleStore store;
...@@ -106,6 +104,7 @@ public class FlowRuleManager ...@@ -106,6 +104,7 @@ public class FlowRuleManager
106 104
107 @Activate 105 @Activate
108 public void activate() { 106 public void activate() {
107 + futureService = Executors.newCachedThreadPool(namedThreads("provider-future-listeners"));
109 store.setDelegate(delegate); 108 store.setDelegate(delegate);
110 eventDispatcher.addSink(FlowRuleEvent.class, listenerRegistry); 109 eventDispatcher.addSink(FlowRuleEvent.class, listenerRegistry);
111 log.info("Started"); 110 log.info("Started");
...@@ -113,7 +112,7 @@ public class FlowRuleManager ...@@ -113,7 +112,7 @@ public class FlowRuleManager
113 112
114 @Deactivate 113 @Deactivate
115 public void deactivate() { 114 public void deactivate() {
116 - futureListeners.shutdownNow(); 115 + futureService.shutdownNow();
117 116
118 store.unsetDelegate(delegate); 117 store.unsetDelegate(delegate);
119 eventDispatcher.removeSink(FlowRuleEvent.class); 118 eventDispatcher.removeSink(FlowRuleEvent.class);
...@@ -364,6 +363,9 @@ public class FlowRuleManager ...@@ -364,6 +363,9 @@ public class FlowRuleManager
364 363
365 // Store delegate to re-post events emitted from the store. 364 // Store delegate to re-post events emitted from the store.
366 private class InternalStoreDelegate implements FlowRuleStoreDelegate { 365 private class InternalStoreDelegate implements FlowRuleStoreDelegate {
366 +
367 + private static final int TIMEOUT = 5000; // ms
368 +
367 // TODO: Right now we only dispatch events at individual flowEntry level. 369 // TODO: Right now we only dispatch events at individual flowEntry level.
368 // It may be more efficient for also dispatch events as a batch. 370 // It may be more efficient for also dispatch events as a batch.
369 @Override 371 @Override
...@@ -384,15 +386,28 @@ public class FlowRuleManager ...@@ -384,15 +386,28 @@ public class FlowRuleManager
384 386
385 FlowRuleProvider flowRuleProvider = 387 FlowRuleProvider flowRuleProvider =
386 getProvider(batchOperation.getOperations().get(0).getTarget().deviceId()); 388 getProvider(batchOperation.getOperations().get(0).getTarget().deviceId());
387 - final ListenableFuture<CompletedBatchOperation> result = 389 + final Future<CompletedBatchOperation> result =
388 flowRuleProvider.executeBatch(batchOperation); 390 flowRuleProvider.executeBatch(batchOperation);
389 - result.addListener(new Runnable() { 391 + futureService.submit(new Runnable() {
390 @Override 392 @Override
391 public void run() { 393 public void run() {
392 - store.batchOperationComplete(FlowRuleBatchEvent.completed(request, 394 + CompletedBatchOperation res;
393 - Futures.getUnchecked(result))); 395 + try {
396 + res = result.get(TIMEOUT, TimeUnit.MILLISECONDS);
397 + store.batchOperationComplete(FlowRuleBatchEvent.completed(request, res));
398 + } catch (TimeoutException | InterruptedException | ExecutionException e) {
399 + log.warn("Something went wrong with the batch operation {}",
400 + request.batchId(), e);
401 +
402 + Set<FlowRule> failures = new HashSet<>(batchOperation.size());
403 + for (FlowRuleBatchEntry op : batchOperation.getOperations()) {
404 + failures.add(op.getTarget());
405 + }
406 + res = new CompletedBatchOperation(false, failures);
407 + store.batchOperationComplete(FlowRuleBatchEvent.completed(request, res));
408 + }
394 } 409 }
395 - }, futureListeners); 410 + });
396 break; 411 break;
397 412
398 case BATCH_OPERATION_COMPLETED: 413 case BATCH_OPERATION_COMPLETED:
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
15 */ 15 */
16 package org.onlab.onos.net.intent.impl; 16 package org.onlab.onos.net.intent.impl;
17 17
18 +import com.google.common.base.Predicate;
19 +import com.google.common.collect.FluentIterable;
18 import com.google.common.collect.ImmutableList; 20 import com.google.common.collect.ImmutableList;
19 import org.apache.felix.scr.annotations.Component; 21 import org.apache.felix.scr.annotations.Component;
20 import org.apache.felix.scr.annotations.Reference; 22 import org.apache.felix.scr.annotations.Reference;
...@@ -94,11 +96,19 @@ public abstract class ConnectivityIntentCompiler<T extends ConnectivityIntent> ...@@ -94,11 +96,19 @@ public abstract class ConnectivityIntentCompiler<T extends ConnectivityIntent>
94 protected Path getPath(ConnectivityIntent intent, 96 protected Path getPath(ConnectivityIntent intent,
95 ElementId one, ElementId two) { 97 ElementId one, ElementId two) {
96 Set<Path> paths = pathService.getPaths(one, two, weight(intent.constraints())); 98 Set<Path> paths = pathService.getPaths(one, two, weight(intent.constraints()));
97 - if (paths.isEmpty()) { 99 + final List<Constraint> constraints = intent.constraints();
98 - throw new PathNotFoundException("No packet path from " + one + " to " + two); 100 + ImmutableList<Path> filtered = FluentIterable.from(paths)
101 + .filter(new Predicate<Path>() {
102 + @Override
103 + public boolean apply(Path path) {
104 + return checkPath(path, constraints);
105 + }
106 + }).toList();
107 + if (filtered.isEmpty()) {
108 + throw new PathNotFoundException("No packet path form " + one + " to " + two);
99 } 109 }
100 // TODO: let's be more intelligent about this eventually 110 // TODO: let's be more intelligent about this eventually
101 - return paths.iterator().next(); 111 + return filtered.iterator().next();
102 } 112 }
103 113
104 /** 114 /**
......
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.net.flow;
17 +
18 +import java.util.concurrent.TimeUnit;
19 +
20 +import org.junit.Test;
21 +import org.onlab.onos.net.intent.IntentTestsMocks;
22 +
23 +import com.google.common.testing.EqualsTester;
24 +
25 +import static org.hamcrest.MatcherAssert.assertThat;
26 +import static org.hamcrest.Matchers.greaterThan;
27 +import static org.hamcrest.Matchers.is;
28 +import static org.onlab.onos.net.NetTestTools.did;
29 +
30 +/**
31 + * Unit tests for the DefaultFlowEntry class.
32 + */
33 +public class DefaultFlowEntryTest {
34 + private static final IntentTestsMocks.MockSelector SELECTOR =
35 + new IntentTestsMocks.MockSelector();
36 + private static final IntentTestsMocks.MockTreatment TREATMENT =
37 + new IntentTestsMocks.MockTreatment();
38 +
39 + private static DefaultFlowEntry makeFlowEntry(int uniqueValue) {
40 + return new DefaultFlowEntry(did("id" + Integer.toString(uniqueValue)),
41 + SELECTOR,
42 + TREATMENT,
43 + uniqueValue,
44 + FlowEntry.FlowEntryState.ADDED,
45 + uniqueValue,
46 + uniqueValue,
47 + uniqueValue,
48 + uniqueValue,
49 + uniqueValue);
50 + }
51 +
52 + final DefaultFlowEntry defaultFlowEntry1 = makeFlowEntry(1);
53 + final DefaultFlowEntry sameAsDefaultFlowEntry1 = makeFlowEntry(1);
54 + final DefaultFlowEntry defaultFlowEntry2 = makeFlowEntry(2);
55 +
56 + /**
57 + * Tests the equals, hashCode and toString methods using Guava EqualsTester.
58 + */
59 + @Test
60 + public void testEquals() {
61 + new EqualsTester()
62 + .addEqualityGroup(defaultFlowEntry1, sameAsDefaultFlowEntry1)
63 + .addEqualityGroup(defaultFlowEntry2)
64 + .testEquals();
65 + }
66 +
67 + /**
68 + * Tests the construction of a default flow entry from a device id.
69 + */
70 + @Test
71 + public void testDeviceBasedObject() {
72 + assertThat(defaultFlowEntry1.deviceId(), is(did("id1")));
73 + assertThat(defaultFlowEntry1.selector(), is(SELECTOR));
74 + assertThat(defaultFlowEntry1.treatment(), is(TREATMENT));
75 + assertThat(defaultFlowEntry1.timeout(), is(1));
76 + assertThat(defaultFlowEntry1.life(), is(1L));
77 + assertThat(defaultFlowEntry1.packets(), is(1L));
78 + assertThat(defaultFlowEntry1.bytes(), is(1L));
79 + assertThat(defaultFlowEntry1.state(), is(FlowEntry.FlowEntryState.ADDED));
80 + assertThat(defaultFlowEntry1.lastSeen(),
81 + greaterThan(System.currentTimeMillis() -
82 + TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS)));
83 + }
84 +
85 + /**
86 + * Tests the setters on a default flow entry object.
87 + */
88 + @Test
89 + public void testSetters() {
90 + final DefaultFlowEntry entry = makeFlowEntry(1);
91 +
92 + entry.setLastSeen();
93 + entry.setState(FlowEntry.FlowEntryState.PENDING_REMOVE);
94 + entry.setPackets(11);
95 + entry.setBytes(22);
96 + entry.setLife(33);
97 +
98 + assertThat(entry.deviceId(), is(did("id1")));
99 + assertThat(entry.selector(), is(SELECTOR));
100 + assertThat(entry.treatment(), is(TREATMENT));
101 + assertThat(entry.timeout(), is(1));
102 + assertThat(entry.life(), is(33L));
103 + assertThat(entry.packets(), is(11L));
104 + assertThat(entry.bytes(), is(22L));
105 + assertThat(entry.state(), is(FlowEntry.FlowEntryState.PENDING_REMOVE));
106 + assertThat(entry.lastSeen(),
107 + greaterThan(System.currentTimeMillis() -
108 + TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS)));
109 + }
110 +
111 + /**
112 + * Tests a default flow rule built for an error.
113 + */
114 + @Test
115 + public void testErrorObject() {
116 + final DefaultFlowEntry errorEntry =
117 + new DefaultFlowEntry(new IntentTestsMocks.MockFlowRule(1),
118 + 111,
119 + 222);
120 + assertThat(errorEntry.errType(), is(111));
121 + assertThat(errorEntry.errCode(), is(222));
122 + assertThat(errorEntry.state(), is(FlowEntry.FlowEntryState.FAILED));
123 + assertThat(errorEntry.lastSeen(),
124 + greaterThan(System.currentTimeMillis() -
125 + TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS)));
126 + }
127 +
128 + /**
129 + * Tests a default flow entry constructed from a flow rule.
130 + */
131 + @Test
132 + public void testFlowBasedObject() {
133 + final DefaultFlowEntry entry =
134 + new DefaultFlowEntry(new IntentTestsMocks.MockFlowRule(1));
135 + assertThat(entry.priority(), is(1));
136 + assertThat(entry.appId(), is((short) 0));
137 + assertThat(entry.lastSeen(),
138 + greaterThan(System.currentTimeMillis() -
139 + TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS)));
140 + }
141 +
142 + /**
143 + * Tests a default flow entry constructed from a flow rule plus extra
144 + * parameters.
145 + */
146 + @Test
147 + public void testFlowBasedObjectWithParameters() {
148 + final DefaultFlowEntry entry =
149 + new DefaultFlowEntry(new IntentTestsMocks.MockFlowRule(33),
150 + FlowEntry.FlowEntryState.REMOVED,
151 + 101, 102, 103);
152 + assertThat(entry.state(), is(FlowEntry.FlowEntryState.REMOVED));
153 + assertThat(entry.life(), is(101L));
154 + assertThat(entry.packets(), is(102L));
155 + assertThat(entry.bytes(), is(103L));
156 + assertThat(entry.lastSeen(),
157 + greaterThan(System.currentTimeMillis() -
158 + TimeUnit.MILLISECONDS.convert(1, TimeUnit.SECONDS)));
159 + }
160 +}
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 +
17 +package org.onlab.onos.net.flow;
18 +
19 +import org.junit.Test;
20 +import org.onlab.onos.net.intent.IntentTestsMocks;
21 +
22 +import com.google.common.testing.EqualsTester;
23 +
24 +import static org.hamcrest.MatcherAssert.assertThat;
25 +import static org.hamcrest.Matchers.is;
26 +import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutableBaseClass;
27 +import static org.onlab.onos.net.NetTestTools.APP_ID;
28 +import static org.onlab.onos.net.NetTestTools.did;
29 +
30 +/**
31 + * Unit tests for the default flow rule class.
32 + */
33 +public class DefaultFlowRuleTest {
34 + private static final IntentTestsMocks.MockSelector SELECTOR =
35 + new IntentTestsMocks.MockSelector();
36 + private static final IntentTestsMocks.MockTreatment TREATMENT =
37 + new IntentTestsMocks.MockTreatment();
38 +
39 + final FlowRule flowRule1 = new IntentTestsMocks.MockFlowRule(1);
40 + final FlowRule sameAsFlowRule1 = new IntentTestsMocks.MockFlowRule(1);
41 + final FlowRule flowRule2 = new IntentTestsMocks.MockFlowRule(2);
42 + final DefaultFlowRule defaultFlowRule1 = new DefaultFlowRule(flowRule1);
43 + final DefaultFlowRule sameAsDefaultFlowRule1 = new DefaultFlowRule(sameAsFlowRule1);
44 + final DefaultFlowRule defaultFlowRule2 = new DefaultFlowRule(flowRule2);
45 +
46 + /**
47 + * Checks that the DefaultFlowRule class is immutable but can be inherited
48 + * from.
49 + */
50 + @Test
51 + public void testImmutability() {
52 + assertThatClassIsImmutableBaseClass(DefaultFlowRule.class);
53 + }
54 +
55 + /**
56 + * Tests the equals, hashCode and toString methods using Guava EqualsTester.
57 + */
58 + @Test
59 + public void testEquals() {
60 + new EqualsTester()
61 + .addEqualityGroup(defaultFlowRule1, sameAsDefaultFlowRule1)
62 + .addEqualityGroup(defaultFlowRule2)
63 + .testEquals();
64 + }
65 +
66 + /**
67 + * Tests creation of a DefaultFlowRule using a FlowRule constructor.
68 + */
69 + @Test
70 + public void testCreationFromFlowRule() {
71 + assertThat(defaultFlowRule1.deviceId(), is(flowRule1.deviceId()));
72 + assertThat(defaultFlowRule1.appId(), is(flowRule1.appId()));
73 + assertThat(defaultFlowRule1.id(), is(flowRule1.id()));
74 + assertThat(defaultFlowRule1.isPermanent(), is(flowRule1.isPermanent()));
75 + assertThat(defaultFlowRule1.priority(), is(flowRule1.priority()));
76 + assertThat(defaultFlowRule1.selector(), is(flowRule1.selector()));
77 + assertThat(defaultFlowRule1.treatment(), is(flowRule1.treatment()));
78 + assertThat(defaultFlowRule1.timeout(), is(flowRule1.timeout()));
79 + }
80 +
81 + /**
82 + * Tests creation of a DefaultFlowRule using a FlowId constructor.
83 + */
84 + @Test
85 + public void testCreationWithFlowId() {
86 + final DefaultFlowRule rule =
87 + new DefaultFlowRule(did("1"), SELECTOR,
88 + TREATMENT, 22, 33,
89 + 44, false);
90 + assertThat(rule.deviceId(), is(did("1")));
91 + assertThat(rule.id().value(), is(33L));
92 + assertThat(rule.isPermanent(), is(false));
93 + assertThat(rule.priority(), is(22));
94 + assertThat(rule.selector(), is(SELECTOR));
95 + assertThat(rule.treatment(), is(TREATMENT));
96 + assertThat(rule.timeout(), is(44));
97 + }
98 +
99 + /**
100 + * Tests the creation of a DefaultFlowRule using an AppId constructor.
101 + */
102 + @Test
103 + public void testCreationWithAppId() {
104 + final DefaultFlowRule rule =
105 + new DefaultFlowRule(did("1"), SELECTOR,
106 + TREATMENT, 22, APP_ID,
107 + 44, false);
108 + assertThat(rule.deviceId(), is(did("1")));
109 + assertThat(rule.isPermanent(), is(false));
110 + assertThat(rule.priority(), is(22));
111 + assertThat(rule.selector(), is(SELECTOR));
112 + assertThat(rule.treatment(), is(TREATMENT));
113 + assertThat(rule.timeout(), is(44));
114 + }
115 +}
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
16 package org.onlab.onos.net.intent; 16 package org.onlab.onos.net.intent;
17 17
18 import static org.onlab.onos.net.NetTestTools.createPath; 18 import static org.onlab.onos.net.NetTestTools.createPath;
19 +import static org.onlab.onos.net.NetTestTools.did;
19 import static org.onlab.onos.net.NetTestTools.link; 20 import static org.onlab.onos.net.NetTestTools.link;
20 21
21 import java.util.ArrayList; 22 import java.util.ArrayList;
...@@ -31,6 +32,8 @@ import org.onlab.onos.net.DeviceId; ...@@ -31,6 +32,8 @@ import org.onlab.onos.net.DeviceId;
31 import org.onlab.onos.net.ElementId; 32 import org.onlab.onos.net.ElementId;
32 import org.onlab.onos.net.Link; 33 import org.onlab.onos.net.Link;
33 import org.onlab.onos.net.Path; 34 import org.onlab.onos.net.Path;
35 +import org.onlab.onos.net.flow.FlowId;
36 +import org.onlab.onos.net.flow.FlowRule;
34 import org.onlab.onos.net.flow.TrafficSelector; 37 import org.onlab.onos.net.flow.TrafficSelector;
35 import org.onlab.onos.net.flow.TrafficTreatment; 38 import org.onlab.onos.net.flow.TrafficTreatment;
36 import org.onlab.onos.net.flow.criteria.Criterion; 39 import org.onlab.onos.net.flow.criteria.Criterion;
...@@ -271,4 +274,60 @@ public class IntentTestsMocks { ...@@ -271,4 +274,60 @@ public class IntentTestsMocks {
271 } 274 }
272 } 275 }
273 276
277 + private static final IntentTestsMocks.MockSelector SELECTOR =
278 + new IntentTestsMocks.MockSelector();
279 + private static final IntentTestsMocks.MockTreatment TREATMENT =
280 + new IntentTestsMocks.MockTreatment();
281 +
282 + public static class MockFlowRule implements FlowRule {
283 +
284 + int priority;
285 + public MockFlowRule(int priority) {
286 + this.priority = priority;
287 + }
288 +
289 + @Override
290 + public FlowId id() {
291 + return FlowId.valueOf(1);
292 + }
293 +
294 + @Override
295 + public short appId() {
296 + return 0;
297 + }
298 +
299 + @Override
300 + public int priority() {
301 + return priority;
302 + }
303 +
304 + @Override
305 + public DeviceId deviceId() {
306 + return did("1");
307 + }
308 +
309 + @Override
310 + public TrafficSelector selector() {
311 + return SELECTOR;
312 + }
313 +
314 + @Override
315 + public TrafficTreatment treatment() {
316 + return TREATMENT;
317 + }
318 +
319 + @Override
320 + public int timeout() {
321 + return 0;
322 + }
323 +
324 + @Override
325 + public boolean isPermanent() {
326 + return false;
327 + }
328 +
329 +
330 + }
331 +
332 +
274 } 333 }
......
...@@ -85,6 +85,8 @@ import com.google.common.cache.Cache; ...@@ -85,6 +85,8 @@ import com.google.common.cache.Cache;
85 import com.google.common.cache.CacheBuilder; 85 import com.google.common.cache.CacheBuilder;
86 import com.google.common.cache.CacheLoader; 86 import com.google.common.cache.CacheLoader;
87 import com.google.common.cache.LoadingCache; 87 import com.google.common.cache.LoadingCache;
88 +import com.google.common.cache.RemovalListener;
89 +import com.google.common.cache.RemovalNotification;
88 import com.google.common.collect.ArrayListMultimap; 90 import com.google.common.collect.ArrayListMultimap;
89 import com.google.common.collect.ImmutableList; 91 import com.google.common.collect.ImmutableList;
90 import com.google.common.collect.ImmutableSet; 92 import com.google.common.collect.ImmutableSet;
...@@ -132,8 +134,7 @@ public class DistributedFlowRuleStore ...@@ -132,8 +134,7 @@ public class DistributedFlowRuleStore
132 private Cache<Integer, SettableFuture<CompletedBatchOperation>> pendingFutures = 134 private Cache<Integer, SettableFuture<CompletedBatchOperation>> pendingFutures =
133 CacheBuilder.newBuilder() 135 CacheBuilder.newBuilder()
134 .expireAfterWrite(pendingFutureTimeoutMinutes, TimeUnit.MINUTES) 136 .expireAfterWrite(pendingFutureTimeoutMinutes, TimeUnit.MINUTES)
135 - // TODO Explicitly fail the future if expired? 137 + .removalListener(new TimeoutFuture())
136 - //.removalListener(listener)
137 .build(); 138 .build();
138 139
139 // Cache of SMaps used for backup data. each SMap contain device flow table 140 // Cache of SMaps used for backup data. each SMap contain device flow table
...@@ -541,6 +542,17 @@ public class DistributedFlowRuleStore ...@@ -541,6 +542,17 @@ public class DistributedFlowRuleStore
541 log.debug("removedFromPrimary {}", removed); 542 log.debug("removedFromPrimary {}", removed);
542 } 543 }
543 544
545 + private static final class TimeoutFuture
546 + implements RemovalListener<Integer, SettableFuture<CompletedBatchOperation>> {
547 + @Override
548 + public void onRemoval(RemovalNotification<Integer, SettableFuture<CompletedBatchOperation>> notification) {
549 + // wrapping in ExecutionException to support Future.get
550 + notification.getValue()
551 + .setException(new ExecutionException("Timed out",
552 + new TimeoutException()));
553 + }
554 + }
555 +
544 private final class OnStoreBatch implements ClusterMessageHandler { 556 private final class OnStoreBatch implements ClusterMessageHandler {
545 private final NodeId local; 557 private final NodeId local;
546 558
...@@ -580,7 +592,18 @@ public class DistributedFlowRuleStore ...@@ -580,7 +592,18 @@ public class DistributedFlowRuleStore
580 592
581 @Override 593 @Override
582 public void run() { 594 public void run() {
583 - CompletedBatchOperation result = Futures.getUnchecked(f); 595 + CompletedBatchOperation result;
596 + try {
597 + result = f.get();
598 + } catch (InterruptedException | ExecutionException e) {
599 + log.error("Batch operation failed", e);
600 + // create everything failed response
601 + Set<FlowRule> failures = new HashSet<>(operation.size());
602 + for (FlowRuleBatchEntry op : operation.getOperations()) {
603 + failures.add(op.getTarget());
604 + }
605 + result = new CompletedBatchOperation(false, failures);
606 + }
584 try { 607 try {
585 message.respond(SERIALIZER.encode(result)); 608 message.respond(SERIALIZER.encode(result));
586 } catch (IOException e) { 609 } catch (IOException e) {
......
...@@ -72,7 +72,7 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService { ...@@ -72,7 +72,7 @@ public class DatabaseManager implements DatabaseService, DatabaseAdminService {
72 public static final String LOG_FILE_PREFIX = "/tmp/onos-copy-cat-log_"; 72 public static final String LOG_FILE_PREFIX = "/tmp/onos-copy-cat-log_";
73 73
74 // Current working dir seems to be /opt/onos/apache-karaf-3.0.2 74 // Current working dir seems to be /opt/onos/apache-karaf-3.0.2
75 - // TODO: Get the path to /opt/onos/config 75 + // TODO: Set the path to /opt/onos/config
76 private static final String CONFIG_DIR = "../config"; 76 private static final String CONFIG_DIR = "../config";
77 77
78 private static final String DEFAULT_MEMBER_FILE = "tablets.json"; 78 private static final String DEFAULT_MEMBER_FILE = "tablets.json";
......
...@@ -75,10 +75,14 @@ import org.onlab.onos.net.intent.OpticalConnectivityIntent; ...@@ -75,10 +75,14 @@ import org.onlab.onos.net.intent.OpticalConnectivityIntent;
75 import org.onlab.onos.net.intent.OpticalPathIntent; 75 import org.onlab.onos.net.intent.OpticalPathIntent;
76 import org.onlab.onos.net.intent.PathIntent; 76 import org.onlab.onos.net.intent.PathIntent;
77 import org.onlab.onos.net.intent.PointToPointIntent; 77 import org.onlab.onos.net.intent.PointToPointIntent;
78 +import org.onlab.onos.net.intent.constraint.AnnotationConstraint;
78 import org.onlab.onos.net.intent.constraint.BandwidthConstraint; 79 import org.onlab.onos.net.intent.constraint.BandwidthConstraint;
79 import org.onlab.onos.net.intent.constraint.BooleanConstraint; 80 import org.onlab.onos.net.intent.constraint.BooleanConstraint;
80 import org.onlab.onos.net.intent.constraint.LambdaConstraint; 81 import org.onlab.onos.net.intent.constraint.LambdaConstraint;
82 +import org.onlab.onos.net.intent.constraint.LatencyConstraint;
81 import org.onlab.onos.net.intent.constraint.LinkTypeConstraint; 83 import org.onlab.onos.net.intent.constraint.LinkTypeConstraint;
84 +import org.onlab.onos.net.intent.constraint.ObstacleConstraint;
85 +import org.onlab.onos.net.intent.constraint.WaypointConstraint;
82 import org.onlab.onos.net.link.DefaultLinkDescription; 86 import org.onlab.onos.net.link.DefaultLinkDescription;
83 import org.onlab.onos.net.packet.DefaultOutboundPacket; 87 import org.onlab.onos.net.packet.DefaultOutboundPacket;
84 import org.onlab.onos.net.provider.ProviderId; 88 import org.onlab.onos.net.provider.ProviderId;
...@@ -208,9 +212,14 @@ public final class KryoNamespaces { ...@@ -208,9 +212,14 @@ public final class KryoNamespaces {
208 LinkResourceRequest.class, 212 LinkResourceRequest.class,
209 Lambda.class, 213 Lambda.class,
210 Bandwidth.class, 214 Bandwidth.class,
215 + // Constraints
211 LambdaConstraint.class, 216 LambdaConstraint.class,
212 BandwidthConstraint.class, 217 BandwidthConstraint.class,
213 LinkTypeConstraint.class, 218 LinkTypeConstraint.class,
219 + LatencyConstraint.class,
220 + WaypointConstraint.class,
221 + ObstacleConstraint.class,
222 + AnnotationConstraint.class,
214 BooleanConstraint.class 223 BooleanConstraint.class
215 ) 224 )
216 .register(DefaultApplicationId.class, new DefaultApplicationIdSerializer()) 225 .register(DefaultApplicationId.class, new DefaultApplicationIdSerializer())
......
...@@ -18,6 +18,8 @@ package org.onlab.onos.store.trivial.impl; ...@@ -18,6 +18,8 @@ package org.onlab.onos.store.trivial.impl;
18 import com.google.common.base.Function; 18 import com.google.common.base.Function;
19 import com.google.common.cache.Cache; 19 import com.google.common.cache.Cache;
20 import com.google.common.cache.CacheBuilder; 20 import com.google.common.cache.CacheBuilder;
21 +import com.google.common.cache.RemovalListener;
22 +import com.google.common.cache.RemovalNotification;
21 import com.google.common.collect.FluentIterable; 23 import com.google.common.collect.FluentIterable;
22 import com.google.common.util.concurrent.Futures; 24 import com.google.common.util.concurrent.Futures;
23 import com.google.common.util.concurrent.SettableFuture; 25 import com.google.common.util.concurrent.SettableFuture;
...@@ -53,8 +55,10 @@ import java.util.List; ...@@ -53,8 +55,10 @@ import java.util.List;
53 import java.util.concurrent.ConcurrentHashMap; 55 import java.util.concurrent.ConcurrentHashMap;
54 import java.util.concurrent.ConcurrentMap; 56 import java.util.concurrent.ConcurrentMap;
55 import java.util.concurrent.CopyOnWriteArrayList; 57 import java.util.concurrent.CopyOnWriteArrayList;
58 +import java.util.concurrent.ExecutionException;
56 import java.util.concurrent.Future; 59 import java.util.concurrent.Future;
57 import java.util.concurrent.TimeUnit; 60 import java.util.concurrent.TimeUnit;
61 +import java.util.concurrent.TimeoutException;
58 import java.util.concurrent.atomic.AtomicInteger; 62 import java.util.concurrent.atomic.AtomicInteger;
59 63
60 import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked; 64 import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
...@@ -86,8 +90,7 @@ public class SimpleFlowRuleStore ...@@ -86,8 +90,7 @@ public class SimpleFlowRuleStore
86 private Cache<Integer, SettableFuture<CompletedBatchOperation>> pendingFutures = 90 private Cache<Integer, SettableFuture<CompletedBatchOperation>> pendingFutures =
87 CacheBuilder.newBuilder() 91 CacheBuilder.newBuilder()
88 .expireAfterWrite(pendingFutureTimeoutMinutes, TimeUnit.MINUTES) 92 .expireAfterWrite(pendingFutureTimeoutMinutes, TimeUnit.MINUTES)
89 - // TODO Explicitly fail the future if expired? 93 + .removalListener(new TimeoutFuture())
90 - //.removalListener(listener)
91 .build(); 94 .build();
92 95
93 @Activate 96 @Activate
...@@ -303,4 +306,15 @@ public class SimpleFlowRuleStore ...@@ -303,4 +306,15 @@ public class SimpleFlowRuleStore
303 } 306 }
304 notifyDelegate(event); 307 notifyDelegate(event);
305 } 308 }
309 +
310 + private static final class TimeoutFuture
311 + implements RemovalListener<Integer, SettableFuture<CompletedBatchOperation>> {
312 + @Override
313 + public void onRemoval(RemovalNotification<Integer, SettableFuture<CompletedBatchOperation>> notification) {
314 + // wrapping in ExecutionException to support Future.get
315 + notification.getValue()
316 + .setException(new ExecutionException("Timed out",
317 + new TimeoutException()));
318 + }
319 + }
306 } 320 }
......
...@@ -197,6 +197,8 @@ ...@@ -197,6 +197,8 @@
197 <feature name="onos-app-sdnip" version="1.0.0" 197 <feature name="onos-app-sdnip" version="1.0.0"
198 description="SDN-IP peering application"> 198 description="SDN-IP peering application">
199 <feature>onos-api</feature> 199 <feature>onos-api</feature>
200 + <feature>onos-app-proxyarp</feature>
201 + <feature>onos-app-config</feature>
200 <bundle>mvn:org.onlab.onos/onos-app-sdnip/1.0.0-SNAPSHOT</bundle> 202 <bundle>mvn:org.onlab.onos/onos-app-sdnip/1.0.0-SNAPSHOT</bundle>
201 </feature> 203 </feature>
202 204
...@@ -225,4 +227,12 @@ ...@@ -225,4 +227,12 @@
225 <bundle>mvn:org.onlab.onos/onos-app-metrics-topology/1.0.0-SNAPSHOT</bundle> 227 <bundle>mvn:org.onlab.onos/onos-app-metrics-topology/1.0.0-SNAPSHOT</bundle>
226 </feature> 228 </feature>
227 229
230 + <feature name="onos-app-demo" version="1.0.0"
231 + description="ONOS demo applications">
232 + <feature>onos-api</feature>
233 + <bundle>mvn:org.onlab.onos/onos-app-demo/1.0.0-SNAPSHOT</bundle>
234 + </feature>
235 +
236 +
237 +
228 </features> 238 </features>
......
...@@ -15,21 +15,11 @@ ...@@ -15,21 +15,11 @@
15 */ 15 */
16 package org.onlab.onos.provider.of.flow.impl; 16 package org.onlab.onos.provider.of.flow.impl;
17 17
18 -import static org.slf4j.LoggerFactory.getLogger; 18 +import com.google.common.collect.ArrayListMultimap;
19 - 19 +import com.google.common.collect.Maps;
20 -import java.util.Collections; 20 +import com.google.common.collect.Multimap;
21 -import java.util.HashMap; 21 +import com.google.common.collect.Sets;
22 -import java.util.List; 22 +import com.google.common.util.concurrent.ExecutionList;
23 -import java.util.Map;
24 -import java.util.Set;
25 -import java.util.concurrent.ConcurrentHashMap;
26 -import java.util.concurrent.CountDownLatch;
27 -import java.util.concurrent.ExecutionException;
28 -import java.util.concurrent.Executor;
29 -import java.util.concurrent.TimeUnit;
30 -import java.util.concurrent.TimeoutException;
31 -import java.util.concurrent.atomic.AtomicBoolean;
32 -
33 import org.apache.felix.scr.annotations.Activate; 23 import org.apache.felix.scr.annotations.Activate;
34 import org.apache.felix.scr.annotations.Component; 24 import org.apache.felix.scr.annotations.Component;
35 import org.apache.felix.scr.annotations.Deactivate; 25 import org.apache.felix.scr.annotations.Deactivate;
...@@ -80,15 +70,23 @@ import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg; ...@@ -80,15 +70,23 @@ import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
80 import org.projectfloodlight.openflow.protocol.instruction.OFInstruction; 70 import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
81 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions; 71 import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
82 import org.projectfloodlight.openflow.types.OFPort; 72 import org.projectfloodlight.openflow.types.OFPort;
83 -import org.projectfloodlight.openflow.types.U32;
84 import org.slf4j.Logger; 73 import org.slf4j.Logger;
85 74
86 -import com.google.common.collect.ArrayListMultimap; 75 +import java.util.Collections;
87 -import com.google.common.collect.Maps; 76 +import java.util.HashMap;
88 -import com.google.common.collect.Multimap; 77 +import java.util.List;
89 -import com.google.common.collect.Sets; 78 +import java.util.Map;
90 -import com.google.common.util.concurrent.ExecutionList; 79 +import java.util.Set;
91 -import com.google.common.util.concurrent.ListenableFuture; 80 +import java.util.concurrent.ConcurrentHashMap;
81 +import java.util.concurrent.CountDownLatch;
82 +import java.util.concurrent.ExecutionException;
83 +import java.util.concurrent.Future;
84 +import java.util.concurrent.TimeUnit;
85 +import java.util.concurrent.TimeoutException;
86 +import java.util.concurrent.atomic.AtomicBoolean;
87 +import java.util.concurrent.atomic.AtomicLong;
88 +
89 +import static org.slf4j.LoggerFactory.getLogger;
92 90
93 /** 91 /**
94 * Provider which uses an OpenFlow controller to detect network 92 * Provider which uses an OpenFlow controller to detect network
...@@ -124,6 +122,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -124,6 +122,8 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
124 122
125 private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap(); 123 private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap();
126 124
125 + private final AtomicLong xidCounter = new AtomicLong(0);
126 +
127 /** 127 /**
128 * Creates an OpenFlow host provider. 128 * Creates an OpenFlow host provider.
129 */ 129 */
...@@ -154,6 +154,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -154,6 +154,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
154 154
155 log.info("Stopped"); 155 log.info("Stopped");
156 } 156 }
157 +
157 @Override 158 @Override
158 public void applyFlowRule(FlowRule... flowRules) { 159 public void applyFlowRule(FlowRule... flowRules) {
159 for (int i = 0; i < flowRules.length; i++) { 160 for (int i = 0; i < flowRules.length; i++) {
...@@ -167,7 +168,6 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -167,7 +168,6 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
167 } 168 }
168 169
169 170
170 -
171 @Override 171 @Override
172 public void removeFlowRule(FlowRule... flowRules) { 172 public void removeFlowRule(FlowRule... flowRules) {
173 for (int i = 0; i < flowRules.length; i++) { 173 for (int i = 0; i < flowRules.length; i++) {
...@@ -188,11 +188,15 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -188,11 +188,15 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
188 } 188 }
189 189
190 @Override 190 @Override
191 - public ListenableFuture<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch) { 191 + public Future<CompletedBatchOperation> executeBatch(BatchOperation<FlowRuleBatchEntry> batch) {
192 final Set<Dpid> sws = 192 final Set<Dpid> sws =
193 Collections.newSetFromMap(new ConcurrentHashMap<Dpid, Boolean>()); 193 Collections.newSetFromMap(new ConcurrentHashMap<Dpid, Boolean>());
194 final Map<Long, FlowRuleBatchEntry> fmXids = new HashMap<Long, FlowRuleBatchEntry>(); 194 final Map<Long, FlowRuleBatchEntry> fmXids = new HashMap<Long, FlowRuleBatchEntry>();
195 - OFFlowMod mod = null; 195 + /*
196 + * Use identity hash map for reference equality as we could have equal
197 + * flow mods for different switches.
198 + */
199 + Map<OFFlowMod, OpenFlowSwitch> mods = Maps.newIdentityHashMap();
196 for (FlowRuleBatchEntry fbe : batch.getOperations()) { 200 for (FlowRuleBatchEntry fbe : batch.getOperations()) {
197 FlowRule flowRule = fbe.getTarget(); 201 FlowRule flowRule = fbe.getTarget();
198 OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri())); 202 OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId().uri()));
...@@ -208,6 +212,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -208,6 +212,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
208 } 212 }
209 sws.add(new Dpid(sw.getId())); 213 sws.add(new Dpid(sw.getId()));
210 FlowModBuilder builder = FlowModBuilder.builder(flowRule, sw.factory()); 214 FlowModBuilder builder = FlowModBuilder.builder(flowRule, sw.factory());
215 + OFFlowMod mod = null;
211 switch (fbe.getOperator()) { 216 switch (fbe.getOperator()) {
212 case ADD: 217 case ADD:
213 mod = builder.buildFlowAdd(); 218 mod = builder.buildFlowAdd();
...@@ -222,19 +227,23 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -222,19 +227,23 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
222 log.error("Unsupported batch operation {}", fbe.getOperator()); 227 log.error("Unsupported batch operation {}", fbe.getOperator());
223 } 228 }
224 if (mod != null) { 229 if (mod != null) {
225 - sw.sendMsg(mod); 230 + mods.put(mod, sw);
226 - fmXids.put(mod.getXid(), fbe); 231 + fmXids.put(xidCounter.getAndIncrement(), fbe);
227 } else { 232 } else {
228 log.error("Conversion of flowrule {} failed.", flowRule); 233 log.error("Conversion of flowrule {} failed.", flowRule);
229 } 234 }
230 -
231 } 235 }
232 InstallationFuture installation = new InstallationFuture(sws, fmXids); 236 InstallationFuture installation = new InstallationFuture(sws, fmXids);
233 for (Long xid : fmXids.keySet()) { 237 for (Long xid : fmXids.keySet()) {
234 pendingFMs.put(xid, installation); 238 pendingFMs.put(xid, installation);
235 } 239 }
236 - pendingFutures.put(U32.f(batch.hashCode()), installation); 240 + pendingFutures.put(installation.xid(), installation);
237 - installation.verify(U32.f(batch.hashCode())); 241 + for (Map.Entry<OFFlowMod, OpenFlowSwitch> entry : mods.entrySet()) {
242 + OpenFlowSwitch sw = entry.getValue();
243 + OFFlowMod mod = entry.getKey();
244 + sw.sendMsg(mod);
245 + }
246 + installation.verify();
238 return installation; 247 return installation;
239 } 248 }
240 249
...@@ -352,8 +361,9 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -352,8 +361,9 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
352 361
353 } 362 }
354 363
355 - private class InstallationFuture implements ListenableFuture<CompletedBatchOperation> { 364 + private class InstallationFuture implements Future<CompletedBatchOperation> {
356 365
366 + private final Long xid;
357 private final Set<Dpid> sws; 367 private final Set<Dpid> sws;
358 private final AtomicBoolean ok = new AtomicBoolean(true); 368 private final AtomicBoolean ok = new AtomicBoolean(true);
359 private final Map<Long, FlowRuleBatchEntry> fms; 369 private final Map<Long, FlowRuleBatchEntry> fms;
...@@ -361,18 +371,22 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -361,18 +371,22 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
361 private final Set<FlowEntry> offendingFlowMods = Sets.newHashSet(); 371 private final Set<FlowEntry> offendingFlowMods = Sets.newHashSet();
362 372
363 private final CountDownLatch countDownLatch; 373 private final CountDownLatch countDownLatch;
364 - private Long pendingXid;
365 private BatchState state; 374 private BatchState state;
366 375
367 private final ExecutionList executionList = new ExecutionList(); 376 private final ExecutionList executionList = new ExecutionList();
368 377
369 public InstallationFuture(Set<Dpid> sws, Map<Long, FlowRuleBatchEntry> fmXids) { 378 public InstallationFuture(Set<Dpid> sws, Map<Long, FlowRuleBatchEntry> fmXids) {
379 + this.xid = xidCounter.getAndIncrement();
370 this.state = BatchState.STARTED; 380 this.state = BatchState.STARTED;
371 this.sws = sws; 381 this.sws = sws;
372 this.fms = fmXids; 382 this.fms = fmXids;
373 countDownLatch = new CountDownLatch(sws.size()); 383 countDownLatch = new CountDownLatch(sws.size());
374 } 384 }
375 385
386 + public Long xid() {
387 + return xid;
388 + }
389 +
376 public void fail(OFErrorMsg msg, Dpid dpid) { 390 public void fail(OFErrorMsg msg, Dpid dpid) {
377 391
378 ok.set(false); 392 ok.set(false);
...@@ -434,13 +448,12 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -434,13 +448,12 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
434 } 448 }
435 449
436 450
437 - public void verify(Long id) { 451 + public void verify() {
438 - pendingXid = id;
439 for (Dpid dpid : sws) { 452 for (Dpid dpid : sws) {
440 OpenFlowSwitch sw = controller.getSwitch(dpid); 453 OpenFlowSwitch sw = controller.getSwitch(dpid);
441 OFBarrierRequest.Builder builder = sw.factory() 454 OFBarrierRequest.Builder builder = sw.factory()
442 .buildBarrierRequest() 455 .buildBarrierRequest()
443 - .setXid(id); 456 + .setXid(xid);
444 sw.sendMsg(builder.build()); 457 sw.sendMsg(builder.build());
445 } 458 }
446 } 459 }
...@@ -462,7 +475,6 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -462,7 +475,6 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
462 } 475 }
463 476
464 } 477 }
465 - invokeCallbacks();
466 return true; 478 return true;
467 } 479 }
468 480
...@@ -481,6 +493,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -481,6 +493,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
481 countDownLatch.await(); 493 countDownLatch.await();
482 this.state = BatchState.FINISHED; 494 this.state = BatchState.FINISHED;
483 CompletedBatchOperation result = new CompletedBatchOperation(ok.get(), offendingFlowMods); 495 CompletedBatchOperation result = new CompletedBatchOperation(ok.get(), offendingFlowMods);
496 + //FIXME do cleanup here
484 return result; 497 return result;
485 } 498 }
486 499
...@@ -491,6 +504,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -491,6 +504,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
491 if (countDownLatch.await(timeout, unit)) { 504 if (countDownLatch.await(timeout, unit)) {
492 this.state = BatchState.FINISHED; 505 this.state = BatchState.FINISHED;
493 CompletedBatchOperation result = new CompletedBatchOperation(ok.get(), offendingFlowMods); 506 CompletedBatchOperation result = new CompletedBatchOperation(ok.get(), offendingFlowMods);
507 + // FIXME do cleanup here
494 return result; 508 return result;
495 } 509 }
496 throw new TimeoutException(); 510 throw new TimeoutException();
...@@ -498,9 +512,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -498,9 +512,7 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
498 512
499 private void cleanUp() { 513 private void cleanUp() {
500 if (isDone() || isCancelled()) { 514 if (isDone() || isCancelled()) {
501 - if (pendingXid != null) { 515 + pendingFutures.remove(xid);
502 - pendingFutures.remove(pendingXid);
503 - }
504 for (Long xid : fms.keySet()) { 516 for (Long xid : fms.keySet()) {
505 pendingFMs.remove(xid); 517 pendingFMs.remove(xid);
506 } 518 }
...@@ -509,21 +521,10 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr ...@@ -509,21 +521,10 @@ public class OpenFlowRuleProvider extends AbstractProvider implements FlowRulePr
509 521
510 private void removeRequirement(Dpid dpid) { 522 private void removeRequirement(Dpid dpid) {
511 countDownLatch.countDown(); 523 countDownLatch.countDown();
512 - if (countDownLatch.getCount() == 0) {
513 - invokeCallbacks();
514 - }
515 sws.remove(dpid); 524 sws.remove(dpid);
525 + //FIXME don't do cleanup here
516 cleanUp(); 526 cleanUp();
517 } 527 }
518 -
519 - @Override
520 - public void addListener(Runnable runnable, Executor executor) {
521 - executionList.add(runnable, executor);
522 - }
523 -
524 - private void invokeCallbacks() {
525 - executionList.execute();
526 - }
527 } 528 }
528 529
529 } 530 }
......
1 -onos-config command will copy files contained in this directory to ONOS instances according to cell definition 1 +The onos-config command will copy files contained in this directory to ONOS
2 - 2 +instances according to cell definition.
......
...@@ -10,4 +10,5 @@ ...@@ -10,4 +10,5 @@
10 [ "$1" = "-w" ] && shift && onos-wait-for-start $1 10 [ "$1" = "-w" ] && shift && onos-wait-for-start $1
11 11
12 [ -n "$1" ] && OCI=$(find_node $1) && shift 12 [ -n "$1" ] && OCI=$(find_node $1) && shift
13 +unset KARAF_HOME
13 client -h $OCI -u karaf "$@" 2>/dev/null 14 client -h $OCI -u karaf "$@" 2>/dev/null
......
...@@ -3,10 +3,7 @@ ...@@ -3,10 +3,7 @@
3 # Launches ONOS GUI on the specified node. 3 # Launches ONOS GUI on the specified node.
4 # ----------------------------------------------------------------------------- 4 # -----------------------------------------------------------------------------
5 5
6 -[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
7 -. $ONOS_ROOT/tools/build/envDefaults
8 -
9 host=${1:-$OCI} 6 host=${1:-$OCI}
10 host=${host:-localhost} 7 host=${host:-localhost}
11 8
12 -open http://$host:8181/onos/tvue 9 +open http://$host:8181/onos/ui
......
...@@ -40,6 +40,7 @@ fi ...@@ -40,6 +40,7 @@ fi
40 # Load the cell setup 40 # Load the cell setup
41 . $ONOS_ROOT/tools/test/cells/${cell} 41 . $ONOS_ROOT/tools/test/cells/${cell}
42 42
43 +echo "ONOS_CELL=${ONOS_CELL}"
43 echo "ONOS_NIC=${ONOS_NIC}" 44 echo "ONOS_NIC=${ONOS_NIC}"
44 for n in {0..9}; do 45 for n in {0..9}; do
45 ocn="OC${n}" 46 ocn="OC${n}"
......
...@@ -108,33 +108,26 @@ ...@@ -108,33 +108,26 @@
108 ], 108 ],
109 109
110 "links" : [ 110 "links" : [
111 - { "src": "of:0000ffffffffff01/10", "dst": "of:0000ffffffffff02/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } }, 111 + { "src": "of:0000ffffffffff01/50", "dst": "of:0000ffffffffff02/30","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
112 - { "src": "of:0000ffffffffff02/10", "dst": "of:0000ffffffffff03/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } }, 112 + { "src": "of:0000ffffffffff02/50", "dst": "of:0000ffffffffff03/30","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
113 - { "src": "of:0000ffffffffff03/30", "dst": "of:0000ffffffffff04/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } }, 113 + { "src": "of:0000ffffffffff03/50", "dst": "of:0000ffffffffff04/50","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
114 - { "src": "of:0000ffffffffff02/20", "dst": "of:0000ffffffffff05/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } }, 114 + { "src": "of:0000ffffffffff01/20", "dst": "of:0000ffffffffff05/50","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
115 - { "src": "of:0000ffffffffff03/20", "dst": "of:0000ffffffffff06/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } }, 115 + { "src": "of:0000ffffffffff02/20", "dst": "of:0000ffffffffff05/20","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
116 - { "src": "of:0000ffffffffff05/30", "dst": "of:0000ffffffffff06/20", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } }, 116 + { "src": "of:0000ffffffffff03/20", "dst": "of:0000ffffffffff06/50","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
117 - { "src": "of:0000ffffffffff05/20", "dst": "of:0000ffffffffff07/21", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } }, 117 + { "src": "of:0000ffffffffff04/20", "dst": "of:0000ffffffffff06/20","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
118 - { "src": "of:0000ffffffffff06/30", "dst": "of:0000ffffffffff08/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } }, 118 + { "src": "of:0000ffffffffff05/30", "dst": "of:0000ffffffffff06/40","type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
119 - { "src": "of:0000ffffffffff07/30", "dst": "of:0000ffffffffff08/20", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } }, 119 + { "src": "of:0000ffffffffff05/40", "dst": "of:0000ffffffffff07/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
120 - { "src": "of:0000ffffffffff07/20", "dst": "of:0000ffffffffff09/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } }, 120 + { "src": "of:0000ffffffffff06/30", "dst": "of:0000ffffffffff08/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
121 - { "src": "of:0000ffffffffff08/30", "dst": "of:0000ffffffffff0A/10", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } }, 121 + { "src": "of:0000ffffffffff07/20", "dst": "of:0000ffffffffff08/30", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
122 + { "src": "of:0000ffffffffff07/30", "dst": "of:0000ffffffffff09/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
123 + { "src": "of:0000ffffffffff08/20", "dst": "of:0000ffffffffff0A/50", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
122 { "src": "of:0000ffffffffff09/20", "dst": "of:0000ffffffffff0A/20", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } }, 124 { "src": "of:0000ffffffffff09/20", "dst": "of:0000ffffffffff0A/20", "type": "OPTICAL", "annotations": { "optical.waves": 80, "optical.type": "WDM", "optical.kms": 1000, "durable": "true" } },
123 125
124 - { "src": "of:0000ffffffff0001/2", "dst": "of:0000ffffffffff01/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } }, 126 + { "src": "of:0000ffffffff0001/2", "dst": "of:0000ffffffffff01/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
125 - { "src": "of:0000ffffffff0003/2", "dst": "of:0000ffffffffff03/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } }, 127 + { "src": "of:0000ffffffff0002/2", "dst": "of:0000ffffffffff04/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
126 - { "src": "of:0000ffffffff0004/2", "dst": "of:0000ffffffffff04/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } }, 128 + { "src": "of:0000ffffffff0003/2", "dst": "of:0000ffffffffff06/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
127 - { "src": "of:0000ffffffff0007/2", "dst": "of:0000ffffffffff07/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } }, 129 + { "src": "of:0000ffffffff0004/2", "dst": "of:0000ffffffffff07/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
128 - { "src": "of:0000ffffffff0009/2", "dst": "of:0000ffffffffff09/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } }, 130 + { "src": "of:0000ffffffff0005/2", "dst": "of:0000ffffffffff09/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } },
129 - { "src": "of:0000ffffffff000A/2", "dst": "of:0000ffffffffff0A/1", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } } 131 + { "src": "of:0000ffffffff0006/2", "dst": "of:0000ffffffffff0A/10", "type": "OPTICAL", "annotations": { "bandwidth": 100000, "optical.type": "cross-connect", "durable": "true" } }
130 - ],
131 -
132 - "hosts" : [
133 - { "mac": "00:00:00:00:00:01", "vlan": -1, "location": "of:0000ffffffff0001/1", "ip": "10.0.0.1" },
134 - { "mac": "00:00:00:00:00:03", "vlan": -1, "location": "of:0000ffffffff0003/1", "ip": "10.0.0.3" },
135 - { "mac": "00:00:00:00:00:04", "vlan": -1, "location": "of:0000ffffffff0004/1", "ip": "10.0.0.4" },
136 - { "mac": "00:00:00:00:00:07", "vlan": -1, "location": "of:0000ffffffff0007/1", "ip": "10.0.0.7" },
137 - { "mac": "00:00:00:00:00:09", "vlan": -1, "location": "of:0000ffffffff0009/1", "ip": "10.0.0.9" },
138 - { "mac": "00:00:00:00:00:0A", "vlan": -1, "location": "of:0000ffffffff000A/1", "ip": "10.0.0.10" }
139 ] 132 ]
140 } 133 }
......
...@@ -43,9 +43,9 @@ public class ImmutableClassChecker { ...@@ -43,9 +43,9 @@ public class ImmutableClassChecker {
43 * @param clazz the class to check 43 * @param clazz the class to check
44 * @return true if the given class is a properly specified immutable class. 44 * @return true if the given class is a properly specified immutable class.
45 */ 45 */
46 - private boolean isImmutableClass(Class<?> clazz) { 46 + private boolean isImmutableClass(Class<?> clazz, boolean allowNonFinalClass) {
47 // class must be declared final 47 // class must be declared final
48 - if (!Modifier.isFinal(clazz.getModifiers())) { 48 + if (!allowNonFinalClass && !Modifier.isFinal(clazz.getModifiers())) {
49 failureReason = "a class that is not final"; 49 failureReason = "a class that is not final";
50 return false; 50 return false;
51 } 51 }
...@@ -113,16 +113,43 @@ public class ImmutableClassChecker { ...@@ -113,16 +113,43 @@ public class ImmutableClassChecker {
113 } 113 }
114 114
115 /** 115 /**
116 - * Assert that the given class adheres to the utility class rules. 116 + * Assert that the given class adheres to the immutable class rules.
117 * 117 *
118 * @param clazz the class to check 118 * @param clazz the class to check
119 * 119 *
120 - * @throws java.lang.AssertionError if the class is not a valid 120 + * @throws java.lang.AssertionError if the class is not an
121 - * utility class 121 + * immutable class
122 */ 122 */
123 public static void assertThatClassIsImmutable(Class<?> clazz) { 123 public static void assertThatClassIsImmutable(Class<?> clazz) {
124 final ImmutableClassChecker checker = new ImmutableClassChecker(); 124 final ImmutableClassChecker checker = new ImmutableClassChecker();
125 - if (!checker.isImmutableClass(clazz)) { 125 + if (!checker.isImmutableClass(clazz, false)) {
126 + final Description toDescription = new StringDescription();
127 + final Description mismatchDescription = new StringDescription();
128 +
129 + checker.describeTo(toDescription);
130 + checker.describeMismatch(mismatchDescription);
131 + final String reason =
132 + "\n" +
133 + "Expected: is \"" + toDescription.toString() + "\"\n" +
134 + " but : was \"" + mismatchDescription.toString() + "\"";
135 +
136 + throw new AssertionError(reason);
137 + }
138 + }
139 +
140 + /**
141 + * Assert that the given class adheres to the immutable class rules, but
142 + * is not declared final. Classes that need to be inherited from cannot be
143 + * declared final.
144 + *
145 + * @param clazz the class to check
146 + *
147 + * @throws java.lang.AssertionError if the class is not an
148 + * immutable class
149 + */
150 + public static void assertThatClassIsImmutableBaseClass(Class<?> clazz) {
151 + final ImmutableClassChecker checker = new ImmutableClassChecker();
152 + if (!checker.isImmutableClass(clazz, true)) {
126 final Description toDescription = new StringDescription(); 153 final Description toDescription = new StringDescription();
127 final Description mismatchDescription = new StringDescription(); 154 final Description mismatchDescription = new StringDescription();
128 155
......
...@@ -24,6 +24,7 @@ import org.onlab.onos.cluster.ClusterService; ...@@ -24,6 +24,7 @@ import org.onlab.onos.cluster.ClusterService;
24 import org.onlab.onos.cluster.ControllerNode; 24 import org.onlab.onos.cluster.ControllerNode;
25 import org.onlab.onos.cluster.NodeId; 25 import org.onlab.onos.cluster.NodeId;
26 import org.onlab.onos.mastership.MastershipService; 26 import org.onlab.onos.mastership.MastershipService;
27 +import org.onlab.onos.net.Annotated;
27 import org.onlab.onos.net.Annotations; 28 import org.onlab.onos.net.Annotations;
28 import org.onlab.onos.net.ConnectPoint; 29 import org.onlab.onos.net.ConnectPoint;
29 import org.onlab.onos.net.DefaultEdgeLink; 30 import org.onlab.onos.net.DefaultEdgeLink;
...@@ -45,6 +46,8 @@ import org.onlab.onos.net.link.LinkService; ...@@ -45,6 +46,8 @@ import org.onlab.onos.net.link.LinkService;
45 import org.onlab.onos.net.provider.ProviderId; 46 import org.onlab.onos.net.provider.ProviderId;
46 import org.onlab.osgi.ServiceDirectory; 47 import org.onlab.osgi.ServiceDirectory;
47 import org.onlab.packet.IpAddress; 48 import org.onlab.packet.IpAddress;
49 +import org.slf4j.Logger;
50 +import org.slf4j.LoggerFactory;
48 51
49 import java.util.Iterator; 52 import java.util.Iterator;
50 import java.util.Map; 53 import java.util.Map;
...@@ -68,6 +71,8 @@ import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED; ...@@ -68,6 +71,8 @@ import static org.onlab.onos.net.link.LinkEvent.Type.LINK_REMOVED;
68 */ 71 */
69 public abstract class TopologyMessages { 72 public abstract class TopologyMessages {
70 73
74 + protected static final Logger log = LoggerFactory.getLogger(TopologyMessages.class);
75 +
71 private static final ProviderId PID = new ProviderId("core", "org.onlab.onos.core", true); 76 private static final ProviderId PID = new ProviderId("core", "org.onlab.onos.core", true);
72 private static final String COMPACT = "%s/%s-%s/%s"; 77 private static final String COMPACT = "%s/%s-%s/%s";
73 78
...@@ -195,7 +200,7 @@ public abstract class TopologyMessages { ...@@ -195,7 +200,7 @@ public abstract class TopologyMessages {
195 .put("id", device.id().toString()) 200 .put("id", device.id().toString())
196 .put("type", device.type().toString().toLowerCase()) 201 .put("type", device.type().toString().toLowerCase())
197 .put("online", deviceService.isAvailable(device.id())) 202 .put("online", deviceService.isAvailable(device.id()))
198 - .put("master", mastershipService.getMasterFor(device.id()).toString()); 203 + .put("master", master(device.id()));
199 204
200 // Generate labels: id, chassis id, no-label, optional-name 205 // Generate labels: id, chassis id, no-label, optional-name
201 ArrayNode labels = mapper.createArrayNode(); 206 ArrayNode labels = mapper.createArrayNode();
...@@ -207,6 +212,7 @@ public abstract class TopologyMessages { ...@@ -207,6 +212,7 @@ public abstract class TopologyMessages {
207 // Add labels, props and stuff the payload into envelope. 212 // Add labels, props and stuff the payload into envelope.
208 payload.set("labels", labels); 213 payload.set("labels", labels);
209 payload.set("props", props(device.annotations())); 214 payload.set("props", props(device.annotations()));
215 + addGeoLocation(device, payload);
210 addMetaUi(device.id().toString(), payload); 216 addMetaUi(device.id().toString(), payload);
211 217
212 String type = (event.type() == DEVICE_ADDED) ? "addDevice" : 218 String type = (event.type() == DEVICE_ADDED) ? "addDevice" :
...@@ -220,6 +226,7 @@ public abstract class TopologyMessages { ...@@ -220,6 +226,7 @@ public abstract class TopologyMessages {
220 ObjectNode payload = mapper.createObjectNode() 226 ObjectNode payload = mapper.createObjectNode()
221 .put("id", compactLinkString(link)) 227 .put("id", compactLinkString(link))
222 .put("type", link.type().toString().toLowerCase()) 228 .put("type", link.type().toString().toLowerCase())
229 + .put("online", true) // TODO: add link state field
223 .put("linkWidth", 2) 230 .put("linkWidth", 2)
224 .put("src", link.src().deviceId().toString()) 231 .put("src", link.src().deviceId().toString())
225 .put("srcPort", link.src().port().toString()) 232 .put("srcPort", link.src().port().toString())
...@@ -237,10 +244,11 @@ public abstract class TopologyMessages { ...@@ -237,10 +244,11 @@ public abstract class TopologyMessages {
237 .put("id", host.id().toString()) 244 .put("id", host.id().toString())
238 .put("ingress", compactLinkString(edgeLink(host, true))) 245 .put("ingress", compactLinkString(edgeLink(host, true)))
239 .put("egress", compactLinkString(edgeLink(host, false))); 246 .put("egress", compactLinkString(edgeLink(host, false)));
240 - payload.set("cp", location(mapper, host.location())); 247 + payload.set("cp", hostConnect(mapper, host.location()));
241 payload.set("labels", labels(mapper, ip(host.ipAddresses()), 248 payload.set("labels", labels(mapper, ip(host.ipAddresses()),
242 host.mac().toString())); 249 host.mac().toString()));
243 payload.set("props", props(host.annotations())); 250 payload.set("props", props(host.annotations()));
251 + addGeoLocation(host, payload);
244 addMetaUi(host.id().toString(), payload); 252 addMetaUi(host.id().toString(), payload);
245 253
246 String type = (event.type() == HOST_ADDED) ? "addHost" : 254 String type = (event.type() == HOST_ADDED) ? "addHost" :
...@@ -249,7 +257,7 @@ public abstract class TopologyMessages { ...@@ -249,7 +257,7 @@ public abstract class TopologyMessages {
249 } 257 }
250 258
251 // Encodes the specified host location into a JSON object. 259 // Encodes the specified host location into a JSON object.
252 - private ObjectNode location(ObjectMapper mapper, HostLocation location) { 260 + private ObjectNode hostConnect(ObjectMapper mapper, HostLocation location) {
253 return mapper.createObjectNode() 261 return mapper.createObjectNode()
254 .put("device", location.deviceId().toString()) 262 .put("device", location.deviceId().toString())
255 .put("port", location.port().toLong()); 263 .put("port", location.port().toLong());
...@@ -264,6 +272,12 @@ public abstract class TopologyMessages { ...@@ -264,6 +272,12 @@ public abstract class TopologyMessages {
264 return json; 272 return json;
265 } 273 }
266 274
275 + // Returns the name of the master node for the specified device id.
276 + private String master(DeviceId deviceId) {
277 + NodeId master = mastershipService.getMasterFor(deviceId);
278 + return master != null ? master.toString() : "";
279 + }
280 +
267 // Generates an edge link from the specified host location. 281 // Generates an edge link from the specified host location.
268 private EdgeLink edgeLink(Host host, boolean ingress) { 282 private EdgeLink edgeLink(Host host, boolean ingress) {
269 return new DefaultEdgeLink(PID, new ConnectPoint(host.id(), portNumber(0)), 283 return new DefaultEdgeLink(PID, new ConnectPoint(host.id(), portNumber(0)),
...@@ -278,10 +292,28 @@ public abstract class TopologyMessages { ...@@ -278,10 +292,28 @@ public abstract class TopologyMessages {
278 } 292 }
279 } 293 }
280 294
295 + // Adds a geo location JSON to the specified payload object.
296 + private void addGeoLocation(Annotated annotated, ObjectNode payload) {
297 + Annotations annotations = annotated.annotations();
298 + String slat = annotations.value("latitude");
299 + String slng = annotations.value("longitude");
300 + try {
301 + if (slat != null && slng != null && !slat.isEmpty() && !slng.isEmpty()) {
302 + double lat = Double.parseDouble(slat);
303 + double lng = Double.parseDouble(slng);
304 + ObjectNode loc = mapper.createObjectNode()
305 + .put("type", "latlng").put("lat", lat).put("lng", lng);
306 + payload.set("location", loc);
307 + }
308 + } catch (NumberFormatException e) {
309 + log.warn("Invalid geo data latitude={}; longiture={}", slat, slng);
310 + }
311 + }
312 +
281 // Updates meta UI information for the specified object. 313 // Updates meta UI information for the specified object.
282 protected void updateMetaUi(ObjectNode event) { 314 protected void updateMetaUi(ObjectNode event) {
283 ObjectNode payload = payload(event); 315 ObjectNode payload = payload(event);
284 - metaUi.put(string(payload, "id"), payload); 316 + metaUi.put(string(payload, "id"), (ObjectNode) payload.path("memento"));
285 } 317 }
286 318
287 // Returns device details response. 319 // Returns device details response.
...@@ -289,7 +321,6 @@ public abstract class TopologyMessages { ...@@ -289,7 +321,6 @@ public abstract class TopologyMessages {
289 Device device = deviceService.getDevice(deviceId); 321 Device device = deviceService.getDevice(deviceId);
290 Annotations annot = device.annotations(); 322 Annotations annot = device.annotations();
291 int portCount = deviceService.getPorts(deviceId).size(); 323 int portCount = deviceService.getPorts(deviceId).size();
292 - NodeId master = mastershipService.getMasterFor(device.id());
293 return envelope("showDetails", sid, 324 return envelope("showDetails", sid,
294 json(deviceId.toString(), 325 json(deviceId.toString(),
295 device.type().toString().toLowerCase(), 326 device.type().toString().toLowerCase(),
...@@ -303,7 +334,7 @@ public abstract class TopologyMessages { ...@@ -303,7 +334,7 @@ public abstract class TopologyMessages {
303 new Prop("Longitude", annot.value("longitude")), 334 new Prop("Longitude", annot.value("longitude")),
304 new Prop("Ports", Integer.toString(portCount)), 335 new Prop("Ports", Integer.toString(portCount)),
305 new Separator(), 336 new Separator(),
306 - new Prop("Master", master.toString()))); 337 + new Prop("Master", master(deviceId))));
307 } 338 }
308 339
309 // Returns host details response. 340 // Returns host details response.
...@@ -319,16 +350,15 @@ public abstract class TopologyMessages { ...@@ -319,16 +350,15 @@ public abstract class TopologyMessages {
319 new Prop("Longitude", annot.value("longitude")))); 350 new Prop("Longitude", annot.value("longitude"))));
320 } 351 }
321 352
322 -
323 // Produces a path message to the client. 353 // Produces a path message to the client.
324 - protected ObjectNode pathMessage(Path path) { 354 + protected ObjectNode pathMessage(Path path, String type) {
325 ObjectNode payload = mapper.createObjectNode(); 355 ObjectNode payload = mapper.createObjectNode();
326 ArrayNode links = mapper.createArrayNode(); 356 ArrayNode links = mapper.createArrayNode();
327 for (Link link : path.links()) { 357 for (Link link : path.links()) {
328 links.add(compactLinkString(link)); 358 links.add(compactLinkString(link));
329 } 359 }
330 360
331 - payload.set("links", links); 361 + payload.put("type", type).set("links", links);
332 return payload; 362 return payload;
333 } 363 }
334 364
......
...@@ -110,8 +110,8 @@ public class TopologyWebSocket ...@@ -110,8 +110,8 @@ public class TopologyWebSocket
110 try { 110 try {
111 ObjectNode event = (ObjectNode) mapper.reader().readTree(data); 111 ObjectNode event = (ObjectNode) mapper.reader().readTree(data);
112 String type = string(event, "event", "unknown"); 112 String type = string(event, "event", "unknown");
113 - if (type.equals("showDetails")) { 113 + if (type.equals("requestDetails")) {
114 - showDetails(event); 114 + requestDetails(event);
115 } else if (type.equals("updateMeta")) { 115 } else if (type.equals("updateMeta")) {
116 updateMetaUi(event); 116 updateMetaUi(event);
117 } else if (type.equals("requestPath")) { 117 } else if (type.equals("requestPath")) {
...@@ -122,7 +122,7 @@ public class TopologyWebSocket ...@@ -122,7 +122,7 @@ public class TopologyWebSocket
122 cancelTraffic(event); 122 cancelTraffic(event);
123 } 123 }
124 } catch (Exception e) { 124 } catch (Exception e) {
125 - System.out.println("WTF?! " + data); 125 + log.warn("Unable to parse GUI request {} due to {}", data, e);
126 e.printStackTrace(); 126 e.printStackTrace();
127 } 127 }
128 } 128 }
...@@ -165,9 +165,9 @@ public class TopologyWebSocket ...@@ -165,9 +165,9 @@ public class TopologyWebSocket
165 } 165 }
166 166
167 // Sends back device or host details. 167 // Sends back device or host details.
168 - private void showDetails(ObjectNode event) { 168 + private void requestDetails(ObjectNode event) {
169 ObjectNode payload = payload(event); 169 ObjectNode payload = payload(event);
170 - String type = string(payload, "type", "unknown"); 170 + String type = string(payload, "class", "unknown");
171 if (type.equals("device")) { 171 if (type.equals("device")) {
172 sendMessage(deviceDetails(deviceId(string(payload, "id")), 172 sendMessage(deviceDetails(deviceId(string(payload, "id")),
173 number(event, "sid"))); 173 number(event, "sid")));
...@@ -282,7 +282,8 @@ public class TopologyWebSocket ...@@ -282,7 +282,8 @@ public class TopologyWebSocket
282 if (installable != null && !installable.isEmpty()) { 282 if (installable != null && !installable.isEmpty()) {
283 PathIntent pathIntent = (PathIntent) installable.iterator().next(); 283 PathIntent pathIntent = (PathIntent) installable.iterator().next();
284 Path path = pathIntent.path(); 284 Path path = pathIntent.path();
285 - ObjectNode payload = pathMessage(path).put("intentId", intent.id().toString()); 285 + ObjectNode payload = pathMessage(path, "host")
286 + .put("intentId", intent.id().toString());
286 sendMessage(envelope("showPath", sid, payload)); 287 sendMessage(envelope("showPath", sid, payload));
287 } 288 }
288 } 289 }
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
21 <display-name>ONOS GUI</display-name> 21 <display-name>ONOS GUI</display-name>
22 22
23 <welcome-file-list> 23 <welcome-file-list>
24 - <welcome-file>index.html</welcome-file> 24 + <welcome-file>index2.html</welcome-file>
25 </welcome-file-list> 25 </welcome-file-list>
26 26
27 <servlet> 27 <servlet>
......
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 +
17 +/*
18 + ONOS GUI -- Floating Panels -- CSS file
19 +
20 + @author Simon Hunt
21 + */
22 +
23 +.fpanel {
24 + position: absolute;
25 + z-index: 100;
26 + display: block;
27 + top: 10%;
28 + width: 280px;
29 + right: -300px;
30 + opacity: 0;
31 + background-color: rgba(255,255,255,0.8);
32 +
33 + padding: 10px;
34 + color: black;
35 + font-size: 10pt;
36 + box-shadow: 2px 2px 16px #777;
37 +}
38 +
39 +/* TODO: light/dark themes */
40 +.light .fpanel {
41 +
42 +}
43 +.dark .fpanel {
44 +
45 +}
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
40 <link rel="stylesheet" href="base.css"> 40 <link rel="stylesheet" href="base.css">
41 <link rel="stylesheet" href="onos2.css"> 41 <link rel="stylesheet" href="onos2.css">
42 <link rel="stylesheet" href="mast2.css"> 42 <link rel="stylesheet" href="mast2.css">
43 + <link rel="stylesheet" href="floatPanel.css">
43 44
44 <!-- This is where contributed stylesheets get INJECTED --> 45 <!-- This is where contributed stylesheets get INJECTED -->
45 <!-- TODO: replace with template marker and inject refs server-side --> 46 <!-- TODO: replace with template marker and inject refs server-side -->
...@@ -62,8 +63,9 @@ ...@@ -62,8 +63,9 @@
62 <div id="view"> 63 <div id="view">
63 <!-- NOTE: views injected here by onos.js --> 64 <!-- NOTE: views injected here by onos.js -->
64 </div> 65 </div>
65 - <div id="overlays"> 66 + <div id="floatPanels">
66 - <!-- NOTE: overlays injected here, as needed --> 67 + <!-- NOTE: floating panels injected here, as needed -->
68 + <!-- see onos.ui.addFloatingPanel -->
67 </div> 69 </div>
68 <div id="alerts"> 70 <div id="alerts">
69 <!-- NOTE: alert content injected here, as needed --> 71 <!-- NOTE: alert content injected here, as needed -->
......
...@@ -10,6 +10,10 @@ ...@@ -10,6 +10,10 @@
10 "", 10 "",
11 null 11 null
12 ], 12 ],
13 - "props": {} 13 + "props": {
14 + "latitude": 123.5,
15 + "longitude": 67.8,
16 + "anotherProp": "foobar"
17 + }
14 } 18 }
15 } 19 }
......
1 +{
2 + "event": "addDevice",
3 + "payload": {
4 + "id": "of:0000000000000003",
5 + "type": "switch",
6 + "online": true,
7 + "labels": [
8 + "of:0000000000000003",
9 + "3",
10 + "",
11 + null
12 + ],
13 + "props": {
14 + "latitude": 123.5,
15 + "longitude": 67.8,
16 + "anotherProp": "foobar"
17 + },
18 + "metaUi": {
19 + "xpc": 57.3,
20 + "ypc": 24.86,
21 + "and": "other properties the UI wishes to remember..."
22 + }
23 + }
24 +}
1 +{
2 + "event": "showDetails",
3 + "sid": 9,
4 + "payload": {
5 + "id": "CA:4B:EE:A4:B0:33/-1",
6 + "type": "host",
7 + "propOrder": [
8 + "MAC",
9 + "IP",
10 + "-",
11 + "Latitude",
12 + "Longitude"
13 + ],
14 + "props": {
15 + "MAC": "CA:4B:EE:A4:B0:33",
16 + "IP": "[10.0.0.1]",
17 + "-": "",
18 + "Latitude": null,
19 + "Longitude": null
20 + }
21 + }
22 +}
1 +{
2 + "event": "showDetails",
3 + "sid": 37,
4 + "payload": {
5 + "id": "of:000000000000000a",
6 + "type": "switch",
7 + "propOrder": [
8 + "Name",
9 + "Vendor",
10 + "H/W Version",
11 + "S/W Version",
12 + "Serial Number",
13 + "-",
14 + "Latitude",
15 + "Longitude",
16 + "Ports",
17 + "-",
18 + "Master"
19 + ],
20 + "props": {
21 + "Name": null,
22 + "Vendor": "Nicira, Inc.",
23 + "H/W Version": "Open vSwitch",
24 + "S/W Version": "2.0.1",
25 + "Serial Number": "None",
26 + "-": "",
27 + "Latitude": null,
28 + "Longitude": null,
29 + "Ports": "5",
30 + "Master":"local"
31 + }
32 + }
33 +}
1 { 1 {
2 - "event": "noop", 2 + "event": "requestDetails",
3 + "sid": 15,
3 "payload": { 4 "payload": {
4 - "id": "xyyzy" 5 + "id": "of:0000000000000003",
6 + "class": "device"
5 } 7 }
6 } 8 }
9 +
......
1 +{
2 + "event": "requestDetails",
3 + "sid": 9,
4 + "payload": {
5 + "id": "CA:4B:EE:A4:B0:33/-1",
6 + "class": "host"
7 + }
8 +}
...@@ -4,7 +4,11 @@ ...@@ -4,7 +4,11 @@
4 "payload": { 4 "payload": {
5 "id": "62:4F:65:BF:FF:B3/-1", 5 "id": "62:4F:65:BF:FF:B3/-1",
6 "class": "host", 6 "class": "host",
7 - "x": 197, 7 + "memento": {
8 - "y": 177 8 + "xpc": 57.3,
9 + "ypc": 24.86,
10 + "and": "other properties the UI wishes to remember..."
11 + }
9 } 12 }
10 } 13 }
14 +
......
...@@ -5,5 +5,9 @@ ...@@ -5,5 +5,9 @@
5 "title": "Host Intent Scenario", 5 "title": "Host Intent Scenario",
6 "params": { 6 "params": {
7 "lastAuto": 0 7 "lastAuto": 0
8 - } 8 + },
9 + "description": [
10 + "Currently this is just a sketch of the event sequence,",
11 + " but is NOT YET a runnable scenario."
12 + ]
9 } 13 }
...\ No newline at end of file ...\ No newline at end of file
......
1 +{
2 + "event": "removeHost",
3 + "payload": {
4 + "id": "A6:96:E5:03:52:5F/-1",
5 + "ingress": "A6:96:E5:03:52:5F/-1/0-of:0000ffffffff0008/1",
6 + "egress": "of:0000ffffffff0008/1-A6:96:E5:03:52:5F/-1/0",
7 + "cp": {
8 + "device": "of:0000ffffffff0008",
9 + "port": 1
10 + },
11 + "labels": [
12 + "10.0.0.17",
13 + "A6:96:E5:03:52:5F"
14 + ],
15 + "props": {}
16 + }
17 +}
...@@ -4,6 +4,11 @@ ...@@ -4,6 +4,11 @@
4 "id": "of:0000ffffffff0008", 4 "id": "of:0000ffffffff0008",
5 "type": "switch", 5 "type": "switch",
6 "online": false, 6 "online": false,
7 + "location": {
8 + "type": "latlng",
9 + "lat": 37.6,
10 + "lng": 122.3
11 + },
7 "labels": [ 12 "labels": [
8 "0000ffffffff0008", 13 "0000ffffffff0008",
9 "FF:FF:FF:FF:00:08", 14 "FF:FF:FF:FF:00:08",
......
...@@ -20,6 +20,6 @@ ...@@ -20,6 +20,6 @@
20 "10. update link (increase width, update props)", 20 "10. update link (increase width, update props)",
21 "11. update link (reduce width, update props)", 21 "11. update link (reduce width, update props)",
22 "12. remove link", 22 "12. remove link",
23 - "" 23 + "13. remove host (10.0.0.17)"
24 ] 24 ]
25 } 25 }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 "payload": { 3 "payload": {
4 "id": "of:0000ffffffffff04", 4 "id": "of:0000ffffffffff04",
5 "type": "roadm", 5 "type": "roadm",
6 - "online": false, 6 + "online": true,
7 "labels": [ 7 "labels": [
8 "0000ffffffffff04", 8 "0000ffffffffff04",
9 "FF:FF:FF:FF:FF:04", 9 "FF:FF:FF:FF:FF:04",
......
...@@ -3,15 +3,15 @@ ...@@ -3,15 +3,15 @@
3 "payload": { 3 "payload": {
4 "id": "of:0000ffffffff000A", 4 "id": "of:0000ffffffff000A",
5 "type": "switch", 5 "type": "switch",
6 - "online": false, 6 + "online": true,
7 "labels": [ 7 "labels": [
8 "0000ffffffff000A", 8 "0000ffffffff000A",
9 "FF:FF:FF:FF:00:0A", 9 "FF:FF:FF:FF:00:0A",
10 "?" 10 "?"
11 ], 11 ],
12 "metaUi": { 12 "metaUi": {
13 - "Zx": 832, 13 + "x": 832,
14 - "Zy": 223 14 + "y": 223
15 } 15 }
16 } 16 }
17 } 17 }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 "payload": { 3 "payload": {
4 "id": "of:0000ffffffff0001", 4 "id": "of:0000ffffffff0001",
5 "type": "switch", 5 "type": "switch",
6 - "online": false, 6 + "online": true,
7 "labels": [ 7 "labels": [
8 "0000ffffffff0001", 8 "0000ffffffff0001",
9 "FF:FF:FF:FF:00:01", 9 "FF:FF:FF:FF:00:01",
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 "payload": { 3 "payload": {
4 "id": "of:0000ffffffffff01", 4 "id": "of:0000ffffffffff01",
5 "type": "roadm", 5 "type": "roadm",
6 - "online": false, 6 + "online": true,
7 "labels": [ 7 "labels": [
8 "0000ffffffffff01", 8 "0000ffffffffff01",
9 "FF:FF:FF:FF:FF:01", 9 "FF:FF:FF:FF:FF:01",
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 "payload": { 3 "payload": {
4 "id": "of:0000ffffffff0004", 4 "id": "of:0000ffffffff0004",
5 "type": "switch", 5 "type": "switch",
6 - "online": false, 6 + "online": true,
7 "labels": [ 7 "labels": [
8 "0000ffffffff0004", 8 "0000ffffffff0004",
9 "FF:FF:FF:FF:00:04", 9 "FF:FF:FF:FF:00:04",
......
...@@ -3,15 +3,15 @@ ...@@ -3,15 +3,15 @@
3 "payload": { 3 "payload": {
4 "id": "of:0000ffffffffff0A", 4 "id": "of:0000ffffffffff0A",
5 "type": "roadm", 5 "type": "roadm",
6 - "online": false, 6 + "online": true,
7 "labels": [ 7 "labels": [
8 "0000ffffffffff0A", 8 "0000ffffffffff0A",
9 "FF:FF:FF:FF:FF:0A", 9 "FF:FF:FF:FF:FF:0A",
10 "?" 10 "?"
11 ], 11 ],
12 "metaUi": { 12 "metaUi": {
13 - "Zx": 840, 13 + "x": 840,
14 - "Zy": 290 14 + "y": 290
15 } 15 }
16 } 16 }
17 } 17 }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 "payload": { 3 "payload": {
4 "id": "of:0000ffffffffff09", 4 "id": "of:0000ffffffffff09",
5 "type": "roadm", 5 "type": "roadm",
6 - "online": false, 6 + "online": true,
7 "labels": [ 7 "labels": [
8 "0000ffffffffff09", 8 "0000ffffffffff09",
9 "FF:FF:FF:FF:FF:09", 9 "FF:FF:FF:FF:FF:09",
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 + "id": "of:0000ffffffffff02/20-of:0000ffffffffff05/10",
5 + "type": "optical",
6 + "linkWidth": 4,
4 "src": "of:0000ffffffffff02", 7 "src": "of:0000ffffffffff02",
5 "srcPort": "20", 8 "srcPort": "20",
6 "dst": "of:0000ffffffffff05", 9 "dst": "of:0000ffffffffff05",
7 "dstPort": "10", 10 "dstPort": "10",
8 - "type": "optical",
9 - "linkWidth": 6,
10 "props" : { 11 "props" : {
11 "BW": "80 G" 12 "BW": "80 G"
12 } 13 }
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 + "id": "of:0000ffffffff000A/2-of:0000ffffffffff0A/1",
5 + "type": "optical",
6 + "linkWidth": 2,
4 "src": "of:0000ffffffff000A", 7 "src": "of:0000ffffffff000A",
5 "srcPort": "2", 8 "srcPort": "2",
6 "dst": "of:0000ffffffffff0A", 9 "dst": "of:0000ffffffffff0A",
7 "dstPort": "1", 10 "dstPort": "1",
8 - "type": "optical",
9 - "linkWidth": 2,
10 "props" : { 11 "props" : {
11 "BW": "100 G" 12 "BW": "100 G"
12 } 13 }
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 + "id": "of:0000ffffffffff03/10-of:0000ffffffffff02/10",
5 + "type": "optical",
6 + "linkWidth": 2,
4 "src": "of:0000ffffffffff03", 7 "src": "of:0000ffffffffff03",
5 "srcPort": "10", 8 "srcPort": "10",
6 "dst": "of:0000ffffffffff02", 9 "dst": "of:0000ffffffffff02",
7 "dstPort": "10", 10 "dstPort": "10",
8 - "type": "optical",
9 - "linkWidth": 2,
10 "props" : { 11 "props" : {
11 "BW": "70 G" 12 "BW": "70 G"
12 } 13 }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 "payload": { 3 "payload": {
4 "id": "of:0000ffffffffff08", 4 "id": "of:0000ffffffffff08",
5 "type": "roadm", 5 "type": "roadm",
6 - "online": false, 6 + "online": true,
7 "labels": [ 7 "labels": [
8 "0000ffffffffff08", 8 "0000ffffffffff08",
9 "FF:FF:FF:FF:FF:08", 9 "FF:FF:FF:FF:FF:08",
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 + "id": "of:0000ffffffffff07/21-of:0000ffffffffff05/20",
5 + "type": "optical",
6 + "linkWidth": 2,
4 "src": "of:0000ffffffffff07", 7 "src": "of:0000ffffffffff07",
5 "srcPort": "21", 8 "srcPort": "21",
6 "dst": "of:0000ffffffffff05", 9 "dst": "of:0000ffffffffff05",
7 "dstPort": "20", 10 "dstPort": "20",
8 - "type": "optical",
9 - "linkWidth": 2,
10 "props" : { 11 "props" : {
11 "BW": "70 G" 12 "BW": "70 G"
12 } 13 }
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 + "id": "of:0000ffffffff0001/2-of:0000ffffffffff01/1",
5 + "type": "optical",
6 + "linkWidth": 2,
4 "src": "of:0000ffffffff0001", 7 "src": "of:0000ffffffff0001",
5 "srcPort": "2", 8 "srcPort": "2",
6 "dst": "of:0000ffffffffff01", 9 "dst": "of:0000ffffffffff01",
7 "dstPort": "1", 10 "dstPort": "1",
8 - "type": "optical",
9 - "linkWidth": 2,
10 "props" : { 11 "props" : {
11 "BW": "70 G" 12 "BW": "70 G"
12 } 13 }
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 + "id": "of:0000ffffffffff09/20-of:0000ffffffffff0A/20",
5 + "type": "optical",
6 + "linkWidth": 2,
4 "src": "of:0000ffffffffff09", 7 "src": "of:0000ffffffffff09",
5 "srcPort": "20", 8 "srcPort": "20",
6 "dst": "of:0000ffffffffff0A", 9 "dst": "of:0000ffffffffff0A",
7 "dstPort": "20", 10 "dstPort": "20",
8 - "type": "optical",
9 - "linkWidth": 2,
10 "props" : { 11 "props" : {
11 "BW": "70 G" 12 "BW": "70 G"
12 } 13 }
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 - "src": "of:0000ffffffffff06", 4 + "id": "of:0000ffffffffff07/30-of:0000ffffffffff08/20",
5 - "srcPort": "20",
6 - "dst": "of:0000ffffffffff05",
7 - "dstPort": "30",
8 "type": "optical", 5 "type": "optical",
9 - "linkWidth": 6, 6 + "linkWidth": 4,
7 + "src": "of:0000ffffffffff07",
8 + "srcPort": "30",
9 + "dst": "of:0000ffffffffff08",
10 + "dstPort": "20",
10 "props" : { 11 "props" : {
11 "BW": "70 G" 12 "BW": "70 G"
12 } 13 }
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 - "src": "of:0000ffffffffff07", 4 + "id": "of:0000ffffffffff02/10-of:0000ffffffffff01/10",
5 - "srcPort": "30",
6 - "dst": "of:0000ffffffffff08",
7 - "dstPort": "20",
8 "type": "optical", 5 "type": "optical",
9 - "linkWidth": 6, 6 + "linkWidth": 2,
7 + "src": "of:0000ffffffffff02",
8 + "srcPort": "10",
9 + "dst": "of:0000ffffffffff01",
10 + "dstPort": "10",
10 "props" : { 11 "props" : {
11 "BW": "70 G" 12 "BW": "70 G"
12 } 13 }
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 - "src": "of:0000ffffffffff03", 4 + "id": "of:0000ffffffffff04/27-of:0000ffffffffff08/10",
5 - "srcPort": "20", 5 + "src": "of:0000ffffffffff04",
6 - "dst": "of:0000ffffffffff06", 6 + "srcPort": "27",
7 + "dst": "of:0000ffffffffff08",
7 "dstPort": "10", 8 "dstPort": "10",
8 "type": "optical", 9 "type": "optical",
9 "linkWidth": 2, 10 "linkWidth": 2,
10 "props" : { 11 "props" : {
11 - "BW": "70 G" 12 + "BW": "30 G"
12 } 13 }
13 } 14 }
14 } 15 }
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 - "src": "of:0000ffffffffff02", 4 + "id": "of:0000ffffffff0003/2-of:0000ffffffffff03/1",
5 - "srcPort": "10",
6 - "dst": "of:0000ffffffffff01",
7 - "dstPort": "10",
8 "type": "optical", 5 "type": "optical",
9 "linkWidth": 2, 6 "linkWidth": 2,
7 + "src": "of:0000ffffffff0003",
8 + "srcPort": "2",
9 + "dst": "of:0000ffffffffff03",
10 + "dstPort": "1",
10 "props" : { 11 "props" : {
11 "BW": "70 G" 12 "BW": "70 G"
12 } 13 }
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 + "id": "of:0000ffffffffff09/1-of:0000ffffffff0009/2",
5 + "type": "optical",
6 + "linkWidth": 2,
4 "src": "of:0000ffffffffff09", 7 "src": "of:0000ffffffffff09",
5 "srcPort": "1", 8 "srcPort": "1",
6 "dst": "of:0000ffffffff0009", 9 "dst": "of:0000ffffffff0009",
7 "dstPort": "2", 10 "dstPort": "2",
8 - "type": "optical",
9 - "linkWidth": 2,
10 "props" : { 11 "props" : {
11 "BW": "70 G" 12 "BW": "70 G"
12 } 13 }
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 + "id": "of:0000ffffffffff03/30-of:0000ffffffffff04/10",
5 + "type": "optical",
6 + "linkWidth": 2,
4 "src": "of:0000ffffffffff03", 7 "src": "of:0000ffffffffff03",
5 "srcPort": "30", 8 "srcPort": "30",
6 "dst": "of:0000ffffffffff04", 9 "dst": "of:0000ffffffffff04",
7 "dstPort": "10", 10 "dstPort": "10",
8 - "type": "optical",
9 - "linkWidth": 2,
10 "props" : { 11 "props" : {
11 "BW": "70 G" 12 "BW": "70 G"
12 } 13 }
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 + "id": "of:0000ffffffffff07/20-of:0000ffffffffff09/10",
5 + "type": "optical",
6 + "linkWidth": 2,
4 "src": "of:0000ffffffffff07", 7 "src": "of:0000ffffffffff07",
5 "srcPort": "20", 8 "srcPort": "20",
6 "dst": "of:0000ffffffffff09", 9 "dst": "of:0000ffffffffff09",
7 "dstPort": "10", 10 "dstPort": "10",
8 - "type": "optical",
9 - "linkWidth": 2,
10 "props" : { 11 "props" : {
11 "BW": "70 G" 12 "BW": "70 G"
12 } 13 }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 "payload": { 3 "payload": {
4 "id": "of:0000ffffffffff03", 4 "id": "of:0000ffffffffff03",
5 "type": "roadm", 5 "type": "roadm",
6 - "online": false, 6 + "online": true,
7 "labels": [ 7 "labels": [
8 "0000ffffffffff03", 8 "0000ffffffffff03",
9 "FF:FF:FF:FF:FF:03", 9 "FF:FF:FF:FF:FF:03",
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 + "id": "of:0000ffffffffff0A/10-of:0000ffffffffff08/30",
5 + "type": "optical",
6 + "linkWidth": 4,
4 "src": "of:0000ffffffffff0A", 7 "src": "of:0000ffffffffff0A",
5 "srcPort": "10", 8 "srcPort": "10",
6 "dst": "of:0000ffffffffff08", 9 "dst": "of:0000ffffffffff08",
7 "dstPort": "30", 10 "dstPort": "30",
8 - "type": "optical",
9 - "linkWidth": 6,
10 "props" : { 11 "props" : {
11 "BW": "70 G" 12 "BW": "70 G"
12 } 13 }
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 + "id": "of:0000ffffffff0004/2-of:0000ffffffffff04/1",
5 + "type": "optical",
6 + "linkWidth": 2,
4 "src": "of:0000ffffffff0004", 7 "src": "of:0000ffffffff0004",
5 "srcPort": "2", 8 "srcPort": "2",
6 "dst": "of:0000ffffffffff04", 9 "dst": "of:0000ffffffffff04",
7 "dstPort": "1", 10 "dstPort": "1",
8 - "type": "optical",
9 - "linkWidth": 2,
10 "props" : { 11 "props" : {
11 "BW": "70 G" 12 "BW": "70 G"
12 } 13 }
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 + "id": "of:0000ffffffffff07/1-of:0000ffffffff0007/2",
5 + "type": "optical",
6 + "linkWidth": 2,
4 "src": "of:0000ffffffffff07", 7 "src": "of:0000ffffffffff07",
5 "srcPort": "1", 8 "srcPort": "1",
6 "dst": "of:0000ffffffff0007", 9 "dst": "of:0000ffffffff0007",
7 "dstPort": "2", 10 "dstPort": "2",
8 - "type": "optical",
9 - "linkWidth": 2,
10 "props" : { 11 "props" : {
11 "BW": "70 G" 12 "BW": "70 G"
12 } 13 }
......
1 { 1 {
2 - "event": "addLink", 2 + "event": "updateDevice",
3 "payload": { 3 "payload": {
4 - "src": "of:0000ffffffff0003", 4 + "id": "of:0000ffffffffff06",
5 - "srcPort": "2", 5 + "type": "roadm",
6 - "dst": "of:0000ffffffffff03", 6 + "online": true,
7 - "dstPort": "1", 7 + "labels": [
8 - "type": "optical", 8 + "0000ffffffffff06",
9 - "linkWidth": 2, 9 + "FF:FF:FF:FF:FF:06",
10 - "props" : { 10 + "?"
11 - "BW": "70 G" 11 + ],
12 + "metaUi": {
13 + "x": 336,
14 + "y": 254
12 } 15 }
13 } 16 }
14 } 17 }
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 + "id": "of:0000ffffffffff06/20-of:0000ffffffffff05/30",
4 "src": "of:0000ffffffffff06", 5 "src": "of:0000ffffffffff06",
5 - "srcPort": "30", 6 + "srcPort": "20",
6 - "dst": "of:0000ffffffffff08", 7 + "dst": "of:0000ffffffffff05",
7 - "dstPort": "10", 8 + "dstPort": "30",
8 "type": "optical", 9 "type": "optical",
9 - "linkWidth": 6, 10 + "linkWidth": 4,
10 "props" : { 11 "props" : {
11 "BW": "70 G" 12 "BW": "70 G"
12 } 13 }
......
1 { 1 {
2 "event": "addLink", 2 "event": "addLink",
3 "payload": { 3 "payload": {
4 - "src": "of:0000ffffffffff04", 4 + "id": "of:0000ffffffffff03/20-of:0000ffffffffff06/10",
5 - "srcPort": "27",
6 - "dst": "of:0000ffffffffff08",
7 - "dstPort": "10",
8 "type": "optical", 5 "type": "optical",
9 "linkWidth": 2, 6 "linkWidth": 2,
7 + "src": "of:0000ffffffffff03",
8 + "srcPort": "20",
9 + "dst": "of:0000ffffffffff06",
10 + "dstPort": "10",
10 "props" : { 11 "props" : {
11 - "BW": "30 G" 12 + "BW": "70 G"
12 } 13 }
13 } 14 }
14 } 15 }
......
1 +{
2 + "event": "addLink",
3 + "payload": {
4 + "id": "of:0000ffffffffff06/30-of:0000ffffffffff08/10",
5 + "type": "optical",
6 + "linkWidth": 4,
7 + "src": "of:0000ffffffffff06",
8 + "srcPort": "30",
9 + "dst": "of:0000ffffffffff08",
10 + "dstPort": "10",
11 + "props" : {
12 + "BW": "70 G"
13 + }
14 + }
15 +}
1 +{
2 + "event": "updateDevice",
3 + "payload": {
4 + "id": "of:0000ffffffffff08",
5 + "type": "roadm",
6 + "online": false,
7 + "labels": [
8 + "0000ffffffffff08",
9 + "FF:FF:FF:FF:FF:08",
10 + "?"
11 + ],
12 + "metaUi": {
13 + "x": 539,
14 + "y": 186
15 + }
16 + }
17 +}
1 +{
2 + "event": "removeLink",
3 + "payload": {
4 + "id": "of:0000ffffffffff07/30-of:0000ffffffffff08/20",
5 + "type": "optical",
6 + "linkWidth": 4,
7 + "src": "of:0000ffffffffff07",
8 + "srcPort": "30",
9 + "dst": "of:0000ffffffffff08",
10 + "dstPort": "20",
11 + "props" : {
12 + "BW": "70 G"
13 + }
14 + }
15 +}
1 +{
2 + "event": "removeLink",
3 + "payload": {
4 + "id": "of:0000ffffffffff04/27-of:0000ffffffffff08/10",
5 + "src": "of:0000ffffffffff04",
6 + "srcPort": "27",
7 + "dst": "of:0000ffffffffff08",
8 + "dstPort": "10",
9 + "type": "optical",
10 + "linkWidth": 2,
11 + "props" : {
12 + "BW": "30 G"
13 + }
14 + }
15 +}
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 "payload": { 3 "payload": {
4 "id": "of:0000ffffffff0007", 4 "id": "of:0000ffffffff0007",
5 "type": "switch", 5 "type": "switch",
6 - "online": false, 6 + "online": true,
7 "labels": [ 7 "labels": [
8 "0000ffffffff0007", 8 "0000ffffffff0007",
9 "FF:FF:FF:FF:00:07", 9 "FF:FF:FF:FF:00:07",
......
1 +{
2 + "event": "removeLink",
3 + "payload": {
4 + "id": "of:0000ffffffffff0A/10-of:0000ffffffffff08/30",
5 + "type": "optical",
6 + "linkWidth": 4,
7 + "src": "of:0000ffffffffff0A",
8 + "srcPort": "10",
9 + "dst": "of:0000ffffffffff08",
10 + "dstPort": "30",
11 + "props" : {
12 + "BW": "70 G"
13 + }
14 + }
15 +}
1 +{
2 + "event": "removeLink",
3 + "payload": {
4 + "id": "of:0000ffffffffff06/30-of:0000ffffffffff08/10",
5 + "type": "optical",
6 + "linkWidth": 4,
7 + "src": "of:0000ffffffffff06",
8 + "srcPort": "30",
9 + "dst": "of:0000ffffffffff08",
10 + "dstPort": "10",
11 + "props" : {
12 + "BW": "70 G"
13 + }
14 + }
15 +}
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 "payload": { 3 "payload": {
4 "id": "of:0000ffffffff0009", 4 "id": "of:0000ffffffff0009",
5 "type": "switch", 5 "type": "switch",
6 - "online": false, 6 + "online": true,
7 "labels": [ 7 "labels": [
8 "0000ffffffff0009", 8 "0000ffffffff0009",
9 "FF:FF:FF:FF:00:09", 9 "FF:FF:FF:FF:00:09",
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 "payload": { 3 "payload": {
4 "id": "of:0000ffffffffff02", 4 "id": "of:0000ffffffffff02",
5 "type": "roadm", 5 "type": "roadm",
6 - "online": false, 6 + "online": true,
7 "labels": [ 7 "labels": [
8 "0000ffffffffff02", 8 "0000ffffffffff02",
9 "FF:FF:FF:FF:FF:02", 9 "FF:FF:FF:FF:FF:02",
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 "payload": { 3 "payload": {
4 "id": "of:0000ffffffff0003", 4 "id": "of:0000ffffffff0003",
5 "type": "switch", 5 "type": "switch",
6 - "online": false, 6 + "online": true,
7 "labels": [ 7 "labels": [
8 "0000ffffffff0003", 8 "0000ffffffff0003",
9 "FF:FF:FF:FF:00:03", 9 "FF:FF:FF:FF:00:03",
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 "payload": { 3 "payload": {
4 "id": "of:0000ffffffffff07", 4 "id": "of:0000ffffffffff07",
5 "type": "roadm", 5 "type": "roadm",
6 - "online": false, 6 + "online": true,
7 "labels": [ 7 "labels": [
8 "0000ffffffffff07", 8 "0000ffffffffff07",
9 "FF:FF:FF:FF:FF:07", 9 "FF:FF:FF:FF:FF:07",
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 "payload": { 3 "payload": {
4 "id": "of:0000ffffffffff05", 4 "id": "of:0000ffffffffff05",
5 "type": "roadm", 5 "type": "roadm",
6 - "online": false, 6 + "online": true,
7 "labels": [ 7 "labels": [
8 "0000ffffffffff05", 8 "0000ffffffffff05",
9 "FF:FF:FF:FF:FF:05", 9 "FF:FF:FF:FF:FF:05",
......
...@@ -6,5 +6,15 @@ ...@@ -6,5 +6,15 @@
6 "title": "Startup Scenario", 6 "title": "Startup Scenario",
7 "params": { 7 "params": {
8 "lastAuto": 32 8 "lastAuto": 32
9 - } 9 + },
10 + "description": [
11 + "Loads 16 devices (10 optical, 6 packet)",
12 + " and their associated links.",
13 + "",
14 + "Press 'S' to load initial events.",
15 + "",
16 + "Press spacebar to complete the scenario...",
17 + " * 4 events - device online, add 3 links",
18 + " * 5 events - device offline, remove 4 links"
19 + ]
10 } 20 }
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -3,6 +3,11 @@ ...@@ -3,6 +3,11 @@
3 "id": "of:0000000000000001", 3 "id": "of:0000000000000001",
4 "type": "roadm", 4 "type": "roadm",
5 "propOrder": [ "name", "type", "-", "dpid", "latitude", "longitude", "allowed" ], 5 "propOrder": [ "name", "type", "-", "dpid", "latitude", "longitude", "allowed" ],
6 + "location": {
7 + "type": "latlng",
8 + "lat": 37.6,
9 + "lng": 122.3
10 + },
6 "props": { 11 "props": {
7 "allowed": true, 12 "allowed": true,
8 "latitude": 37.6, 13 "latitude": 37.6,
......
...@@ -3,6 +3,11 @@ ...@@ -3,6 +3,11 @@
3 "id": "of:0000000000000002", 3 "id": "of:0000000000000002",
4 "type": "switch", 4 "type": "switch",
5 "propOrder": [ "name", "type", "dpid", "latitude", "longitude", "allowed" ], 5 "propOrder": [ "name", "type", "dpid", "latitude", "longitude", "allowed" ],
6 + "location": {
7 + "type": "latlng",
8 + "lat": 37.6,
9 + "lng": 122.3
10 + },
6 "props": { 11 "props": {
7 "allowed": true, 12 "allowed": true,
8 "latitude": 37.3, 13 "latitude": 37.3,
......
...@@ -3,6 +3,11 @@ ...@@ -3,6 +3,11 @@
3 "id": "of:0000000000000003", 3 "id": "of:0000000000000003",
4 "type": "switch", 4 "type": "switch",
5 "propOrder": [ "name", "type", "dpid", "latitude", "longitude", "allowed" ], 5 "propOrder": [ "name", "type", "dpid", "latitude", "longitude", "allowed" ],
6 + "location": {
7 + "type": "latlng",
8 + "lat": 33.9,
9 + "lng": 118.4
10 + },
6 "props": { 11 "props": {
7 "allowed": true, 12 "allowed": true,
8 "latitude": 33.9, 13 "latitude": 33.9,
......
...@@ -3,6 +3,11 @@ ...@@ -3,6 +3,11 @@
3 "id": "of:0000000000000004", 3 "id": "of:0000000000000004",
4 "type": "switch", 4 "type": "switch",
5 "propOrder": [ "name", "type", "dpid", "latitude", "longitude", "allowed" ], 5 "propOrder": [ "name", "type", "dpid", "latitude", "longitude", "allowed" ],
6 + "location": {
7 + "type": "latlng",
8 + "lat": 32.8,
9 + "lng": 117.1
10 + },
6 "props": { 11 "props": {
7 "allowed": true, 12 "allowed": true,
8 "latitude": 32.8, 13 "latitude": 32.8,
......
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
50 50
51 // internal state 51 // internal state
52 var views = {}, 52 var views = {},
53 + fpanels = {},
53 current = { 54 current = {
54 view: null, 55 view: null,
55 ctx: '', 56 ctx: '',
...@@ -57,7 +58,7 @@ ...@@ -57,7 +58,7 @@
57 theme: settings.theme 58 theme: settings.theme
58 }, 59 },
59 built = false, 60 built = false,
60 - errorCount = 0, 61 + buildErrors = [],
61 keyHandler = { 62 keyHandler = {
62 globalKeys: {}, 63 globalKeys: {},
63 maskedKeys: {}, 64 maskedKeys: {},
...@@ -70,7 +71,11 @@ ...@@ -70,7 +71,11 @@
70 }; 71 };
71 72
72 // DOM elements etc. 73 // DOM elements etc.
73 - var $view, 74 + // TODO: verify existence of following elements...
75 + var $view = d3.select('#view'),
76 + $floatPanels = d3.select('#floatPanels'),
77 + $alerts = d3.select('#alerts'),
78 + // note, following elements added programmatically...
74 $mastRadio; 79 $mastRadio;
75 80
76 81
...@@ -241,10 +246,22 @@ ...@@ -241,10 +246,22 @@
241 setView(view, hash, t); 246 setView(view, hash, t);
242 } 247 }
243 248
249 + function buildError(msg) {
250 + buildErrors.push(msg);
251 + }
252 +
244 function reportBuildErrors() { 253 function reportBuildErrors() {
245 traceFn('reportBuildErrors'); 254 traceFn('reportBuildErrors');
246 - // TODO: validate registered views / nav-item linkage etc. 255 + var nerr = buildErrors.length,
256 + errmsg;
257 + if (!nerr) {
247 console.log('(no build errors)'); 258 console.log('(no build errors)');
259 + } else {
260 + errmsg = 'Build errors: ' + nerr + ' found...\n\n' +
261 + buildErrors.join('\n');
262 + doAlert(errmsg);
263 + console.error(errmsg);
264 + }
248 } 265 }
249 266
250 // returns the reference if it is a function, null otherwise 267 // returns the reference if it is a function, null otherwise
...@@ -449,22 +466,20 @@ ...@@ -449,22 +466,20 @@
449 } 466 }
450 467
451 function createAlerts() { 468 function createAlerts() {
452 - var al = d3.select('#alerts') 469 + $alerts.style('display', 'block');
453 - .style('display', 'block'); 470 + $alerts.append('span')
454 - al.append('span')
455 .attr('class', 'close') 471 .attr('class', 'close')
456 .text('X') 472 .text('X')
457 .on('click', closeAlerts); 473 .on('click', closeAlerts);
458 - al.append('pre'); 474 + $alerts.append('pre');
459 - al.append('p').attr('class', 'footnote') 475 + $alerts.append('p').attr('class', 'footnote')
460 .text('Press ESCAPE to close'); 476 .text('Press ESCAPE to close');
461 alerts.open = true; 477 alerts.open = true;
462 alerts.count = 0; 478 alerts.count = 0;
463 } 479 }
464 480
465 function closeAlerts() { 481 function closeAlerts() {
466 - d3.select('#alerts') 482 + $alerts.style('display', 'none')
467 - .style('display', 'none')
468 .html(''); 483 .html('');
469 alerts.open = false; 484 alerts.open = false;
470 } 485 }
...@@ -474,7 +489,7 @@ ...@@ -474,7 +489,7 @@
474 oldContent; 489 oldContent;
475 490
476 if (alerts.count) { 491 if (alerts.count) {
477 - oldContent = d3.select('#alerts pre').html(); 492 + oldContent = $alerts.select('pre').html();
478 } 493 }
479 494
480 lines = msg.split('\n'); 495 lines = msg.split('\n');
...@@ -485,7 +500,7 @@ ...@@ -485,7 +500,7 @@
485 lines += '\n----\n' + oldContent; 500 lines += '\n----\n' + oldContent;
486 } 501 }
487 502
488 - d3.select('#alerts pre').html(lines); 503 + $alerts.select('pre').html(lines);
489 alerts.count++; 504 alerts.count++;
490 } 505 }
491 506
...@@ -691,6 +706,53 @@ ...@@ -691,6 +706,53 @@
691 libApi[libName] = api; 706 libApi[libName] = api;
692 }, 707 },
693 708
709 + // TODO: implement floating panel as a class
710 + // TODO: parameterize position (currently hard-coded to TopRight)
711 + /*
712 + * Creates div in floating panels block, with the given id.
713 + * Returns panel token used to interact with the panel
714 + */
715 + addFloatingPanel: function (id, position) {
716 + var pos = position || 'TR',
717 + el,
718 + fp;
719 +
720 + if (fpanels[id]) {
721 + buildError('Float panel with id "' + id + '" already exists.');
722 + return null;
723 + }
724 +
725 + el = $floatPanels.append('div')
726 + .attr('id', id)
727 + .attr('class', 'fpanel');
728 +
729 + fp = {
730 + id: id,
731 + el: el,
732 + pos: pos,
733 + show: function () {
734 + console.log('show pane: ' + id);
735 + el.transition().duration(750)
736 + .style('right', '20px')
737 + .style('opacity', 1);
738 + },
739 + hide: function () {
740 + console.log('hide pane: ' + id);
741 + el.transition().duration(750)
742 + .style('right', '-320px')
743 + .style('opacity', 0);
744 + },
745 + empty: function () {
746 + return el.html('');
747 + },
748 + append: function (what) {
749 + return el.append(what);
750 + }
751 + };
752 + fpanels[id] = fp;
753 + return fp;
754 + },
755 +
694 // TODO: it remains to be seen whether we keep this style of docs 756 // TODO: it remains to be seen whether we keep this style of docs
695 /** @api ui addView( vid, nid, cb ) 757 /** @api ui addView( vid, nid, cb )
696 * Adds a view to the UI. 758 * Adds a view to the UI.
...@@ -782,7 +844,6 @@ ...@@ -782,7 +844,6 @@
782 } 844 }
783 built = true; 845 built = true;
784 846
785 - $view = d3.select('#view');
786 $mastRadio = d3.select('#mastRadio'); 847 $mastRadio = d3.select('#mastRadio');
787 848
788 $(window).on('hashchange', hash); 849 $(window).on('hashchange', hash);
......
...@@ -96,3 +96,45 @@ ...@@ -96,3 +96,45 @@
96 fill: white; 96 fill: white;
97 stroke: red; 97 stroke: red;
98 } 98 }
99 +
100 +
101 +/* detail topo-detail pane */
102 +
103 +#topo-detail {
104 +/* gets base CSS from .fpanel in floatPanel.css */
105 +}
106 +
107 +
108 +#topo-detail h2 {
109 + margin: 8px 4px;
110 + color: black;
111 + vertical-align: middle;
112 +}
113 +
114 +#topo-detail h2 img {
115 + height: 32px;
116 + padding-right: 8px;
117 + vertical-align: middle;
118 +}
119 +
120 +#topo-detail p, table {
121 + margin: 4px 4px;
122 +}
123 +
124 +#topo-detail td.label {
125 + font-style: italic;
126 + color: #777;
127 + padding-right: 12px;
128 +}
129 +
130 +#topo-detail td.value {
131 +
132 +}
133 +
134 +#topo-detail hr {
135 + height: 1px;
136 + color: #ccc;
137 + background-color: #ccc;
138 + border: 0;
139 +}
140 +
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
30 // configuration data 30 // configuration data
31 var config = { 31 var config = {
32 useLiveData: true, 32 useLiveData: true,
33 + fnTrace: true,
33 debugOn: false, 34 debugOn: false,
34 debug: { 35 debug: {
35 showNodeXY: true, 36 showNodeXY: true,
...@@ -151,7 +152,7 @@ ...@@ -151,7 +152,7 @@
151 webSock, 152 webSock,
152 deviceLabelIndex = 0, 153 deviceLabelIndex = 0,
153 hostLabelIndex = 0, 154 hostLabelIndex = 0,
154 - 155 + detailPane,
155 selectOrder = [], 156 selectOrder = [],
156 selections = {}, 157 selections = {},
157 158
...@@ -180,12 +181,21 @@ ...@@ -180,12 +181,21 @@
180 return config.debugOn && config.debug[what]; 181 return config.debugOn && config.debug[what];
181 } 182 }
182 183
184 + function fnTrace(msg, id) {
185 + if (config.fnTrace) {
186 + console.log('FN: ' + msg + ' [' + id + ']');
187 + }
188 + }
183 189
184 // ============================== 190 // ==============================
185 // Key Callbacks 191 // Key Callbacks
186 192
187 function testMe(view) { 193 function testMe(view) {
188 view.alert('test'); 194 view.alert('test');
195 + detailPane.show();
196 + setTimeout(function () {
197 + detailPane.hide();
198 + }, 3000);
189 } 199 }
190 200
191 function abortIfLive() { 201 function abortIfLive() {
...@@ -220,7 +230,7 @@ ...@@ -220,7 +230,7 @@
220 var v = scenario.view, 230 var v = scenario.view,
221 frame; 231 frame;
222 if (stack.length === 0) { 232 if (stack.length === 0) {
223 - v.alert('Error:\n\nNo event #' + evn + ' found.'); 233 + v.alert('Oops!\n\nNo event #' + evn + ' found.');
224 return; 234 return;
225 } 235 }
226 frame = stack.shift(); 236 frame = stack.shift();
...@@ -279,14 +289,6 @@ ...@@ -279,14 +289,6 @@
279 view.alert('unpin() callback') 289 view.alert('unpin() callback')
280 } 290 }
281 291
282 - function requestPath(view) {
283 - var payload = {
284 - one: selections[selectOrder[0]].obj.id,
285 - two: selections[selectOrder[1]].obj.id
286 - }
287 - sendMessage('requestPath', payload);
288 - }
289 -
290 // ============================== 292 // ==============================
291 // Radio Button Callbacks 293 // Radio Button Callbacks
292 294
...@@ -334,6 +336,7 @@ ...@@ -334,6 +336,7 @@
334 function logicError(msg) { 336 function logicError(msg) {
335 // TODO, report logic error to server, via websock, so it can be logged 337 // TODO, report logic error to server, via websock, so it can be logged
336 network.view.alert('Logic Error:\n\n' + msg); 338 network.view.alert('Logic Error:\n\n' + msg);
339 + console.warn(msg);
337 } 340 }
338 341
339 var eventDispatch = { 342 var eventDispatch = {
...@@ -345,11 +348,13 @@ ...@@ -345,11 +348,13 @@
345 updateHost: updateHost, 348 updateHost: updateHost,
346 removeDevice: stillToImplement, 349 removeDevice: stillToImplement,
347 removeLink: removeLink, 350 removeLink: removeLink,
348 - removeHost: stillToImplement, 351 + removeHost: removeHost,
352 + showDetails: showDetails,
349 showPath: showPath 353 showPath: showPath
350 }; 354 };
351 355
352 function addDevice(data) { 356 function addDevice(data) {
357 + fnTrace('addDevice', data.payload.id);
353 var device = data.payload, 358 var device = data.payload,
354 nodeData = createDeviceNode(device); 359 nodeData = createDeviceNode(device);
355 network.nodes.push(nodeData); 360 network.nodes.push(nodeData);
...@@ -359,6 +364,7 @@ ...@@ -359,6 +364,7 @@
359 } 364 }
360 365
361 function addLink(data) { 366 function addLink(data) {
367 + fnTrace('addLink', data.payload.id);
362 var link = data.payload, 368 var link = data.payload,
363 lnk = createLink(link); 369 lnk = createLink(link);
364 if (lnk) { 370 if (lnk) {
...@@ -370,6 +376,7 @@ ...@@ -370,6 +376,7 @@
370 } 376 }
371 377
372 function addHost(data) { 378 function addHost(data) {
379 + fnTrace('addHost', data.payload.id);
373 var host = data.payload, 380 var host = data.payload,
374 node = createHostNode(host), 381 node = createHostNode(host),
375 lnk; 382 lnk;
...@@ -379,6 +386,7 @@ ...@@ -379,6 +386,7 @@
379 386
380 lnk = createHostLink(host); 387 lnk = createHostLink(host);
381 if (lnk) { 388 if (lnk) {
389 + node.linkData = lnk; // cache ref on its host
382 network.links.push(lnk); 390 network.links.push(lnk);
383 network.lookup[host.ingress] = lnk; 391 network.lookup[host.ingress] = lnk;
384 network.lookup[host.egress] = lnk; 392 network.lookup[host.egress] = lnk;
...@@ -387,7 +395,9 @@ ...@@ -387,7 +395,9 @@
387 network.force.start(); 395 network.force.start();
388 } 396 }
389 397
398 + // TODO: fold updateX(...) methods into one base method; remove duplication
390 function updateDevice(data) { 399 function updateDevice(data) {
400 + fnTrace('updateDevice', data.payload.id);
391 var device = data.payload, 401 var device = data.payload,
392 id = device.id, 402 id = device.id,
393 nodeData = network.lookup[id]; 403 nodeData = network.lookup[id];
...@@ -400,6 +410,7 @@ ...@@ -400,6 +410,7 @@
400 } 410 }
401 411
402 function updateLink(data) { 412 function updateLink(data) {
413 + fnTrace('updateLink', data.payload.id);
403 var link = data.payload, 414 var link = data.payload,
404 id = link.id, 415 id = link.id,
405 linkData = network.lookup[id]; 416 linkData = network.lookup[id];
...@@ -412,6 +423,7 @@ ...@@ -412,6 +423,7 @@
412 } 423 }
413 424
414 function updateHost(data) { 425 function updateHost(data) {
426 + fnTrace('updateHost', data.payload.id);
415 var host = data.payload, 427 var host = data.payload,
416 id = host.id, 428 id = host.id,
417 hostData = network.lookup[id]; 429 hostData = network.lookup[id];
...@@ -423,7 +435,9 @@ ...@@ -423,7 +435,9 @@
423 } 435 }
424 } 436 }
425 437
438 + // TODO: fold removeX(...) methods into base method - remove dup code
426 function removeLink(data) { 439 function removeLink(data) {
440 + fnTrace('removeLink', data.payload.id);
427 var link = data.payload, 441 var link = data.payload,
428 id = link.id, 442 id = link.id,
429 linkData = network.lookup[id]; 443 linkData = network.lookup[id];
...@@ -434,7 +448,26 @@ ...@@ -434,7 +448,26 @@
434 } 448 }
435 } 449 }
436 450
451 + function removeHost(data) {
452 + fnTrace('removeHost', data.payload.id);
453 + var host = data.payload,
454 + id = host.id,
455 + hostData = network.lookup[id];
456 + if (hostData) {
457 + removeHostElement(hostData);
458 + } else {
459 + logicError('removeHost lookup fail. ID = "' + id + '"');
460 + }
461 + }
462 +
463 + function showDetails(data) {
464 + fnTrace('showDetails', data.payload.id);
465 + populateDetails(data.payload);
466 + detailPane.show();
467 + }
468 +
437 function showPath(data) { 469 function showPath(data) {
470 + fnTrace('showPath', data.payload.id);
438 var links = data.payload.links, 471 var links = data.payload.links,
439 s = [ data.event + "\n" + links.length ]; 472 s = [ data.event + "\n" + links.length ];
440 links.forEach(function (d, i) { 473 links.forEach(function (d, i) {
...@@ -470,6 +503,32 @@ ...@@ -470,6 +503,32 @@
470 } 503 }
471 504
472 // ============================== 505 // ==============================
506 + // Out-going messages...
507 +
508 + function getSel(idx) {
509 + return selections[selectOrder[idx]];
510 + }
511 +
512 + // for now, just a host-to-host intent, (and implicit start-monitoring)
513 + function requestPath() {
514 + var payload = {
515 + one: getSel(0).obj.id,
516 + two: getSel(1).obj.id
517 + };
518 + sendMessage('requestPath', payload);
519 + }
520 +
521 + // request details for the selected element
522 + function requestDetails() {
523 + var data = getSel(0).obj,
524 + payload = {
525 + id: data.id,
526 + class: data.class
527 + };
528 + sendMessage('requestDetails', payload);
529 + }
530 +
531 + // ==============================
473 // force layout modification functions 532 // force layout modification functions
474 533
475 function translate(x, y) { 534 function translate(x, y) {
...@@ -589,19 +648,18 @@ ...@@ -589,19 +648,18 @@
589 //link .foo() .bar() ... 648 //link .foo() .bar() ...
590 649
591 // operate on exiting links: 650 // operate on exiting links:
592 - // TODO: better transition (longer as a dashed, grey line)
593 link.exit() 651 link.exit()
594 .attr({ 652 .attr({
595 'stroke-dasharray': '3, 3' 653 'stroke-dasharray': '3, 3'
596 }) 654 })
597 .style('opacity', 0.4) 655 .style('opacity', 0.4)
598 .transition() 656 .transition()
599 - .duration(2000) 657 + .duration(1500)
600 .attr({ 658 .attr({
601 'stroke-dasharray': '3, 12' 659 'stroke-dasharray': '3, 12'
602 }) 660 })
603 .transition() 661 .transition()
604 - .duration(1000) 662 + .duration(500)
605 .style('opacity', 0.0) 663 .style('opacity', 0.0)
606 .remove(); 664 .remove();
607 } 665 }
...@@ -855,17 +913,37 @@ ...@@ -855,17 +913,37 @@
855 //node .foo() .bar() ... 913 //node .foo() .bar() ...
856 914
857 // operate on exiting nodes: 915 // operate on exiting nodes:
858 - // TODO: figure out how to remove the node 'g' AND its children 916 + // Note that the node is removed after 2 seconds.
859 - node.exit() 917 + // Sub element animations should be shorter than 2 seconds.
918 + var exiting = node.exit()
860 .transition() 919 .transition()
861 - .duration(750) 920 + .duration(2000)
862 - .attr({ 921 + .style('opacity', 0)
863 - opacity: 0,
864 - cx: 0,
865 - cy: 0,
866 - r: 0
867 - })
868 .remove(); 922 .remove();
923 +
924 + // host node exits....
925 + exiting.filter('.host').each(function (d) {
926 + var node = d3.select(this);
927 +
928 + node.select('text')
929 + .style('opacity', 0.5)
930 + .transition()
931 + .duration(1000)
932 + .style('opacity', 0);
933 + // note, leave <g>.remove to remove this element
934 +
935 + node.select('circle')
936 + .style('stroke-fill', '#555')
937 + .style('fill', '#888')
938 + .style('opacity', 0.5)
939 + .transition()
940 + .duration(1500)
941 + .attr('r', 0);
942 + // note, leave <g>.remove to remove this element
943 +
944 + });
945 +
946 + // TODO: device node exits
869 } 947 }
870 948
871 function find(id, array) { 949 function find(id, array) {
...@@ -882,12 +960,27 @@ ...@@ -882,12 +960,27 @@
882 delete network.lookup[linkData.id]; 960 delete network.lookup[linkData.id];
883 // remove from links array 961 // remove from links array
884 var idx = find(linkData.id, network.links); 962 var idx = find(linkData.id, network.links);
885 - 963 + network.links.splice(idx, 1);
886 - network.links.splice(linkData.index, 1);
887 // remove from SVG 964 // remove from SVG
888 updateLinks(); 965 updateLinks();
966 + network.force.resume();
889 } 967 }
890 968
969 + function removeHostElement(hostData) {
970 + // first, remove associated hostLink...
971 + removeLinkElement(hostData.linkData);
972 +
973 + // remove from lookup cache
974 + delete network.lookup[hostData.id];
975 + // remove from nodes array
976 + var idx = find(hostData.id, network.nodes);
977 + network.nodes.splice(idx, 1);
978 + // remove from SVG
979 + updateNodes();
980 + network.force.resume();
981 + }
982 +
983 +
891 function tick() { 984 function tick() {
892 node.attr({ 985 node.attr({
893 transform: function (d) { return translate(d.x, d.y); } 986 transform: function (d) { return translate(d.x, d.y); }
...@@ -951,6 +1044,8 @@ ...@@ -951,6 +1044,8 @@
951 1044
952 var sid = 0; 1045 var sid = 0;
953 1046
1047 + // TODO: use cache of pending messages (key = sid) to reconcile responses
1048 +
954 function sendMessage(evType, payload) { 1049 function sendMessage(evType, payload) {
955 var toSend = { 1050 var toSend = {
956 event: evType, 1051 event: evType,
...@@ -969,7 +1064,6 @@ ...@@ -969,7 +1064,6 @@
969 wsTrace('rx', msg); 1064 wsTrace('rx', msg);
970 } 1065 }
971 function wsTrace(rxtx, msg) { 1066 function wsTrace(rxtx, msg) {
972 -
973 console.log('[' + rxtx + '] ' + msg); 1067 console.log('[' + rxtx + '] ' + msg);
974 // TODO: integrate with trace view 1068 // TODO: integrate with trace view
975 //if (trace) { 1069 //if (trace) {
...@@ -998,7 +1092,7 @@ ...@@ -998,7 +1092,7 @@
998 1092
999 if (meta && n.classed('selected')) { 1093 if (meta && n.classed('selected')) {
1000 deselectObject(obj.id); 1094 deselectObject(obj.id);
1001 - //flyinPane(null); 1095 + updateDetailPane();
1002 return; 1096 return;
1003 } 1097 }
1004 1098
...@@ -1010,17 +1104,16 @@ ...@@ -1010,17 +1104,16 @@
1010 selectOrder.push(obj.id); 1104 selectOrder.push(obj.id);
1011 1105
1012 n.classed('selected', true); 1106 n.classed('selected', true);
1013 - //flyinPane(obj); 1107 + updateDetailPane();
1014 } 1108 }
1015 1109
1016 function deselectObject(id) { 1110 function deselectObject(id) {
1017 var obj = selections[id]; 1111 var obj = selections[id];
1018 if (obj) { 1112 if (obj) {
1019 d3.select(obj.el).classed('selected', false); 1113 d3.select(obj.el).classed('selected', false);
1020 - selections[id] = null; 1114 + delete selections[id];
1021 - // TODO: use splice to remove element
1022 } 1115 }
1023 - //flyinPane(null); 1116 + updateDetailPane();
1024 } 1117 }
1025 1118
1026 function deselectAll() { 1119 function deselectAll() {
...@@ -1028,10 +1121,10 @@ ...@@ -1028,10 +1121,10 @@
1028 node.classed('selected', false); 1121 node.classed('selected', false);
1029 selections = {}; 1122 selections = {};
1030 selectOrder = []; 1123 selectOrder = [];
1031 - //flyinPane(null); 1124 + updateDetailPane();
1032 } 1125 }
1033 1126
1034 - // TODO: this click handler does not get unloaded when the view does 1127 + // FIXME: this click handler does not get unloaded when the view does
1035 $('#view').on('click', function(e) { 1128 $('#view').on('click', function(e) {
1036 if (!$(e.target).closest('.node').length) { 1129 if (!$(e.target).closest('.node').length) {
1037 if (!e.metaKey) { 1130 if (!e.metaKey) {
...@@ -1040,6 +1133,66 @@ ...@@ -1040,6 +1133,66 @@
1040 } 1133 }
1041 }); 1134 });
1042 1135
1136 + // update the state of the detail pane, based on current selections
1137 + function updateDetailPane() {
1138 + var nSel = selectOrder.length;
1139 + if (!nSel) {
1140 + detailPane.hide();
1141 + } else if (nSel === 1) {
1142 + singleSelect();
1143 + } else {
1144 + multiSelect();
1145 + }
1146 + }
1147 +
1148 + function singleSelect() {
1149 + requestDetails();
1150 + // NOTE: detail pane will be shown from showDetails event.
1151 + }
1152 +
1153 + function multiSelect() {
1154 + // TODO: use detail pane for multi-select view.
1155 + //detailPane.show();
1156 + }
1157 +
1158 + function populateDetails(data) {
1159 + detailPane.empty();
1160 +
1161 + var title = detailPane.append("h2"),
1162 + table = detailPane.append("table"),
1163 + tbody = table.append("tbody");
1164 +
1165 + $('<img src="img/' + data.type + '.png">').appendTo(title);
1166 + $('<span>').attr('class', 'icon').text(data.id).appendTo(title);
1167 +
1168 + data.propOrder.forEach(function(p) {
1169 + if (p === '-') {
1170 + addSep(tbody);
1171 + } else {
1172 + addProp(tbody, p, data.props[p]);
1173 + }
1174 + });
1175 +
1176 + function addSep(tbody) {
1177 + var tr = tbody.append('tr');
1178 + $('<hr>').appendTo(tr.append('td').attr('colspan', 2));
1179 + }
1180 +
1181 + function addProp(tbody, label, value) {
1182 + var tr = tbody.append('tr');
1183 +
1184 + tr.append('td')
1185 + .attr('class', 'label')
1186 + .text(label + ' :');
1187 +
1188 + tr.append('td')
1189 + .attr('class', 'value')
1190 + .text(value);
1191 + }
1192 + }
1193 +
1194 + // ==============================
1195 + // Test harness code
1043 1196
1044 function prepareScenario(view, ctx, dbg) { 1197 function prepareScenario(view, ctx, dbg) {
1045 var sc = scenario, 1198 var sc = scenario,
...@@ -1058,13 +1211,12 @@ ...@@ -1058,13 +1211,12 @@
1058 d3.json(urlSc, function(err, data) { 1211 d3.json(urlSc, function(err, data) {
1059 var p = data && data.params || {}, 1212 var p = data && data.params || {},
1060 desc = data && data.description || null, 1213 desc = data && data.description || null,
1061 - intro; 1214 + intro = data && data.title;
1062 1215
1063 if (err) { 1216 if (err) {
1064 view.alert('No scenario found:\n\n' + urlSc + '\n\n' + err); 1217 view.alert('No scenario found:\n\n' + urlSc + '\n\n' + err);
1065 } else { 1218 } else {
1066 sc.params = p; 1219 sc.params = p;
1067 - intro = "Scenario loaded: " + ctx + '\n\n' + data.title;
1068 if (desc) { 1220 if (desc) {
1069 intro += '\n\n ' + desc.join('\n '); 1221 intro += '\n\n ' + desc.join('\n ');
1070 } 1222 }
...@@ -1140,16 +1292,18 @@ ...@@ -1140,16 +1292,18 @@
1140 d.fixed = true; 1292 d.fixed = true;
1141 d3.select(self).classed('fixed', true); 1293 d3.select(self).classed('fixed', true);
1142 if (config.useLiveData) { 1294 if (config.useLiveData) {
1143 - tellServerCoords(d); 1295 + sendUpdateMeta(d);
1144 } 1296 }
1145 } 1297 }
1146 1298
1147 - function tellServerCoords(d) { 1299 + function sendUpdateMeta(d) {
1148 sendMessage('updateMeta', { 1300 sendMessage('updateMeta', {
1149 id: d.id, 1301 id: d.id,
1150 'class': d.class, 1302 'class': d.class,
1303 + 'memento': {
1151 x: Math.floor(d.x), 1304 x: Math.floor(d.x),
1152 y: Math.floor(d.y) 1305 y: Math.floor(d.y)
1306 + }
1153 }); 1307 });
1154 } 1308 }
1155 1309
...@@ -1207,4 +1361,6 @@ ...@@ -1207,4 +1361,6 @@
1207 resize: resize 1361 resize: resize
1208 }); 1362 });
1209 1363
1364 + detailPane = onos.ui.addFloatingPanel('topo-detail');
1365 +
1210 }(ONOS)); 1366 }(ONOS));
......