Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
Showing
117 changed files
with
2137 additions
and
486 deletions
... | @@ -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 | -} |
apps/demo/pom.xml
0 → 100644
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; |
apps/demo/src/main/webapp/WEB-INF/web.xml
0 → 100644
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 & SB APIs. | 76 | * Provides implementation of the flow NB & 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> | ... | ... |
providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/OpenFlowRuleProvider.java
... | @@ -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 | } | ... | ... |
... | @@ -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> | ... | ... |
web/gui/src/main/webapp/floatPanel.css
0 → 100644
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 --> | ... | ... |
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 | +} |
... | @@ -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": "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)); | ... | ... |
-
Please register or login to post a comment